From 3264a0c9e3ae3beea2421a935a75fb5c05b6e536 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 19 Jan 2023 10:10:13 -0500 Subject: [PATCH 1/4] Move all the DI initialization to a function to be cleaner. --- lib/di_initialization.dart | 73 ++++++++++++++++++++++++++++++++++++++ lib/main.dart | 61 ++----------------------------- 2 files changed, 75 insertions(+), 59 deletions(-) create mode 100644 lib/di_initialization.dart diff --git a/lib/di_initialization.dart b/lib/di_initialization.dart new file mode 100644 index 0000000..3a0aee1 --- /dev/null +++ b/lib/di_initialization.dart @@ -0,0 +1,73 @@ +import 'package:logging/logging.dart'; +import 'package:result_monad/result_monad.dart'; + +import 'data/interfaces/connections_repo_intf.dart'; +import 'data/interfaces/groups_repo.intf.dart'; +import 'data/interfaces/hashtag_repo_intf.dart'; +import 'data/memory/memory_groups_repo.dart'; +import 'data/objectbox/objectbox_cache.dart'; +import 'data/objectbox/objectbox_connections_repo.dart'; +import 'data/objectbox/objectbox_hashtag_repo.dart'; +import 'globals.dart'; +import 'models/TimelineIdentifiers.dart'; +import 'services/auth_service.dart'; +import 'services/connections_manager.dart'; +import 'services/entry_manager_service.dart'; +import 'services/gallery_service.dart'; +import 'services/hashtag_service.dart'; +import 'services/media_upload_attachment_helper.dart'; +import 'services/notifications_manager.dart'; +import 'services/secrets_service.dart'; +import 'services/setting_service.dart'; +import 'services/timeline_manager.dart'; + +final _logger = Logger('DI_Init'); + +Future dependencyInjectionInitialization() async { + final authService = AuthService(); + final secretsService = SecretsService(); + final entryManagerService = EntryManagerService(); + final timelineManager = TimelineManager(); + final galleryService = GalleryService(); + + getIt.registerSingletonAsync(() async { + final service = SettingsService(); + await service.initialize(); + return service; + }); + + final objectBoxCache = await ObjectBoxCache.create(); + getIt.registerSingleton(objectBoxCache); + getIt.registerSingleton(ObjectBoxConnectionsRepo()); + getIt.registerSingleton(ObjectBoxHashtagRepo()); + getIt.registerSingleton(MemoryGroupsRepo()); + getIt.registerLazySingleton(() => ConnectionsManager()); + getIt.registerLazySingleton(() => HashtagService()); + getIt.registerSingleton(galleryService); + getIt.registerSingleton(entryManagerService); + getIt.registerSingleton(secretsService); + getIt.registerSingleton(authService); + getIt.registerSingleton(timelineManager); + getIt.registerLazySingleton( + () => MediaUploadAttachmentHelper()); + getIt.registerLazySingleton( + () => NotificationsManager()); + galleryService.getGalleries(); + + await secretsService.initialize().andThenSuccessAsync((credentials) async { + if (credentials.isEmpty) { + return; + } + + final wasLoggedIn = await authService.getStoredLoginState(); + if (wasLoggedIn) { + final result = await authService.signIn(credentials); + if (result.isSuccess) { + timelineManager.updateTimeline( + TimelineIdentifiers.home(), TimelineRefreshType.loadOlder); + } + } else { + _logger.severe('Was not logged in'); + } + }); +} diff --git a/lib/main.dart b/lib/main.dart index f9e016f..efa432e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,32 +3,20 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:logging/logging.dart'; import 'package:multi_trigger_autocomplete/multi_trigger_autocomplete.dart'; import 'package:provider/provider.dart'; -import 'package:result_monad/result_monad.dart'; -import 'data/interfaces/connections_repo_intf.dart'; -import 'data/interfaces/groups_repo.intf.dart'; -import 'data/interfaces/hashtag_repo_intf.dart'; -import 'data/memory/memory_groups_repo.dart'; -import 'data/objectbox/objectbox_cache.dart'; -import 'data/objectbox/objectbox_connections_repo.dart'; -import 'data/objectbox/objectbox_hashtag_repo.dart'; +import 'di_initialization.dart'; import 'globals.dart'; -import 'models/TimelineIdentifiers.dart'; import 'routes.dart'; import 'services/auth_service.dart'; import 'services/connections_manager.dart'; import 'services/entry_manager_service.dart'; import 'services/gallery_service.dart'; import 'services/hashtag_service.dart'; -import 'services/media_upload_attachment_helper.dart'; import 'services/notifications_manager.dart'; -import 'services/secrets_service.dart'; import 'services/setting_service.dart'; import 'services/timeline_manager.dart'; import 'utils/app_scrolling_behavior.dart'; -final _logger = Logger('Main'); - void main() async { WidgetsFlutterBinding.ensureInitialized(); await dotenv.load(fileName: '.env'); @@ -41,52 +29,7 @@ void main() async { print(msg); }); - final authService = AuthService(); - final secretsService = SecretsService(); - final entryManagerService = EntryManagerService(); - final timelineManager = TimelineManager(); - final galleryService = GalleryService(); - - getIt.registerSingletonAsync(() async { - final service = SettingsService(); - await service.initialize(); - return service; - }); - - final objectBoxCache = await ObjectBoxCache.create(); - getIt.registerSingleton(objectBoxCache); - getIt.registerSingleton(ObjectBoxConnectionsRepo()); - getIt.registerSingleton(ObjectBoxHashtagRepo()); - getIt.registerSingleton(MemoryGroupsRepo()); - getIt.registerLazySingleton(() => ConnectionsManager()); - getIt.registerLazySingleton(() => HashtagService()); - getIt.registerSingleton(galleryService); - getIt.registerSingleton(entryManagerService); - getIt.registerSingleton(secretsService); - getIt.registerSingleton(authService); - getIt.registerSingleton(timelineManager); - getIt.registerLazySingleton( - () => MediaUploadAttachmentHelper()); - getIt.registerLazySingleton( - () => NotificationsManager()); - galleryService.getGalleries(); - - await secretsService.initialize().andThenSuccessAsync((credentials) async { - if (credentials.isEmpty) { - return; - } - - final wasLoggedIn = await authService.getStoredLoginState(); - if (wasLoggedIn) { - final result = await authService.signIn(credentials); - if (result.isSuccess) { - timelineManager.updateTimeline( - TimelineIdentifiers.home(), TimelineRefreshType.loadOlder); - } - } else { - _logger.severe('Was not logged in'); - } - }); + dependencyInjectionInitialization(); runApp(const App()); } From 02432a0a6ea3a0ea17430bcdb6ab6bc3f2621c00 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 19 Jan 2023 12:50:11 -0500 Subject: [PATCH 2/4] Initial menu screen with gallery and profile moved over to it. --- lib/controls/app_bottom_nav_bar.dart | 52 +++++------------------ lib/controls/standard_appbar.dart | 21 +++++++++ lib/routes.dart | 9 ++++ lib/screens/gallery_browsers_screen.dart | 13 +++--- lib/screens/gallery_screen.dart | 5 +-- lib/screens/menus_screen.dart | 54 ++++++++++++++++++++++++ lib/screens/profile_screen.dart | 15 +++---- 7 files changed, 108 insertions(+), 61 deletions(-) create mode 100644 lib/controls/standard_appbar.dart create mode 100644 lib/screens/menus_screen.dart diff --git a/lib/controls/app_bottom_nav_bar.dart b/lib/controls/app_bottom_nav_bar.dart index b77836f..5cfed2b 100644 --- a/lib/controls/app_bottom_nav_bar.dart +++ b/lib/controls/app_bottom_nav_bar.dart @@ -9,10 +9,8 @@ import '../services/notifications_manager.dart'; enum NavBarButtons { timelines, notifications, - messages, - gallery, contacts, - profile, + menu, } class AppBottomNavBar extends StatelessWidget { @@ -41,17 +39,11 @@ class AppBottomNavBar extends StatelessWidget { case NavBarButtons.notifications: context.pushNamed(ScreenPaths.notifications); break; - case NavBarButtons.messages: - // TODO: Handle this case. - break; case NavBarButtons.contacts: context.pushNamed(ScreenPaths.contacts); break; - case NavBarButtons.profile: - context.pushNamed(ScreenPaths.profile); - break; - case NavBarButtons.gallery: - context.pushNamed(ScreenPaths.gallery); + case NavBarButtons.menu: + context.pushNamed(ScreenPaths.menu); break; } }, @@ -69,14 +61,10 @@ class AppBottomNavBar extends StatelessWidget { return 0; case NavBarButtons.notifications: return 1; - case NavBarButtons.gallery: - return 2; - case NavBarButtons.messages: - return 3; case NavBarButtons.contacts: - return 4; - case NavBarButtons.profile: - return 5; + return 2; + case NavBarButtons.menu: + return 3; } } @@ -90,19 +78,11 @@ class AppBottomNavBar extends StatelessWidget { } if (index == 2) { - return NavBarButtons.gallery; - } - - if (index == 3) { - return NavBarButtons.messages; - } - - if (index == 4) { return NavBarButtons.contacts; } - if (index == 5) { - return NavBarButtons.profile; + if (index == 3) { + return NavBarButtons.menu; } throw ArgumentError('$index has no button type'); @@ -124,25 +104,15 @@ class AppBottomNavBar extends StatelessWidget { ? Icons.notifications_active : Icons.notifications), ), - const BottomNavigationBarItem( - label: 'Gallery', - icon: Icon(Icons.photo_library_outlined), - activeIcon: Icon(Icons.photo_library), - ), - const BottomNavigationBarItem( - label: 'Messages', - icon: Icon(Icons.messenger_outline), - activeIcon: Icon(Icons.messenger), - ), const BottomNavigationBarItem( label: 'Contacts', icon: Icon(Icons.people_outline), activeIcon: Icon(Icons.people_sharp), ), const BottomNavigationBarItem( - label: 'Profile', - icon: Icon(Icons.person_outline), - activeIcon: Icon(Icons.person), + label: 'Menu', + icon: Icon(Icons.menu), + activeIcon: Icon(Icons.menu_open), ), ]; } diff --git a/lib/controls/standard_appbar.dart b/lib/controls/standard_appbar.dart new file mode 100644 index 0000000..167e306 --- /dev/null +++ b/lib/controls/standard_appbar.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +import '../routes.dart'; + +class StandardAppBar { + static AppBar build(BuildContext context, String title) { + return AppBar( + title: Text(title), + actions: [ + IconButton( + onPressed: () { + Navigator.of(context).popUntil((route) { + return route.settings.name == ScreenPaths.timelines; + }); + }, + icon: const Icon(Icons.home), + ), + ], + ); + } +} diff --git a/lib/routes.dart b/lib/routes.dart index ded5485..55ed73d 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -7,6 +7,7 @@ import 'screens/follow_request_adjudication_screen.dart'; import 'screens/gallery_browsers_screen.dart'; import 'screens/gallery_screen.dart'; import 'screens/home.dart'; +import 'screens/menus_screen.dart'; import 'screens/notifications_screen.dart'; import 'screens/post_screen.dart'; import 'screens/profile_screen.dart'; @@ -20,6 +21,7 @@ class ScreenPaths { static String connectHandle = '/connect'; static String contacts = '/contacts'; static String splash = '/splash'; + static String menu = '/menu'; static String timelines = '/'; static String gallery = '/gallery'; static String profile = '/profile'; @@ -90,6 +92,13 @@ final appRouter = GoRouter( child: ProfileScreen(), ), ), + GoRoute( + path: ScreenPaths.menu, + name: ScreenPaths.menu, + pageBuilder: (context, state) => NoTransitionPage( + child: MenusScreen(), + ), + ), GoRoute( path: ScreenPaths.gallery, name: ScreenPaths.gallery, diff --git a/lib/screens/gallery_browsers_screen.dart b/lib/screens/gallery_browsers_screen.dart index 6576cd7..c3bccda 100644 --- a/lib/screens/gallery_browsers_screen.dart +++ b/lib/screens/gallery_browsers_screen.dart @@ -3,8 +3,8 @@ import 'package:go_router/go_router.dart'; import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; -import '../controls/app_bottom_nav_bar.dart'; import '../controls/padding.dart'; +import '../controls/standard_appbar.dart'; import '../services/gallery_service.dart'; class GalleryBrowsersScreen extends StatelessWidget { @@ -15,13 +15,12 @@ class GalleryBrowsersScreen extends StatelessWidget { _logger.finest('Building'); final service = context.watch(); return Scaffold( + appBar: StandardAppBar.build(context, 'Galleries'), body: RefreshIndicator( - onRefresh: () async { - await service.updateGalleries(); - }, - child: buildBody(context, service)), - bottomNavigationBar: const AppBottomNavBar( - currentButton: NavBarButtons.gallery, + onRefresh: () async { + await service.updateGalleries(); + }, + child: buildBody(context, service), ), ); } diff --git a/lib/screens/gallery_screen.dart b/lib/screens/gallery_screen.dart index f6888f6..9005b63 100644 --- a/lib/screens/gallery_screen.dart +++ b/lib/screens/gallery_screen.dart @@ -4,6 +4,7 @@ import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; import '../controls/padding.dart'; +import '../controls/standard_appbar.dart'; import '../serializers/friendica/image_entry_friendica_extensions.dart'; import '../services/gallery_service.dart'; import 'image_viewer_screen.dart'; @@ -20,9 +21,7 @@ class GalleryScreen extends StatelessWidget { _logger.finest('Building'); final service = context.watch(); return Scaffold( - appBar: AppBar( - title: Text(galleryName), - ), + appBar: StandardAppBar.build(context, galleryName), body: RefreshIndicator( onRefresh: () async { await service.updateGalleryImageList(galleryName); diff --git a/lib/screens/menus_screen.dart b/lib/screens/menus_screen.dart new file mode 100644 index 0000000..3d2a2fe --- /dev/null +++ b/lib/screens/menus_screen.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:friendica_portal/globals.dart'; +import 'package:friendica_portal/services/auth_service.dart'; +import 'package:go_router/go_router.dart'; + +import '../controls/app_bottom_nav_bar.dart'; +import '../routes.dart'; + +class MenusScreen extends StatelessWidget { + static const menuButtonWidth = 350.0; + static const menuButtonHeight = 125.0; + + @override + Widget build(BuildContext context) { + final items = [ + buildMenuButton('Gallery', () => context.pushNamed(ScreenPaths.gallery)), + buildMenuButton('Profile', () => context.pushNamed(ScreenPaths.profile)), + buildMenuButton('Logout', () async { + final confirm = await showYesNoDialog(context, 'Log out account?'); + if (confirm == true) { + await getIt().signOut(); + } + }), + ]; + return Scaffold( + body: Center( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: GridView.builder( + itemCount: items.length, + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + mainAxisExtent: menuButtonHeight, + maxCrossAxisExtent: menuButtonWidth, + ), + itemBuilder: (context, index) => items[index], + ), + ), + ), + bottomNavigationBar: const AppBottomNavBar( + currentButton: NavBarButtons.menu, + ), + ); + } + + Widget buildMenuButton(String title, Function() onPressed) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + onPressed: onPressed, + child: Text(title), + ), + ); + } +} diff --git a/lib/screens/profile_screen.dart b/lib/screens/profile_screen.dart index a62ee8d..715b27e 100644 --- a/lib/screens/profile_screen.dart +++ b/lib/screens/profile_screen.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import '../controls/app_bottom_nav_bar.dart'; import '../controls/padding.dart'; +import '../controls/standard_appbar.dart'; import '../services/auth_service.dart'; import '../services/setting_service.dart'; @@ -19,9 +19,7 @@ class _ProfileScreenState extends State { final settings = context.watch(); final authService = context.watch(); return Scaffold( - appBar: AppBar( - title: Text('Profile'), - ), + appBar: StandardAppBar.build(context, 'Profile'), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -34,23 +32,20 @@ class _ProfileScreenState extends State { onChanged: (value) => settings.lowBandwidthMode = value ?? false, ), - Text('Low Bandwidth Mode'), + const Text('Low Bandwidth Mode'), ], ), Text( 'Profile: ${authService.currentClient.fold(onSuccess: (client) => client.credentials.handle, onError: (error) => 'Error Getting Profile')}'), - VerticalPadding(), + const VerticalPadding(), ElevatedButton( onPressed: () async { await authService.signOut(); }, - child: Text('Sign Out')), + child: const Text('Sign Out')), ], ), ), - bottomNavigationBar: AppBottomNavBar( - currentButton: NavBarButtons.profile, - ), ); } } From 3401d51c3c666d40444f75eea0f10f91b952f63a Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 19 Jan 2023 13:18:15 -0500 Subject: [PATCH 3/4] Add initial settings screen with low BW moved to it --- lib/routes.dart | 10 ++++++++- lib/screens/menus_screen.dart | 9 ++++---- lib/screens/profile_screen.dart | 20 ------------------ lib/screens/settings_screen.dart | 36 ++++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 25 deletions(-) create mode 100644 lib/screens/settings_screen.dart diff --git a/lib/routes.dart b/lib/routes.dart index 55ed73d..cc4eb7b 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -11,6 +11,7 @@ import 'screens/menus_screen.dart'; import 'screens/notifications_screen.dart'; import 'screens/post_screen.dart'; import 'screens/profile_screen.dart'; +import 'screens/settings_screen.dart'; import 'screens/sign_in.dart'; import 'screens/splash.dart'; import 'screens/user_posts_screen.dart'; @@ -22,13 +23,13 @@ class ScreenPaths { static String contacts = '/contacts'; static String splash = '/splash'; static String menu = '/menu'; + static String settings = '/settings'; static String timelines = '/'; static String gallery = '/gallery'; static String profile = '/profile'; static String notifications = '/notifications'; static String signin = '/signin'; static String signup = '/signup'; - static String settings = '/settings'; static String userProfile = '/user_profile'; static String userPosts = '/user_posts'; } @@ -99,6 +100,13 @@ final appRouter = GoRouter( child: MenusScreen(), ), ), + GoRoute( + path: ScreenPaths.settings, + name: ScreenPaths.settings, + pageBuilder: (context, state) => NoTransitionPage( + child: SettingsScreen(), + ), + ), GoRoute( path: ScreenPaths.gallery, name: ScreenPaths.gallery, diff --git a/lib/screens/menus_screen.dart b/lib/screens/menus_screen.dart index 3d2a2fe..51771a9 100644 --- a/lib/screens/menus_screen.dart +++ b/lib/screens/menus_screen.dart @@ -12,9 +12,11 @@ class MenusScreen extends StatelessWidget { @override Widget build(BuildContext context) { - final items = [ + final menuItems = [ buildMenuButton('Gallery', () => context.pushNamed(ScreenPaths.gallery)), buildMenuButton('Profile', () => context.pushNamed(ScreenPaths.profile)), + buildMenuButton( + 'Settings', () => context.pushNamed(ScreenPaths.settings)), buildMenuButton('Logout', () async { final confirm = await showYesNoDialog(context, 'Log out account?'); if (confirm == true) { @@ -26,13 +28,12 @@ class MenusScreen extends StatelessWidget { body: Center( child: Padding( padding: const EdgeInsets.all(8.0), - child: GridView.builder( - itemCount: items.length, + child: GridView( gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( mainAxisExtent: menuButtonHeight, maxCrossAxisExtent: menuButtonWidth, ), - itemBuilder: (context, index) => items[index], + children: menuItems, ), ), ), diff --git a/lib/screens/profile_screen.dart b/lib/screens/profile_screen.dart index 715b27e..ef9a5a7 100644 --- a/lib/screens/profile_screen.dart +++ b/lib/screens/profile_screen.dart @@ -1,10 +1,8 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import '../controls/padding.dart'; import '../controls/standard_appbar.dart'; import '../services/auth_service.dart'; -import '../services/setting_service.dart'; class ProfileScreen extends StatefulWidget { const ProfileScreen({super.key}); @@ -16,7 +14,6 @@ class ProfileScreen extends StatefulWidget { class _ProfileScreenState extends State { @override Widget build(BuildContext context) { - final settings = context.watch(); final authService = context.watch(); return Scaffold( appBar: StandardAppBar.build(context, 'Profile'), @@ -24,25 +21,8 @@ class _ProfileScreenState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Checkbox( - value: settings.lowBandwidthMode, - onChanged: (value) => - settings.lowBandwidthMode = value ?? false, - ), - const Text('Low Bandwidth Mode'), - ], - ), Text( 'Profile: ${authService.currentClient.fold(onSuccess: (client) => client.credentials.handle, onError: (error) => 'Error Getting Profile')}'), - const VerticalPadding(), - ElevatedButton( - onPressed: () async { - await authService.signOut(); - }, - child: const Text('Sign Out')), ], ), ), diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart new file mode 100644 index 0000000..21cda7f --- /dev/null +++ b/lib/screens/settings_screen.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:friendica_portal/services/setting_service.dart'; +import 'package:provider/provider.dart'; + +import '../controls/standard_appbar.dart'; + +class SettingsScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + final settings = context.watch(); + return Scaffold( + appBar: StandardAppBar.build(context, 'Settings'), + body: Center( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: ListView( + children: [ + buildLowBandwidthWidget(settings), + ], + ), + ), + )); + } + + Widget buildLowBandwidthWidget(SettingsService settings) { + return ListTile( + title: const Text('Low bandwidth mode'), + trailing: Checkbox( + onChanged: (value) { + settings.lowBandwidthMode = value ?? false; + }, + value: settings.lowBandwidthMode, + ), + ); + } +} From 8a54707bef4761968e8d6cc948f4426316ac4c43 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 19 Jan 2023 20:37:35 -0500 Subject: [PATCH 4/4] 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'; + } + } +}