Browse Source

Add Settings page

master
Kenneth Bruen 2 years ago
parent
commit
a5615fe3cb
Signed by: kbruen
GPG Key ID: C1980A470C3EE5B1
  1. 71
      lib/components/cupertino_listtile.dart
  2. 4
      lib/main.dart
  3. 8
      lib/models/ui_design.dart
  4. 13
      lib/pages/main/main_page.dart
  5. 16
      lib/pages/main/main_page_fluent.dart
  6. 37
      lib/pages/settings/setings_page.dart
  7. 77
      lib/pages/settings/settings_page_cupertino.dart
  8. 42
      lib/pages/settings/settings_page_fluent.dart
  9. 43
      lib/pages/settings/settings_page_material.dart
  10. 34
      lib/providers.dart

71
lib/components/cupertino_listtile.dart

@ -5,39 +5,54 @@ 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(
mainAxisSize: MainAxisSize.max,
children: [
if (leading != null)
leading!,
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (title != null)
title!,
if (subtitle != null)
CupertinoTheme(
data: CupertinoTheme.of(context).copyWith(
textTheme: CupertinoTextThemeData(
textStyle: TextStyle(
fontSize: CupertinoTheme.of(context).textTheme.textStyle.fontSize! - 2,
)
)
),
child: subtitle!,
),
],
return GestureDetector(
onTap: onTap,
behavior: HitTestBehavior.opaque,
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
if (leading != null)
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!,
if (subtitle != null)
CupertinoTheme(
data: CupertinoTheme.of(context).copyWith(
textTheme: CupertinoTextThemeData(
textStyle: TextStyle(
fontSize: CupertinoTheme.of(context).textTheme.textStyle.fontSize! - 2,
)
)
),
child: subtitle!,
),
],
),
),
),
),
if (trailing != null)
trailing!,
],
if (trailing != null)
Padding(
padding: const EdgeInsets.all(8.0),
child: trailing!,
),
],
),
);
}
}

4
lib/main.dart

@ -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();
},

8
lib/models/ui_design.dart

@ -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;

13
lib/pages/main/main_page.dart

@ -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,
});
}

16
lib/pages/main/main_page_fluent.dart

@ -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

@ -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

@ -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

@ -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

@ -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);
},
),
);
},
),
],
),
),
);
}
}

34
lib/providers.dart

@ -13,22 +13,30 @@ final sharedPreferencesProvider = Provider<SharedPreferences>(
(_) => throw UnimplementedError('Please override in ProviderScope'),
);
final uiDesignProvider = Provider((ref) {
final sharedPreferences = ref.watch(sharedPreferencesProvider);
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);
if (design != null) {
await sharedPreferences.setString('uiDesign', design.name);
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;
state = design;
}
else {
await sharedPreferences.remove('uiDesign');
void set(UiDesign? design) async {
if (design != null) {
await sharedPreferences.setString('uiDesign', design.name);
}
else {
await sharedPreferences.remove('uiDesign');
}
state = design ?? defaultUiDesign;
}
ref.invalidate(uiDesignProvider);
}
final uiDesignProvider = StateNotifierProvider<UiDesignNotifier, UiDesign>(
(ref) => UiDesignNotifier(
sharedPreferences: ref.watch(sharedPreferencesProvider),
),
);
final trainInfoArgumentsProvider = Provider<TrainInfoArguments>(
(_) => throw UnimplementedError('Please override in ProviderScope'),

Loading…
Cancel
Save