From 8a54707bef4761968e8d6cc948f4426316ac4c43 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 19 Jan 2023 20:37:35 -0500 Subject: [PATCH] Add dark mode selection/theming --- lib/app_theme.dart | 11 ++++ lib/controls/app_bottom_nav_bar.dart | 7 +-- lib/main.dart | 94 +++++++++++++++------------- lib/screens/home.dart | 4 +- lib/screens/settings_screen.dart | 21 ++++++- lib/services/setting_service.dart | 16 ++++- lib/utils/theme_mode_extensions.dart | 20 ++++++ 7 files changed, 121 insertions(+), 52 deletions(-) create mode 100644 lib/app_theme.dart create mode 100644 lib/utils/theme_mode_extensions.dart diff --git a/lib/app_theme.dart b/lib/app_theme.dart new file mode 100644 index 0000000..e58a39a --- /dev/null +++ b/lib/app_theme.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; + +class AppTheme { + static ThemeData light = ThemeData(primarySwatch: Colors.indigo); + + static ThemeData dark = ThemeData.from( + colorScheme: ColorScheme.fromSwatch( + primarySwatch: Colors.cyan, + brightness: Brightness.dark, + )); +} diff --git a/lib/controls/app_bottom_nav_bar.dart b/lib/controls/app_bottom_nav_bar.dart index 5cfed2b..c5f9596 100644 --- a/lib/controls/app_bottom_nav_bar.dart +++ b/lib/controls/app_bottom_nav_bar.dart @@ -48,10 +48,8 @@ class AppBottomNavBar extends StatelessWidget { } }, type: BottomNavigationBarType.fixed, - selectedItemColor: Colors.black, - unselectedItemColor: Colors.black54, currentIndex: _buttonToIndex(currentButton), - items: _menuItems(hasNotifications), + items: _menuItems(context, hasNotifications), ); } @@ -88,7 +86,8 @@ class AppBottomNavBar extends StatelessWidget { throw ArgumentError('$index has no button type'); } - List _menuItems(bool hasNotifications) { + List _menuItems( + BuildContext context, bool hasNotifications) { return [ const BottomNavigationBarItem( label: 'Timelines', diff --git a/lib/main.dart b/lib/main.dart index efa432e..153d873 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:logging/logging.dart'; import 'package:multi_trigger_autocomplete/multi_trigger_autocomplete.dart'; import 'package:provider/provider.dart'; +import 'app_theme.dart'; import 'di_initialization.dart'; import 'globals.dart'; import 'routes.dart'; @@ -40,51 +41,56 @@ class App extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { - return Portal( - child: MultiProvider( - providers: [ - ChangeNotifierProvider( - create: (_) => getIt(), - lazy: true, + return AnimatedBuilder( + builder: (context, child) { + return Portal( + child: MultiProvider( + providers: [ + ChangeNotifierProvider( + create: (_) => getIt(), + lazy: true, + ), + ChangeNotifierProvider( + create: (_) => getIt(), + lazy: true, + ), + ChangeNotifierProvider( + create: (_) => getIt(), + lazy: true, + ), + ChangeNotifierProvider( + create: (_) => getIt(), + lazy: true, + ), + ChangeNotifierProvider( + create: (_) => getIt(), + lazy: true, + ), + ChangeNotifierProvider( + create: (_) => getIt(), + lazy: true, + ), + ChangeNotifierProvider( + create: (_) => getIt(), + ), + ChangeNotifierProvider( + create: (_) => getIt(), + ), + ], + child: MaterialApp.router( + theme: AppTheme.light, + darkTheme: AppTheme.dark, + themeMode: getIt().themeMode, + debugShowCheckedModeBanner: false, + scrollBehavior: AppScrollingBehavior(), + routerDelegate: appRouter.routerDelegate, + routeInformationProvider: appRouter.routeInformationProvider, + routeInformationParser: appRouter.routeInformationParser, + ), ), - ChangeNotifierProvider( - create: (_) => getIt(), - lazy: true, - ), - ChangeNotifierProvider( - create: (_) => getIt(), - lazy: true, - ), - ChangeNotifierProvider( - create: (_) => getIt(), - lazy: true, - ), - ChangeNotifierProvider( - create: (_) => getIt(), - lazy: true, - ), - ChangeNotifierProvider( - create: (_) => getIt(), - lazy: true, - ), - ChangeNotifierProvider( - create: (_) => getIt(), - ), - ChangeNotifierProvider( - create: (_) => getIt(), - ), - ], - child: MaterialApp.router( - theme: ThemeData( - primarySwatch: Colors.indigo, - ), - debugShowCheckedModeBanner: false, - scrollBehavior: AppScrollingBehavior(), - routerDelegate: appRouter.routerDelegate, - routeInformationProvider: appRouter.routeInformationProvider, - routeInformationParser: appRouter.routeInformationParser, - ), - ), + ); + }, + animation: getIt(), ); } } diff --git a/lib/screens/home.dart b/lib/screens/home.dart index f7bd1d8..5f4e585 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -81,8 +81,8 @@ class _HomeScreenState extends State { context.push('/post/new'); }, icon: Icon( - Icons.add, - color: Theme.of(context).primaryColor, + Icons.edit, + color: Theme.of(context).textTheme.bodyText1!.color, ), ), ], diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 21cda7f..c9c5f6e 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:friendica_portal/services/setting_service.dart'; import 'package:provider/provider.dart'; import '../controls/standard_appbar.dart'; +import '../services/setting_service.dart'; +import '../utils/theme_mode_extensions.dart'; class SettingsScreen extends StatelessWidget { @override @@ -16,6 +17,7 @@ class SettingsScreen extends StatelessWidget { child: ListView( children: [ buildLowBandwidthWidget(settings), + buildThemeWidget(settings), ], ), ), @@ -33,4 +35,21 @@ class SettingsScreen extends StatelessWidget { ), ); } + + Widget buildThemeWidget(SettingsService settings) { + return ListTile( + title: const Text('Dark Mode Theme:'), + trailing: DropdownButton( + value: settings.themeMode, + items: ThemeMode.values + .map((m) => DropdownMenuItem(value: m, child: Text(m.toLabel()))) + .toList(), + onChanged: (value) { + if (value != null) { + settings.themeMode = value; + } + }, + ), + ); + } } diff --git a/lib/services/setting_service.dart b/lib/services/setting_service.dart index a338bc4..58bf0ed 100644 --- a/lib/services/setting_service.dart +++ b/lib/services/setting_service.dart @@ -1,6 +1,8 @@ -import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../utils/theme_mode_extensions.dart'; + class SettingsService extends ChangeNotifier { late final SharedPreferences _prefs; var _initialized = false; @@ -17,14 +19,26 @@ class SettingsService extends ChangeNotifier { notifyListeners(); } + var _themeMode = ThemeMode.system; + + ThemeMode get themeMode => _themeMode; + + set themeMode(ThemeMode mode) { + _themeMode = mode; + _prefs.setString(_themeModeKey, _themeMode.name); + notifyListeners(); + } + Future initialize() async { if (_initialized) { return; } _prefs = await SharedPreferences.getInstance(); _lowBandwidthMode = _prefs.getBool(_lowBandwidthModeKey) ?? false; + _themeMode = ThemeModeExtensions.parse(_prefs.getString(_themeModeKey)); _initialized = true; } } const _lowBandwidthModeKey = 'LowBandwidthMode'; +const _themeModeKey = 'ThemeMode'; diff --git a/lib/utils/theme_mode_extensions.dart b/lib/utils/theme_mode_extensions.dart new file mode 100644 index 0000000..a317775 --- /dev/null +++ b/lib/utils/theme_mode_extensions.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; + +extension ThemeModeExtensions on ThemeMode { + static ThemeMode parse(String? themeModeString) => + ThemeMode.values.firstWhere( + (m) => m.name == themeModeString, + orElse: () => ThemeMode.system, + ); + + String toLabel() { + switch (this) { + case ThemeMode.system: + return 'System'; + case ThemeMode.light: + return 'Light'; + case ThemeMode.dark: + return 'Dark'; + } + } +}