From 325f62107d445d6ca01df5a9f1b65dfec24bd9ca Mon Sep 17 00:00:00 2001 From: Christian Pauly Date: Tue, 30 Aug 2022 14:28:48 +0200 Subject: [PATCH] feat: Add color and sty --- assets/l10n/intl_de.arb | 7 +- assets/l10n/intl_en.arb | 7 +- ios/Podfile.lock | 142 +++++++++--------- ios/Runner.xcodeproj/project.pbxproj | 4 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- ios/Runner/Info.plist | 2 + lib/config/app_themes.dart | 62 ++------ lib/main.dart | 23 ++- lib/pages/settings.dart | 109 ++++++++++++++ lib/pages/views/compose_view.dart | 28 ++-- lib/pages/views/messages_view.dart | 1 - lib/pages/views/search_view.dart | 1 + .../views/settings_notifications_view.dart | 10 +- lib/pages/views/settings_view.dart | 32 +++- lib/utils/theme_mode_localization.dart | 15 ++ lib/widgets/nav_scaffold.dart | 56 ++++--- lib/widgets/theme_builder.dart | 96 ++++++++++++ pubspec.lock | 63 ++++++++ pubspec.yaml | 2 + 19 files changed, 477 insertions(+), 185 deletions(-) create mode 100644 lib/utils/theme_mode_localization.dart create mode 100644 lib/widgets/theme_builder.dart diff --git a/assets/l10n/intl_de.arb b/assets/l10n/intl_de.arb index 1c0ef96..295f8a9 100644 --- a/assets/l10n/intl_de.arb +++ b/assets/l10n/intl_de.arb @@ -271,5 +271,10 @@ "forNewFollowers": "Für neue Follower", "@forNewFollowers": {}, "forNewLikes": "Für neue Likes", - "@forNewLikes": {} + "@forNewLikes": {}, + "color": "Farbe", + "light": "Hell", + "dark": "Dunkel", + "system": "System", + "style": "Style" } diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 5f9740e..83d9352 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -173,5 +173,10 @@ "month": {}, "day": {} } - } + }, + "color": "Color", + "light": "Light", + "dark": "Dark", + "system": "System", + "style": "Style" } \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3d64693..164dd05 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,41 +1,41 @@ PODS: - - Firebase/CoreOnly (8.6.0): - - FirebaseCore (= 8.6.0) - - Firebase/Messaging (8.6.0): + - Firebase/CoreOnly (8.15.0): + - FirebaseCore (= 8.15.0) + - Firebase/Messaging (8.15.0): - Firebase/CoreOnly - - FirebaseMessaging (~> 8.6.0) - - firebase_core (1.6.0): - - Firebase/CoreOnly (= 8.6.0) + - FirebaseMessaging (~> 8.15.0) + - firebase_core (1.17.1): + - Firebase/CoreOnly (= 8.15.0) - Flutter - - firebase_messaging (10.0.6): - - Firebase/Messaging (= 8.6.0) + - firebase_messaging (11.4.1): + - Firebase/Messaging (= 8.15.0) - firebase_core - Flutter - - FirebaseCore (8.6.0): + - FirebaseCore (8.15.0): - FirebaseCoreDiagnostics (~> 8.0) - - GoogleUtilities/Environment (~> 7.4) - - GoogleUtilities/Logger (~> 7.4) - - FirebaseCoreDiagnostics (8.8.0): - - GoogleDataTransport (~> 9.0) - - GoogleUtilities/Environment (~> 7.4) - - GoogleUtilities/Logger (~> 7.4) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/Logger (~> 7.7) + - FirebaseCoreDiagnostics (8.15.0): + - GoogleDataTransport (~> 9.1) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/Logger (~> 7.7) - nanopb (~> 2.30908.0) - - FirebaseInstallations (8.8.0): + - FirebaseInstallations (8.15.0): - FirebaseCore (~> 8.0) - - GoogleUtilities/Environment (~> 7.4) - - GoogleUtilities/UserDefaults (~> 7.4) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/UserDefaults (~> 7.7) - PromisesObjC (< 3.0, >= 1.2) - - FirebaseMessaging (8.6.0): + - FirebaseMessaging (8.15.0): - FirebaseCore (~> 8.0) - FirebaseInstallations (~> 8.0) - - GoogleDataTransport (~> 9.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.4) - - GoogleUtilities/Environment (~> 7.4) - - GoogleUtilities/Reachability (~> 7.4) - - GoogleUtilities/UserDefaults (~> 7.4) + - GoogleDataTransport (~> 9.1) + - GoogleUtilities/AppDelegateSwizzler (~> 7.7) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/Reachability (~> 7.7) + - GoogleUtilities/UserDefaults (~> 7.7) - nanopb (~> 2.30908.0) - Flutter (1.0.0) - - flutter_app_badger (0.0.1): + - flutter_app_badger (1.3.0): - Flutter - flutter_inappwebview (0.0.1): - Flutter @@ -49,28 +49,28 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) - - GoogleDataTransport (9.1.0): - - GoogleUtilities/Environment (~> 7.2) - - nanopb (~> 2.30908.0) + - GoogleDataTransport (9.2.0): + - GoogleUtilities/Environment (~> 7.7) + - nanopb (< 2.30910.0, >= 2.30908.0) - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/AppDelegateSwizzler (7.5.2): + - GoogleUtilities/AppDelegateSwizzler (7.7.0): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (7.5.2): + - GoogleUtilities/Environment (7.7.0): - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Logger (7.5.2): + - GoogleUtilities/Logger (7.7.0): - GoogleUtilities/Environment - - GoogleUtilities/Network (7.5.2): + - GoogleUtilities/Network (7.7.0): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (7.5.2)" - - GoogleUtilities/Reachability (7.5.2): + - "GoogleUtilities/NSData+zlib (7.7.0)" + - GoogleUtilities/Reachability (7.7.0): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (7.5.2): + - GoogleUtilities/UserDefaults (7.7.0): - GoogleUtilities/Logger - - image_picker (0.0.1): + - image_picker_ios (0.0.1): - Flutter - nanopb (2.30908.0): - nanopb/decode (= 2.30908.0) @@ -80,21 +80,23 @@ PODS: - OrderedSet (5.0.0) - package_info (0.0.1): - Flutter - - path_provider (0.0.1): + - path_provider_ios (0.0.1): - Flutter - - PromisesObjC (2.0.0) + - PromisesObjC (2.1.1) - receive_sharing_intent (0.0.1): - Flutter - share (0.0.1): - Flutter + - shared_preferences_ios (0.0.1): + - Flutter - sqflite (0.0.2): - Flutter - FMDB (>= 2.7.5) - uni_links (0.0.1): - Flutter - - url_launcher (0.0.1): + - url_launcher_ios (0.0.1): - Flutter - - video_player (0.0.1): + - video_player_avfoundation (0.0.1): - Flutter - wakelock (0.0.1): - Flutter @@ -106,15 +108,16 @@ DEPENDENCIES: - flutter_app_badger (from `.symlinks/plugins/flutter_app_badger/ios`) - flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - - image_picker (from `.symlinks/plugins/image_picker/ios`) + - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - package_info (from `.symlinks/plugins/package_info/ios`) - - path_provider (from `.symlinks/plugins/path_provider/ios`) + - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`) - share (from `.symlinks/plugins/share/ios`) + - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - uni_links (from `.symlinks/plugins/uni_links/ios`) - - url_launcher (from `.symlinks/plugins/url_launcher/ios`) - - video_player (from `.symlinks/plugins/video_player/ios`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) - wakelock (from `.symlinks/plugins/wakelock/ios`) SPEC REPOS: @@ -144,56 +147,59 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_inappwebview/ios" flutter_local_notifications: :path: ".symlinks/plugins/flutter_local_notifications/ios" - image_picker: - :path: ".symlinks/plugins/image_picker/ios" + image_picker_ios: + :path: ".symlinks/plugins/image_picker_ios/ios" package_info: :path: ".symlinks/plugins/package_info/ios" - path_provider: - :path: ".symlinks/plugins/path_provider/ios" + path_provider_ios: + :path: ".symlinks/plugins/path_provider_ios/ios" receive_sharing_intent: :path: ".symlinks/plugins/receive_sharing_intent/ios" share: :path: ".symlinks/plugins/share/ios" + shared_preferences_ios: + :path: ".symlinks/plugins/shared_preferences_ios/ios" sqflite: :path: ".symlinks/plugins/sqflite/ios" uni_links: :path: ".symlinks/plugins/uni_links/ios" - url_launcher: - :path: ".symlinks/plugins/url_launcher/ios" - video_player: - :path: ".symlinks/plugins/video_player/ios" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" + video_player_avfoundation: + :path: ".symlinks/plugins/video_player_avfoundation/ios" wakelock: :path: ".symlinks/plugins/wakelock/ios" SPEC CHECKSUMS: - Firebase: 21ac9f28b09a8bdfc005f34c984fca84e7e8786d - firebase_core: c21ac09a8d23afd3594b56ed786bad12e5266bba - firebase_messaging: 0da89f644566bce5822fa3a5d47c764c44f1cf1d - FirebaseCore: 620b677f70f5470a8e59cb77f3ddc666f6f09785 - FirebaseCoreDiagnostics: fe77f42da6329d6d83d21fd9d621a6b704413bfc - FirebaseInstallations: 2563cb18a723ef9c6ef18318a49519b75dce613c - FirebaseMessaging: ce0a5ee974f7bfe83b6cc5acce88c2d969e37c41 + Firebase: 5f8193dff4b5b7c5d5ef72ae54bb76c08e2b841d + firebase_core: 318de541b0e61d3f24262982a3f0b54afe72439b + firebase_messaging: 943cfe65e0b3f457240489ce67655e40da1d270c + FirebaseCore: 5743c5785c074a794d35f2fff7ecc254a91e08b1 + FirebaseCoreDiagnostics: 92e07a649aeb66352b319d43bdd2ee3942af84cb + FirebaseInstallations: 40bd9054049b2eae9a2c38ef1c3dd213df3605cd + FirebaseMessaging: 5e5118a2383b3531e730d974680954c679ca0a13 Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - flutter_app_badger: 65de4d6f0c34a891df49e6cfb8a1c0496426fa68 + flutter_app_badger: b87fc231847b03b92ce1412aa351842e7e97932f flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721 flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - GoogleDataTransport: 85fd18ff3019bb85d3f2c551d04c481dedf71fc9 - GoogleUtilities: 8de2a97a17e15b6b98e38e8770e2d129a57c0040 - image_picker: e06f7a68f000bd36f552c1847e33cda96ed31f1f + GoogleDataTransport: 1c8145da7117bd68bbbed00cf304edb6a24de00f + GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1 + image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 - path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c - PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 + path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 + PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb receive_sharing_intent: c0d87310754e74c0f9542947e7cbdf3a0335a3b1 share: 0b2c3e82132f5888bccca3351c504d0003b3b410 + shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 uni_links: d97da20c7701486ba192624d99bffaaffcfc298a - url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef - video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e + url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de + video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f PODFILE CHECKSUM: fe0e1ee7f3d1f7d00b11b474b62dd62134535aea -COCOAPODS: 1.11.2 +COCOAPODS: 1.11.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 3115786..ace1d9a 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -233,7 +233,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1300; - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { 35BC5D28270B6FA4006AD137 = { @@ -799,4 +799,4 @@ /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; -} \ No newline at end of file +} diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140c..3db53b6 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + diff --git a/lib/config/app_themes.dart b/lib/config/app_themes.dart index d600f52..0d46952 100644 --- a/lib/config/app_themes.dart +++ b/lib/config/app_themes.dart @@ -1,58 +1,28 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:fluffypix/config/app_configs.dart'; abstract class AppThemes { static const double radius = 12; - static ThemeData get light => ThemeData( - visualDensity: VisualDensity.standard, + static ThemeData buildTheme( + ColorScheme? scheme, Color? primaryColor, bool isLight) => + ThemeData( + brightness: isLight ? Brightness.light : Brightness.dark, useMaterial3: true, - brightness: Brightness.light, - colorSchemeSeed: AppConfigs.primaryColor, - snackBarTheme: - const SnackBarThemeData(behavior: SnackBarBehavior.floating), - pageTransitionsTheme: const PageTransitionsTheme( - builders: { - TargetPlatform.fuchsia: ZoomPageTransitionsBuilder(), - TargetPlatform.android: ZoomPageTransitionsBuilder(), - TargetPlatform.linux: CupertinoPageTransitionsBuilder(), - TargetPlatform.macOS: CupertinoPageTransitionsBuilder(), - TargetPlatform.windows: CupertinoPageTransitionsBuilder(), - TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), - }, + colorScheme: primaryColor != null ? null : scheme, + appBarTheme: AppBarTheme( + systemOverlayStyle: SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + statusBarIconBrightness: + isLight ? Brightness.dark : Brightness.light, + statusBarBrightness: !isLight ? Brightness.dark : Brightness.light, + ), ), - inputDecorationTheme: InputDecorationTheme( - border: const UnderlineInputBorder(borderSide: BorderSide(width: 1)), - filled: true, - ), - appBarTheme: const AppBarTheme(centerTitle: true), - dividerColor: Colors.grey.shade100, - ); - - static ThemeData get dark => ThemeData( - visualDensity: VisualDensity.standard, - useMaterial3: true, - brightness: Brightness.dark, - colorSchemeSeed: AppConfigs.primaryColor, - snackBarTheme: - const SnackBarThemeData(behavior: SnackBarBehavior.floating), - pageTransitionsTheme: const PageTransitionsTheme( - builders: { - TargetPlatform.fuchsia: ZoomPageTransitionsBuilder(), - TargetPlatform.android: ZoomPageTransitionsBuilder(), - TargetPlatform.linux: CupertinoPageTransitionsBuilder(), - TargetPlatform.macOS: CupertinoPageTransitionsBuilder(), - TargetPlatform.windows: CupertinoPageTransitionsBuilder(), - TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), - }, - ), - inputDecorationTheme: InputDecorationTheme( - border: const UnderlineInputBorder(borderSide: BorderSide(width: 1)), - filled: true, - ), - appBarTheme: const AppBarTheme(centerTitle: true), - dividerColor: Colors.grey.shade700, + dividerColor: isLight ? Colors.grey.shade200 : Colors.grey.shade700, + colorSchemeSeed: + primaryColor ?? (scheme == null ? AppConfigs.primaryColor : null), ); static const double columnWidth = 300; diff --git a/lib/main.dart b/lib/main.dart index 80ab7d9..7db15bd 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,11 +1,13 @@ import 'package:flutter/material.dart'; +import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:fluffypix/config/app_configs.dart'; import 'package:fluffypix/config/app_routes.dart'; import 'package:fluffypix/config/app_themes.dart'; import 'package:fluffypix/model/fluffy_pix.dart'; +import 'package:fluffypix/widgets/theme_builder.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -20,14 +22,19 @@ class FluffyPixApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - title: AppConfigs.applicationName, - theme: AppThemes.light, - darkTheme: AppThemes.dark, - builder: fluffyPix.builder, - onGenerateRoute: AppRoutes(fluffyPix).onGenerateRoute, - localizationsDelegates: L10n.localizationsDelegates, - supportedLocales: L10n.supportedLocales, + return ThemeBuilder( + builder: (context, themeMode, color) => DynamicColorBuilder( + builder: (light, dark) => MaterialApp( + title: AppConfigs.applicationName, + themeMode: themeMode, + theme: AppThemes.buildTheme(light, color, true), + darkTheme: AppThemes.buildTheme(dark, color, false), + builder: fluffyPix.builder, + onGenerateRoute: AppRoutes(fluffyPix).onGenerateRoute, + localizationsDelegates: L10n.localizationsDelegates, + supportedLocales: L10n.supportedLocales, + ), + ), ); } } diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 3fd4a28..0b78c18 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -12,6 +13,8 @@ import 'package:url_launcher/url_launcher_string.dart'; import 'package:fluffypix/config/app_configs.dart'; import 'package:fluffypix/model/fluffy_pix.dart'; import 'package:fluffypix/model/fluffy_pix_login_extension.dart'; +import 'package:fluffypix/utils/theme_mode_localization.dart'; +import '../widgets/theme_builder.dart'; import 'login.dart'; import 'views/settings_view.dart'; @@ -95,6 +98,112 @@ class SettingsPageController extends State { FluffyPix.of(context).useInAppBrowser = b; }); + void setThemeMode() async { + await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(L10n.of(context)!.style), + content: StatefulBuilder(builder: (context, setState) { + final groupValue = ThemeController.of(context).themeMode; + // ignore: prefer_function_declarations_over_variables + final onChanged = (val) { + setState(() { + ThemeController.of(context).setThemeMode(val); + }); + }; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + RadioListTile( + contentPadding: EdgeInsets.zero, + value: ThemeMode.system, + groupValue: groupValue, + onChanged: onChanged, + title: Text(ThemeMode.system.toLocalizedString(context)), + ), + RadioListTile( + contentPadding: EdgeInsets.zero, + value: ThemeMode.light, + groupValue: groupValue, + onChanged: onChanged, + title: Text(ThemeMode.light.toLocalizedString(context)), + ), + RadioListTile( + contentPadding: EdgeInsets.zero, + value: ThemeMode.dark, + groupValue: groupValue, + onChanged: onChanged, + title: Text(ThemeMode.dark.toLocalizedString(context)), + ), + ], + ); + }), + actions: [ + TextButton( + onPressed: Navigator.of(context).pop, + child: Text(L10n.of(context)!.close), + ), + ], + ), + ); + setState(() {}); + } + + void setColor() async { + await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(L10n.of(context)!.color), + content: StatefulBuilder(builder: (context, setState) { + final groupValue = ThemeController.of(context).primaryColor; + // ignore: prefer_function_declarations_over_variables + final onChanged = (val) { + setState(() { + ThemeController.of(context).setPrimaryColor(val); + }); + }; + const colors = [ + null, + AppConfigs.primaryColor, + Colors.blue, + Colors.green, + Colors.yellow, + Colors.red, + Colors.pink, + Colors.teal, + ]; + return SizedBox( + height: 360, + width: 360, + child: ListView( + children: colors + .map((color) => RadioListTile( + contentPadding: EdgeInsets.zero, + value: color, + groupValue: groupValue, + onChanged: onChanged, + title: color == null + ? Text(L10n.of(context)!.system) + : Align( + alignment: Alignment.centerLeft, + child: Icon(Icons.circle, color: color), + ), + )) + .toList(), + ), + ); + }), + actions: [ + TextButton( + onPressed: Navigator.of(context).pop, + child: Text(L10n.of(context)!.close), + ), + ], + ), + ); + setState(() {}); + } + @override Widget build(BuildContext context) => SettingsPageView(this); } diff --git a/lib/pages/views/compose_view.dart b/lib/pages/views/compose_view.dart index 3d07d46..3283452 100644 --- a/lib/pages/views/compose_view.dart +++ b/lib/pages/views/compose_view.dart @@ -23,7 +23,7 @@ class ComposePageView extends StatelessWidget { automaticallyImplyLeading: false, leadingWidth: 64, leading: TextButton( - child: Text(L10n.of(context)!.reset), + child: Text(L10n.of(context)!.reset, maxLines: 1), onPressed: controller.loading || controller.loadingPhoto ? null : controller.resetAction, @@ -69,6 +69,7 @@ class ComposePageView extends StatelessWidget { controller: controller.statusController, decoration: InputDecoration( hintText: L10n.of(context)!.howDoYouFeel, + filled: true, contentPadding: const EdgeInsets.all(12), ), ), @@ -83,25 +84,18 @@ class ComposePageView extends StatelessWidget { : Row( children: [ Expanded( - child: SizedBox( - height: 56, - child: OutlinedButton.icon( - onPressed: controller.addMedia, - icon: const Icon(CupertinoIcons.photo), - label: Text(L10n.of(context)!.addPhoto), - ), + child: OutlinedButton.icon( + onPressed: controller.addMedia, + icon: const Icon(CupertinoIcons.photo), + label: Text(L10n.of(context)!.addPhoto), ), ), const SizedBox(width: 12), Expanded( - child: SizedBox( - height: 56, - child: OutlinedButton.icon( - onPressed: () => - controller.addMedia(video: true), - icon: const Icon(CupertinoIcons.video_camera), - label: Text(L10n.of(context)!.addVideo), - ), + child: OutlinedButton.icon( + onPressed: () => controller.addMedia(video: true), + icon: const Icon(CupertinoIcons.video_camera), + label: Text(L10n.of(context)!.addVideo), ), ), ], @@ -117,7 +111,7 @@ class ComposePageView extends StatelessWidget { Text(controller.visibility.toLocalizedString(context)), ), const SizedBox(height: 12), - SwitchListTile( + SwitchListTile.adaptive( controlAffinity: ListTileControlAffinity.trailing, value: controller.sensitive, onChanged: controller.toggleSensitive, diff --git a/lib/pages/views/messages_view.dart b/lib/pages/views/messages_view.dart index f6a67d1..d6e0d70 100644 --- a/lib/pages/views/messages_view.dart +++ b/lib/pages/views/messages_view.dart @@ -36,7 +36,6 @@ class MessagesPageView extends StatelessWidget { ), ), scrollController: controller.scrollController, - currentIndex: 6, ); } } diff --git a/lib/pages/views/search_view.dart b/lib/pages/views/search_view.dart index e25d8e1..c2b84cd 100644 --- a/lib/pages/views/search_view.dart +++ b/lib/pages/views/search_view.dart @@ -28,6 +28,7 @@ class SearchPageView extends StatelessWidget { onChanged: controller.searchQueryWithCooldown, onSubmitted: controller.searchQuery, decoration: InputDecoration( + filled: true, contentPadding: EdgeInsets.symmetric( horizontal: 16, vertical: 4, diff --git a/lib/pages/views/settings_notifications_view.dart b/lib/pages/views/settings_notifications_view.dart index b0586f4..6d9555a 100644 --- a/lib/pages/views/settings_notifications_view.dart +++ b/lib/pages/views/settings_notifications_view.dart @@ -37,27 +37,27 @@ class SettingsNotificationsPageView extends StatelessWidget { final loading = snapshot.connectionState != ConnectionState.done; return ListView( children: [ - SwitchListTile( + SwitchListTile.adaptive( value: alerts.favourite ?? false, onChanged: loading ? null : controller.toggleFavourite, title: Text(L10n.of(context)!.forNewLikes), ), - SwitchListTile( + SwitchListTile.adaptive( value: alerts.follow ?? false, onChanged: loading ? null : controller.toggleFollow, title: Text(L10n.of(context)!.forNewFollowers), ), - SwitchListTile( + SwitchListTile.adaptive( value: alerts.mention ?? false, onChanged: loading ? null : controller.toggleMention, title: Text(L10n.of(context)!.forNewMentions), ), - SwitchListTile( + SwitchListTile.adaptive( value: alerts.reblog ?? false, onChanged: loading ? null : controller.toggleReblog, title: Text(L10n.of(context)!.forNewSharings), ), - SwitchListTile( + SwitchListTile.adaptive( value: alerts.poll ?? false, onChanged: loading ? null : controller.togglePoll, title: Text(L10n.of(context)!.forEndingPolls), diff --git a/lib/pages/views/settings_view.dart b/lib/pages/views/settings_view.dart index 43bdfe6..9ee866c 100644 --- a/lib/pages/views/settings_view.dart +++ b/lib/pages/views/settings_view.dart @@ -3,8 +3,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:fluffypix/config/app_configs.dart'; import 'package:fluffypix/utils/custom_about_dialog.dart'; -import 'package:fluffypix/widgets/nav_scaffold.dart'; +import 'package:fluffypix/utils/theme_mode_localization.dart'; +import 'package:fluffypix/widgets/theme_builder.dart'; import '../settings.dart'; class SettingsPageView extends StatelessWidget { @@ -13,13 +15,32 @@ class SettingsPageView extends StatelessWidget { const SettingsPageView(this.controller, {Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return NavScaffold( + return Scaffold( appBar: AppBar( title: Text(L10n.of(context)!.settings), ), body: ListView( controller: controller.scrollController, children: [ + ListTile( + leading: const Icon(CupertinoIcons.paintbrush), + title: Text(L10n.of(context)!.style), + trailing: Text(ThemeController.of(context) + .themeMode + .toLocalizedString(context)), + onTap: controller.setThemeMode, + ), + ListTile( + leading: const Icon(CupertinoIcons.color_filter), + title: Text(L10n.of(context)!.color), + trailing: Icon( + Icons.circle, + color: ThemeController.of(context).primaryColor ?? + AppConfigs.primaryColor, + ), + onTap: controller.setColor, + ), + const Divider(), ListTile( leading: const Icon(CupertinoIcons.person), title: Text(L10n.of(context)!.account), @@ -31,17 +52,17 @@ class SettingsPageView extends StatelessWidget { onTap: controller.goToNotificationSettings, ), const Divider(), - SwitchListTile( + SwitchListTile.adaptive( value: controller.allowAnimatedAvatars, onChanged: controller.setAllowAnimatedAvatars, title: Text(L10n.of(context)!.allowAnimatedAvatars), ), - SwitchListTile( + SwitchListTile.adaptive( value: controller.displayThumbnailsOnly, onChanged: controller.setDisplayThumbnailsOnly, title: Text(L10n.of(context)!.displayThumbnailsOnly), ), - SwitchListTile( + SwitchListTile.adaptive( value: controller.useInAppBrowser, onChanged: controller.setUseInAppBrowser, title: Text(L10n.of(context)!.openLinksInAppBrowser), @@ -72,7 +93,6 @@ class SettingsPageView extends StatelessWidget { ), ], ), - currentIndex: 5, ); } } diff --git a/lib/utils/theme_mode_localization.dart b/lib/utils/theme_mode_localization.dart new file mode 100644 index 0000000..7cda7ef --- /dev/null +++ b/lib/utils/theme_mode_localization.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +extension ThemeModeToString on ThemeMode { + String toLocalizedString(BuildContext context) { + if (this == ThemeMode.light) { + return L10n.of(context)!.light; + } + if (this == ThemeMode.dark) { + return L10n.of(context)!.dark; + } + return L10n.of(context)!.system; + } +} diff --git a/lib/widgets/nav_scaffold.dart b/lib/widgets/nav_scaffold.dart index 2052b90..abe13ea 100644 --- a/lib/widgets/nav_scaffold.dart +++ b/lib/widgets/nav_scaffold.dart @@ -85,58 +85,56 @@ class NavScaffold extends StatelessWidget { floatingActionButtonLocation: floatingActionButtonLocation, bottomNavigationBar: AppThemes.isColumnMode(context) ? null - : BottomNavigationBar( - showSelectedLabels: false, - showUnselectedLabels: false, - type: BottomNavigationBarType.fixed, - selectedIconTheme: IconThemeData( - color: Theme.of(context).colorScheme.primary, - ), - unselectedIconTheme: IconThemeData( - color: Theme.of(context).colorScheme.onBackground, - ), - currentIndex: (!AppThemes.isColumnMode(context) && - currentIndex != null && - currentIndex! > 4) - ? 4 - : currentIndex ?? 4, - onTap: (i) => onTap(i, context), - items: [ - BottomNavigationBarItem( + : NavigationBar( + labelBehavior: NavigationDestinationLabelBehavior.alwaysHide, + onDestinationSelected: (i) => onTap(i, context), + selectedIndex: currentIndex ?? 0, + destinations: [ + NavigationDestination( icon: Icon( - currentIndex == 0 ? Icons.home : Icons.home_outlined, + Icons.home_outlined, + size: 28, + ), + selectedIcon: Icon( + Icons.home, size: 28, ), label: L10n.of(context)!.home, ), - BottomNavigationBarItem( + NavigationDestination( icon: const Icon( CupertinoIcons.search, size: 28, ), label: L10n.of(context)!.account, ), - BottomNavigationBarItem( + NavigationDestination( icon: Icon( - currentIndex == 2 - ? CupertinoIcons.add_circled_solid - : CupertinoIcons.add_circled, + CupertinoIcons.add_circled, + size: 28, + ), + selectedIcon: Icon( + CupertinoIcons.add_circled_solid, size: 28, ), label: L10n.of(context)!.newStatus, ), - BottomNavigationBarItem( + NavigationDestination( icon: NotificationCountBuilder( builder: (_) => Icon( - currentIndex == 3 - ? CupertinoIcons.heart_fill - : CupertinoIcons.heart, + CupertinoIcons.heart, + size: 28, + ), + ), + selectedIcon: NotificationCountBuilder( + builder: (_) => Icon( + CupertinoIcons.heart_fill, size: 28, ), ), label: L10n.of(context)!.notifications, ), - BottomNavigationBarItem( + NavigationDestination( icon: Avatar( account: FluffyPix.of(context).ownAccount!, radius: 16, diff --git a/lib/widgets/theme_builder.dart b/lib/widgets/theme_builder.dart new file mode 100644 index 0000000..24caf18 --- /dev/null +++ b/lib/widgets/theme_builder.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; + +import 'package:collection/collection.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class ThemeBuilder extends StatefulWidget { + final Widget Function( + BuildContext context, + ThemeMode themeMode, + Color? primaryColor, + ) builder; + + final String themeModeSettingsKey; + final String primaryColorSettingsKey; + + const ThemeBuilder({ + required this.builder, + this.themeModeSettingsKey = 'theme_mode', + this.primaryColorSettingsKey = 'primary_color', + Key? key, + }) : super(key: key); + + @override + State createState() => ThemeController(); +} + +class ThemeController extends State { + SharedPreferences? _sharedPreferences; + ThemeMode? _themeMode; + Color? _primaryColor; + + ThemeMode get themeMode => _themeMode ?? ThemeMode.system; + Color? get primaryColor => _primaryColor; + + static ThemeController of(BuildContext context) => + Provider.of( + context, + listen: false, + ); + + void _loadData(_) async { + final preferences = + _sharedPreferences ??= await SharedPreferences.getInstance(); + + final rawThemeMode = preferences.getString(widget.themeModeSettingsKey); + final rawColor = preferences.getInt(widget.primaryColorSettingsKey); + + setState(() { + _themeMode = ThemeMode.values + .singleWhereOrNull((value) => value.name == rawThemeMode); + _primaryColor = rawColor == null ? null : Color(rawColor); + }); + } + + Future setThemeMode(ThemeMode newThemeMode) async { + final preferences = + _sharedPreferences ??= await SharedPreferences.getInstance(); + await preferences.setString(widget.themeModeSettingsKey, newThemeMode.name); + setState(() { + _themeMode = newThemeMode; + }); + } + + Future setPrimaryColor(Color? newPrimaryColor) async { + final preferences = + _sharedPreferences ??= await SharedPreferences.getInstance(); + if (newPrimaryColor == null) { + await preferences.remove(widget.primaryColorSettingsKey); + } else { + await preferences.setInt( + widget.primaryColorSettingsKey, + newPrimaryColor.value, + ); + } + setState(() { + _primaryColor = newPrimaryColor; + }); + } + + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback(_loadData); + super.initState(); + } + + @override + Widget build(BuildContext context) => Provider( + create: (_) => this, + child: widget.builder( + context, + themeMode, + primaryColor, + ), + ); +} diff --git a/pubspec.lock b/pubspec.lock index c3b7ba3..4bff45a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -134,6 +134,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.5.4" + dynamic_color: + dependency: "direct main" + description: + name: dynamic_color + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.0" elliptic: dependency: "direct main" description: @@ -665,6 +672,62 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.4" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.15" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.12" + shared_preferences_ios: + dependency: transitive + description: + name: shared_preferences_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shared_preferences_macos: + dependency: transitive + description: + name: shared_preferences_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 6ba7728..25b576b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,8 @@ dependencies: flick_video_player: ^0.3.1 video_player: ^2.1.15 flutter_local_notifications: ^9.1.4 + shared_preferences: ^2.0.15 + dynamic_color: ^1.4.0 dev_dependencies: flutter_lints: ^2.0.1