diff --git a/assets/2.0x/ic_dark_mode_switch_24dp.png b/assets/2.0x/ic_dark_mode_switch_24dp.png new file mode 100644 index 00000000..c763c412 Binary files /dev/null and b/assets/2.0x/ic_dark_mode_switch_24dp.png differ diff --git a/assets/ic_dark_mode_switch_24dp.png b/assets/ic_dark_mode_switch_24dp.png new file mode 100644 index 00000000..bc939d3b Binary files /dev/null and b/assets/ic_dark_mode_switch_24dp.png differ diff --git a/lib/event/event.dart b/lib/event/event.dart index 5044a333..b0ec26b7 100644 --- a/lib/event/event.dart +++ b/lib/event/event.dart @@ -61,3 +61,5 @@ class FileRemovedEvent { final Account account; final File file; } + +class ThemeChangedEvent {} diff --git a/lib/pref.dart b/lib/pref.dart index f6a64c69..f495c0db 100644 --- a/lib/pref.dart +++ b/lib/pref.dart @@ -56,6 +56,10 @@ class Pref { Future setLastVersion(int value) => _pref.setInt("lastVersion", value); + bool isDarkTheme([bool def = false]) => _pref.getBool("isDarkTheme") ?? def; + + Future setDarkTheme(bool value) => _pref.setBool("isDarkTheme", value); + Pref._(); static final _inst = Pref._(); diff --git a/lib/theme.dart b/lib/theme.dart index 40662487..017977b9 100644 --- a/lib/theme.dart +++ b/lib/theme.dart @@ -105,6 +105,14 @@ class AppTheme extends StatelessWidget { : Colors.white60; } + static Color getAppBarDarkModeSwitchColor(BuildContext context) { + return Colors.black87; + } + + static Color getAppBarDarkModeSwitchTrackColor(BuildContext context) { + return Colors.white.withOpacity(.5); + } + static const primarySwatchLight = Colors.blue; static const primarySwatchDark = Colors.cyan; diff --git a/lib/widget/home_app_bar.dart b/lib/widget/home_app_bar.dart index 181d8305..c9daff03 100644 --- a/lib/widget/home_app_bar.dart +++ b/lib/widget/home_app_bar.dart @@ -1,7 +1,11 @@ +import 'package:event_bus/event_bus.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:kiwi/kiwi.dart'; import 'package:nc_photos/account.dart'; +import 'package:nc_photos/event/event.dart'; +import 'package:nc_photos/pref.dart'; import 'package:nc_photos/theme.dart'; import 'package:nc_photos/widget/account_picker_dialog.dart'; import 'package:nc_photos/widget/settings.dart'; @@ -63,6 +67,19 @@ class HomeSliverAppBar extends StatelessWidget { automaticallyImplyLeading: false, actions: (actions ?? []) + [ + Switch( + value: Theme.of(context).brightness == Brightness.dark, + onChanged: _onDarkModeChanged, + activeColor: AppTheme.getAppBarDarkModeSwitchColor(context), + inactiveThumbColor: + AppTheme.getAppBarDarkModeSwitchColor(context), + activeTrackColor: + AppTheme.getAppBarDarkModeSwitchTrackColor(context), + activeThumbImage: + const AssetImage("assets/ic_dark_mode_switch_24dp.png"), + inactiveThumbImage: + const AssetImage("assets/ic_dark_mode_switch_24dp.png"), + ), PopupMenuButton( tooltip: MaterialLocalizations.of(context).moreButtonTooltip, itemBuilder: (context) => @@ -89,6 +106,12 @@ class HomeSliverAppBar extends StatelessWidget { ); } + void _onDarkModeChanged(bool value) { + Pref.inst().setDarkTheme(value).then((_) { + KiwiContainer().resolve().fire(ThemeChangedEvent()); + }); + } + final Account account; /// Screen specific action buttons diff --git a/lib/widget/my_app.dart b/lib/widget/my_app.dart index 852c7467..e13dfc23 100644 --- a/lib/widget/my_app.dart +++ b/lib/widget/my_app.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:logging/logging.dart'; +import 'package:nc_photos/event/event.dart'; +import 'package:nc_photos/pref.dart'; import 'package:nc_photos/snack_bar_manager.dart'; import 'package:nc_photos/theme.dart'; import 'package:nc_photos/widget/album_viewer.dart'; @@ -14,9 +16,18 @@ import 'package:nc_photos/widget/sign_in.dart'; import 'package:nc_photos/widget/splash.dart'; import 'package:nc_photos/widget/viewer.dart'; -class MyApp extends StatelessWidget implements SnackBarHandler { - MyApp() { +class MyApp extends StatefulWidget { + @override + createState() => _MyAppState(); +} + +class _MyAppState extends State implements SnackBarHandler { + @override + void initState() { + super.initState(); SnackBarManager().registerHandler(this); + _themeChangedListener = + AppEventListener(_onThemeChangedEvent)..begin(); } @override @@ -25,6 +36,7 @@ class MyApp extends StatelessWidget implements SnackBarHandler { onGenerateTitle: (context) => AppLocalizations.of(context).appTitle, theme: _getLightTheme(), darkTheme: _getDarkTheme(), + themeMode: Pref.inst().isDarkTheme() ? ThemeMode.dark : ThemeMode.light, initialRoute: Splash.routeName, onGenerateRoute: _onGenerateRoute, scaffoldMessengerKey: _scaffoldMessengerKey, @@ -34,6 +46,13 @@ class MyApp extends StatelessWidget implements SnackBarHandler { ); } + @override + void dispose() { + super.dispose(); + SnackBarManager().unregisterHandler(this); + _themeChangedListener.end(); + } + @override showSnackBar(SnackBar snackBar) => _scaffoldMessengerKey.currentState?.showSnackBar(snackBar); @@ -67,6 +86,10 @@ class MyApp extends StatelessWidget implements SnackBarHandler { return route; } + void _onThemeChangedEvent(ThemeChangedEvent ev) { + setState(() {}); + } + Route _handleBasicRoute(RouteSettings settings) { for (final e in _getRouter().entries) { if (e.key == settings.name) { @@ -165,5 +188,7 @@ class MyApp extends StatelessWidget implements SnackBarHandler { final _scaffoldMessengerKey = GlobalKey(); - static final _log = Logger("widget.my_app.MyApp"); + AppEventListener _themeChangedListener; + + static final _log = Logger("widget.my_app.MyAppState"); }