Add Settings page
This commit is contained in:
parent
342b870e93
commit
a5615fe3cb
10 changed files with 303 additions and 42 deletions
|
@ -5,19 +5,29 @@ class CupertinoListTile extends StatelessWidget {
|
|||
final Widget? title;
|
||||
final Widget? subtitle;
|
||||
final Widget? trailing;
|
||||
final void Function()? onTap;
|
||||
|
||||
const CupertinoListTile({ Key? key, this.leading, this.title, this.subtitle, this.trailing, }) : super(key: key);
|
||||
const CupertinoListTile({ super.key, this.leading, this.title, this.subtitle, this.trailing, this.onTap, });
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
if (leading != null)
|
||||
leading!,
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: leading!,
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (title != null)
|
||||
title!,
|
||||
|
@ -35,9 +45,14 @@ class CupertinoListTile extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (trailing != null)
|
||||
trailing!,
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: trailing!,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/pages/about/about_page.dart';
|
||||
import 'package:info_tren/pages/main/main_page.dart';
|
||||
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
|
||||
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
||||
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
|
||||
|
@ -36,6 +37,9 @@ Map<String, WidgetBuilder> get routes => {
|
|||
AboutPage.routeName: (context) {
|
||||
return const AboutPage();
|
||||
},
|
||||
SettingsPage.routeName: (context) {
|
||||
return const SettingsPage();
|
||||
},
|
||||
SelectTrainPage.routeName: (context) {
|
||||
return const SelectTrainPage();
|
||||
},
|
||||
|
|
|
@ -4,6 +4,14 @@ enum UiDesign {
|
|||
FLUENT,
|
||||
}
|
||||
|
||||
extension UIName on UiDesign {
|
||||
String get userInterfaceName => (const {
|
||||
UiDesign.MATERIAL: 'Material',
|
||||
UiDesign.CUPERTINO: 'Cupertino',
|
||||
UiDesign.FLUENT: 'Fluent',
|
||||
})[this]!;
|
||||
}
|
||||
|
||||
class UnmatchedUiDesignException implements Exception {
|
||||
final UiDesign uiDesign;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:info_tren/pages/about/about_page.dart';
|
|||
import 'package:info_tren/pages/main/main_page_cupertino.dart';
|
||||
import 'package:info_tren/pages/main/main_page_fluent.dart';
|
||||
import 'package:info_tren/pages/main/main_page_material.dart';
|
||||
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
|
||||
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
|
||||
import 'package:info_tren/providers.dart';
|
||||
|
@ -36,6 +37,12 @@ abstract class MainPageShared extends StatelessWidget {
|
|||
const MainPageShared({super.key});
|
||||
|
||||
List<MainPageAction> get popupMenu => [
|
||||
MainPageAction(
|
||||
name: 'Setări',
|
||||
action: (context) {
|
||||
Navigator.of(context).pushNamed(SettingsPage.routeName);
|
||||
},
|
||||
),
|
||||
MainPageAction(
|
||||
name: 'Despre aplicație',
|
||||
action: (context) {
|
||||
|
@ -85,5 +92,9 @@ class MainPageAction {
|
|||
final String? description;
|
||||
final void Function(BuildContext context)? action;
|
||||
|
||||
MainPageAction({required this.name, this.action, this.description});
|
||||
MainPageAction({
|
||||
required this.name,
|
||||
this.action,
|
||||
this.description,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,6 +10,22 @@ class MainPageFluent extends MainPageShared {
|
|||
appBar: NavigationAppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
title: Text(pageTitle),
|
||||
actions: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: popupMenu.map((i) => Center(
|
||||
child: SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: IconButton(
|
||||
icon: Icon({
|
||||
'Setări': FluentIcons.settings,
|
||||
'Despre aplicație': FluentIcons.info,
|
||||
}[i.name]),
|
||||
onPressed: i.action != null ? () => i.action!(context) : null,
|
||||
),
|
||||
),
|
||||
)).toList(),
|
||||
),
|
||||
// centerTitle: true,
|
||||
// actions: [
|
||||
// PopupMenuButton<int>(
|
||||
|
|
37
lib/pages/settings/setings_page.dart
Normal file
37
lib/pages/settings/setings_page.dart
Normal file
|
@ -0,0 +1,37 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/pages/settings/settings_page_cupertino.dart';
|
||||
import 'package:info_tren/pages/settings/settings_page_fluent.dart';
|
||||
import 'package:info_tren/pages/settings/settings_page_material.dart';
|
||||
import 'package:info_tren/providers.dart';
|
||||
|
||||
class SettingsPage extends ConsumerWidget {
|
||||
const SettingsPage({super.key,});
|
||||
|
||||
static const String routeName = '/settings';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final uiDesign = ref.watch(uiDesignProvider);
|
||||
|
||||
switch (uiDesign) {
|
||||
case UiDesign.MATERIAL:
|
||||
return const SettingsPageMaterial();
|
||||
case UiDesign.CUPERTINO:
|
||||
return const SettingsPageCupertino();
|
||||
case UiDesign.FLUENT:
|
||||
return const SettingsPageFluent();
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SettingsPageShared extends StatelessWidget {
|
||||
final String pageTitle = 'Setări';
|
||||
final String appearanceTitle = 'Aspect';
|
||||
|
||||
const SettingsPageShared({super.key});
|
||||
|
||||
}
|
77
lib/pages/settings/settings_page_cupertino.dart
Normal file
77
lib/pages/settings/settings_page_cupertino.dart
Normal file
|
@ -0,0 +1,77 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:info_tren/components/cupertino_listtile.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||
import 'package:info_tren/providers.dart';
|
||||
|
||||
class SettingsPageCupertino extends SettingsPageShared {
|
||||
const SettingsPageCupertino({super.key,});
|
||||
|
||||
Future<T?> singleChoice<T>({required BuildContext context, required List<T> choices, required String Function(T) labelBuilder, String? title}) async {
|
||||
return await showCupertinoModalPopup<T>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return CupertinoActionSheet(
|
||||
title: title != null ? Text(title) : null,
|
||||
actions: choices.map((c) => CupertinoActionSheetAction(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(c);
|
||||
},
|
||||
child: Text(labelBuilder(c)),
|
||||
)).toList(),
|
||||
cancelButton: CupertinoActionSheetAction(
|
||||
child: const Text('Anulare'),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
previousPageTitle: 'Info Tren',
|
||||
middle: Text(pageTitle),
|
||||
),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final mq = MediaQuery.of(context);
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: mq.padding.top,
|
||||
),
|
||||
Consumer(
|
||||
builder: (context, ref, _) {
|
||||
final currentUiDesign = ref.watch(uiDesignProvider);
|
||||
return CupertinoListTile(
|
||||
title: Text(appearanceTitle),
|
||||
trailing: Text(currentUiDesign.userInterfaceName),
|
||||
onTap: () async {
|
||||
final choice = await singleChoice(
|
||||
context: context,
|
||||
choices: UiDesign.values,
|
||||
labelBuilder: (UiDesign ud) => ud.userInterfaceName,
|
||||
title: appearanceTitle,
|
||||
);
|
||||
if (choice != null) {
|
||||
ref.read(uiDesignProvider.notifier).set(choice);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
42
lib/pages/settings/settings_page_fluent.dart
Normal file
42
lib/pages/settings/settings_page_fluent.dart
Normal file
|
@ -0,0 +1,42 @@
|
|||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||
import 'package:info_tren/providers.dart';
|
||||
|
||||
class SettingsPageFluent extends SettingsPageShared {
|
||||
const SettingsPageFluent({super.key,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return NavigationView(
|
||||
appBar: NavigationAppBar(
|
||||
title: Text(pageTitle),
|
||||
),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Consumer(
|
||||
builder: (context, ref, _) {
|
||||
final currentUiDesign = ref.watch(uiDesignProvider);
|
||||
return ListTile(
|
||||
title: Text(appearanceTitle),
|
||||
trailing: ComboBox<UiDesign>(
|
||||
items: UiDesign.values.map((d) => ComboBoxItem(
|
||||
value: d,
|
||||
child: Text(d.userInterfaceName),
|
||||
)).toList(),
|
||||
value: currentUiDesign,
|
||||
onChanged: (newUiDesign) {
|
||||
ref.read(uiDesignProvider.notifier).set(newUiDesign);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
43
lib/pages/settings/settings_page_material.dart
Normal file
43
lib/pages/settings/settings_page_material.dart
Normal file
|
@ -0,0 +1,43 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||
import 'package:info_tren/providers.dart';
|
||||
|
||||
class SettingsPageMaterial extends SettingsPageShared {
|
||||
const SettingsPageMaterial({super.key,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(pageTitle),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Consumer(
|
||||
builder: (context, ref, _) {
|
||||
final currentUiDesign = ref.watch(uiDesignProvider);
|
||||
return ListTile(
|
||||
title: Text(appearanceTitle),
|
||||
trailing: DropdownButton<UiDesign>(
|
||||
items: UiDesign.values.map((d) => DropdownMenuItem(
|
||||
value: d,
|
||||
child: Text(d.userInterfaceName),
|
||||
)).toList(),
|
||||
value: currentUiDesign,
|
||||
onChanged: (newUiDesign) {
|
||||
ref.read(uiDesignProvider.notifier).set(newUiDesign);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -13,22 +13,30 @@ final sharedPreferencesProvider = Provider<SharedPreferences>(
|
|||
(_) => throw UnimplementedError('Please override in ProviderScope'),
|
||||
);
|
||||
|
||||
final uiDesignProvider = Provider((ref) {
|
||||
final sharedPreferences = ref.watch(sharedPreferencesProvider);
|
||||
class UiDesignNotifier extends StateNotifier<UiDesign> {
|
||||
final SharedPreferences sharedPreferences;
|
||||
|
||||
UiDesignNotifier({required this.sharedPreferences,}) : super(UiDesign.MATERIAL) {
|
||||
final stored = sharedPreferences.getString('uiDesign');
|
||||
final design = UiDesign.values.where((element) => element.name == stored).firstOrNull ?? defaultUiDesign;
|
||||
return design;
|
||||
});
|
||||
Future<void> setUiDesign(Ref ref, UiDesign? design) async {
|
||||
final sharedPreferences = ref.watch(sharedPreferencesProvider);
|
||||
state = design;
|
||||
}
|
||||
|
||||
void set(UiDesign? design) async {
|
||||
if (design != null) {
|
||||
await sharedPreferences.setString('uiDesign', design.name);
|
||||
}
|
||||
else {
|
||||
await sharedPreferences.remove('uiDesign');
|
||||
}
|
||||
ref.invalidate(uiDesignProvider);
|
||||
state = design ?? defaultUiDesign;
|
||||
}
|
||||
}
|
||||
final uiDesignProvider = StateNotifierProvider<UiDesignNotifier, UiDesign>(
|
||||
(ref) => UiDesignNotifier(
|
||||
sharedPreferences: ref.watch(sharedPreferencesProvider),
|
||||
),
|
||||
);
|
||||
|
||||
final trainInfoArgumentsProvider = Provider<TrainInfoArguments>(
|
||||
(_) => throw UnimplementedError('Please override in ProviderScope'),
|
||||
|
|
Loading…
Add table
Reference in a new issue