From a3d712d11ddc637d6aa5d6a62d3febd3ef1f90b3 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 16 Apr 2023 21:10:57 +1000 Subject: [PATCH] Adds "dark mode" support (#317) * Adds "dark mode" support - Uses adaptive_theme package * CI fixes * More fixes * Update release notes --- assets/release_notes.md | 2 ++ lib/l10n/app_en.arb | 6 ++++ lib/main.dart | 58 +++++++++++++++++++++++----------- lib/settings/app_settings.dart | 37 ++++++++++++++++++++-- lib/settings/settings.dart | 16 +++++----- pubspec.lock | 8 +++++ pubspec.yaml | 1 + 7 files changed, 99 insertions(+), 29 deletions(-) diff --git a/assets/release_notes.md b/assets/release_notes.md index 385d3072..e3018f1b 100644 --- a/assets/release_notes.md +++ b/assets/release_notes.md @@ -4,10 +4,12 @@ ### 0.11.2 - April 2023 --- +- Adds "dark mode" display option - Add action to issue a purchase order - Add action to cancel a purchase order - Reimplement periodic checks for notifications + ### 0.11.1 - April 2023 --- diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index e9d81e97..661e300f 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -211,6 +211,12 @@ "damaged": "Damaged", "@damaged": {}, + "darkMode": "Dark Mode", + "@darkMode": {}, + + "darkModeEnable": "Enable dark mode", + "@darkModeEnable": {}, + "delete": "Delete", "@delete": {}, diff --git a/lib/main.dart b/lib/main.dart index ef9b49fb..f41357af 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import "dart:async"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; +import "package:adaptive_theme/adaptive_theme.dart"; import "package:flutter_gen/gen_l10n/app_localizations.dart"; import "package:flutter_localizations/flutter_localizations.dart"; import "package:flutter_localized_locales/flutter_localized_locales.dart"; @@ -22,6 +23,8 @@ Future main() async { WidgetsFlutterBinding.ensureInitialized(); + final savedThemeMode = await AdaptiveTheme.getThemeMode(); + await runZonedGuarded>(() async { PackageInfo info = await PackageInfo.fromPlatform(); @@ -53,7 +56,7 @@ Future main() async { }; runApp( - InvenTreeApp() + InvenTreeApp(savedThemeMode) ); }, (Object error, StackTrace stackTrace) async { @@ -65,8 +68,12 @@ Future main() async { class InvenTreeApp extends StatefulWidget { // This widget is the root of your application. + const InvenTreeApp(this.savedThemeMode); + + final AdaptiveThemeMode? savedThemeMode; + @override - InvenTreeAppState createState() => InvenTreeAppState(); + InvenTreeAppState createState() => InvenTreeAppState(savedThemeMode); static InvenTreeAppState? of(BuildContext context) => context.findAncestorStateOfType(); @@ -75,9 +82,13 @@ class InvenTreeApp extends StatefulWidget { class InvenTreeAppState extends State { + InvenTreeAppState(this.savedThemeMode) : super(); + // Custom _locale (default = null; use system default) Locale? _locale; + final AdaptiveThemeMode? savedThemeMode; + @override void initState() { super.initState(); @@ -122,25 +133,36 @@ class InvenTreeAppState extends State { @override Widget build(BuildContext context) { - return MaterialApp( - debugShowCheckedModeBanner: false, - builder: OneContext().builder, - navigatorKey: OneContext().key, - onGenerateTitle: (BuildContext context) => "InvenTree", - theme: ThemeData( + return AdaptiveTheme( + light: ThemeData( + brightness: Brightness.light, + primarySwatch: Colors.lightBlue, + secondaryHeaderColor: Colors.blueGrey + ), + dark: ThemeData( + brightness: Brightness.dark, primarySwatch: Colors.lightBlue, secondaryHeaderColor: Colors.blueGrey, ), - home: InvenTreeHomePage(), - localizationsDelegates: [ - I18N.delegate, - LocaleNamesLocalizationsDelegate(), - GlobalMaterialLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ], - supportedLocales: supported_locales, - locale: _locale, + initial: savedThemeMode ?? AdaptiveThemeMode.light, + builder: (light, dark) => MaterialApp( + theme: light, + darkTheme: dark, + debugShowCheckedModeBanner: false, + builder: OneContext().builder, + navigatorKey: OneContext().key, + onGenerateTitle: (BuildContext context) => "InvenTree", + home: InvenTreeHomePage(), + localizationsDelegates: [ + I18N.delegate, + LocaleNamesLocalizationsDelegate(), + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: supported_locales, + locale: _locale, + ) ); } } diff --git a/lib/settings/app_settings.dart b/lib/settings/app_settings.dart index ad7e58dc..42c94a00 100644 --- a/lib/settings/app_settings.dart +++ b/lib/settings/app_settings.dart @@ -1,7 +1,9 @@ - import "package:flutter/material.dart"; + +import "package:adaptive_theme/adaptive_theme.dart"; import "package:font_awesome_flutter/font_awesome_flutter.dart"; import "package:flutter_localized_locales/flutter_localized_locales.dart"; +import "package:one_context/one_context.dart"; import "package:inventree/api_form.dart"; import "package:inventree/l10.dart"; @@ -9,6 +11,8 @@ import "package:inventree/l10n/supported_locales.dart"; import "package:inventree/main.dart"; import "package:inventree/preferences.dart"; +import "package:inventree/widget/progress.dart"; + class InvenTreeAppSettingsWidget extends StatefulWidget { @override @@ -28,23 +32,32 @@ class _InvenTreeAppSettingsState extends State { bool reportErrors = true; bool strictHttps = false; + bool darkMode = false; + Locale? locale; @override void initState() { super.initState(); - loadSettings(); + loadSettings(OneContext().context!); } - Future loadSettings() async { + Future loadSettings(BuildContext context) async { + + showLoadingOverlay(context); + barcodeSounds = await InvenTreeSettingsManager().getValue(INV_SOUNDS_BARCODE, true) as bool; serverSounds = await InvenTreeSettingsManager().getValue(INV_SOUNDS_SERVER, true) as bool; reportErrors = await InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS, true) as bool; strictHttps = await InvenTreeSettingsManager().getValue(INV_STRICT_HTTPS, false) as bool; + darkMode = AdaptiveTheme.of(context).mode.isDark; + locale = await InvenTreeSettingsManager().getSelectedLocale(); + hideLoadingOverlay(); + if (mounted) { setState(() {}); } @@ -168,6 +181,24 @@ class _InvenTreeAppSettingsState extends State { ), leading: FaIcon(FontAwesomeIcons.mobile), ), + ListTile( + title: Text(L10().darkMode), + subtitle: Text(L10().darkModeEnable), + leading: FaIcon(FontAwesomeIcons.moon), + trailing: Switch( + value: darkMode, + onChanged: (bool value) { + if (value) { + AdaptiveTheme.of(context).setDark(); + } else { + AdaptiveTheme.of(context).setLight(); + } + setState(() { + darkMode = value; + }); + } + ) + ), ListTile( title: Text(L10().strictHttps), subtitle: Text(L10().strictHttpsDetails), diff --git a/lib/settings/settings.dart b/lib/settings/settings.dart index 9c2c95cf..ed589e1e 100644 --- a/lib/settings/settings.dart +++ b/lib/settings/settings.dart @@ -52,6 +52,14 @@ class _InvenTreeSettingsState extends State { Navigator.push(context, MaterialPageRoute(builder: (context) => InvenTreeLoginSettingsWidget())); }, ), + ListTile( + title: Text(L10().appSettings), + subtitle: Text(L10().appSettingsDetails), + leading: FaIcon(FontAwesomeIcons.gears, color: COLOR_CLICK), + onTap: () { + Navigator.push(context, MaterialPageRoute(builder: (context) => InvenTreeAppSettingsWidget())); + } + ), ListTile( title: Text(L10().homeScreen), subtitle: Text(L10().homeScreenSettings), @@ -60,14 +68,6 @@ class _InvenTreeSettingsState extends State { Navigator.push(context, MaterialPageRoute(builder: (context) => HomeScreenSettingsWidget())); } ), - ListTile( - title: Text(L10().appSettings), - subtitle: Text(L10().appSettingsDetails), - leading: FaIcon(FontAwesomeIcons.gears, color: COLOR_CLICK), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => InvenTreeAppSettingsWidget())); - } - ), ListTile( title: Text(L10().part), subtitle: Text(L10().partSettings), diff --git a/pubspec.lock b/pubspec.lock index 336cbeb4..ac1db997 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "52.0.0" + adaptive_theme: + dependency: "direct main" + description: + name: adaptive_theme + sha256: "61bde10390e937d11d05c6cf0d5cf378a73d49f9a442262e43613dae60ed0b3f" + url: "https://pub.dev" + source: hosted + version: "3.2.0" analyzer: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3f157d67..91dc1d80 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,6 +7,7 @@ environment: sdk: ">=2.16.0 <3.0.0" dependencies: + adaptive_theme: ^3.2.0 # Theme management (e.g. dark mode) audioplayers: ^3.0.1 # Play audio files cached_network_image: ^3.2.0 # Download and cache remote images camera: ^0.10.3 # Camera