From 1ec4a6503226645a7d1ec6cac3e6cc39dab14b54 Mon Sep 17 00:00:00 2001 From: odweta Date: Fri, 3 Apr 2026 14:26:57 +0200 Subject: [PATCH] update dark mode handling --- frontend/lib/main.dart | 19 +++++-- frontend/lib/pages/list.dart | 33 +++++++++-- frontend/lib/provider/theme_provider.dart | 34 +++++++++++ frontend/lib/values/strings.dart | 2 + .../Flutter/GeneratedPluginRegistrant.swift | 2 + frontend/pubspec.lock | 56 +++++++++++++++++++ frontend/pubspec.yaml | 1 + 7 files changed, 136 insertions(+), 11 deletions(-) create mode 100644 frontend/lib/provider/theme_provider.dart diff --git a/frontend/lib/main.dart b/frontend/lib/main.dart index 49bedce..95cc867 100644 --- a/frontend/lib/main.dart +++ b/frontend/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:frontend/provider/theme_provider.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; @@ -17,10 +18,17 @@ import 'pages/leaderboard.dart'; void main() { runApp( - ChangeNotifierProvider( - create: (_) => QuoteProvider(), - child: HlaskovnikFrontendApp(), - ), + MultiProvider( + providers: [ + ChangeNotifierProvider( + create: (_) => QuoteProvider() + ), + ChangeNotifierProvider( + create: (_) => ThemeProvider() + ) + ], + child: HlaskovnikFrontendApp() + ) ); } @@ -46,6 +54,7 @@ class HlaskovnikFrontendApp extends StatelessWidget { @override Widget build(BuildContext context) { + final themeProvider = context.watch(); return MaterialApp.router( title: Strings.appName, theme: ThemeData( @@ -62,7 +71,7 @@ class HlaskovnikFrontendApp extends StatelessWidget { ), useMaterial3: true, ), - themeMode: ThemeMode.system, + themeMode: themeProvider.isDark ? ThemeMode.dark : ThemeMode.system, routerConfig: _router, ); } diff --git a/frontend/lib/pages/list.dart b/frontend/lib/pages/list.dart index 407b88f..8a9ba4c 100644 --- a/frontend/lib/pages/list.dart +++ b/frontend/lib/pages/list.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:frontend/provider/quote_provider.dart'; import 'package:frontend/model/quote.dart'; +import 'package:frontend/provider/theme_provider.dart'; import 'package:frontend/values/strings.dart'; import 'package:provider/provider.dart'; @@ -13,15 +14,16 @@ class ListPage extends StatelessWidget { @override Widget build(BuildContext context) { final provider = context.watch(); + final themeProvider = context.watch(); return RefreshIndicator( onRefresh: () async { await provider.readQuotes(); }, - child: _bodyBuilder(context, provider), + child: _bodyBuilder(context, provider, themeProvider), ); } - Widget _bodyBuilder(BuildContext context, QuoteProvider provider) { + Widget _bodyBuilder(BuildContext context, QuoteProvider provider, ThemeProvider themeProvider) { if (provider.loading) { return Center(child: CircularProgressIndicator()); } @@ -51,7 +53,7 @@ class ListPage extends StatelessWidget { ), ), ), - filterBar(context, provider), + filterBar(context, provider, themeProvider), Expanded( child: ListView.builder( padding: EdgeInsets.all(Constants.edgeInset), @@ -220,7 +222,7 @@ class ListPage extends StatelessWidget { ); } - Widget filterBar(BuildContext context, QuoteProvider provider) { + Widget filterBar(BuildContext context, QuoteProvider provider, ThemeProvider themeProvider) { return Padding( padding: const EdgeInsets.all(Constants.edgeInset), child: Row( @@ -246,7 +248,7 @@ class ListPage extends StatelessWidget { IconButton( icon: Icon(Icons.more_horiz), onPressed: () { - _showOptionsBottomSheet(context, provider); + _showOptionsBottomSheet(context, provider, themeProvider); }, ), ], @@ -254,7 +256,7 @@ class ListPage extends StatelessWidget { ); } - void _showOptionsBottomSheet(BuildContext context, QuoteProvider provider) { + void _showOptionsBottomSheet(BuildContext context, QuoteProvider provider, ThemeProvider themeProvider) { showModalBottomSheet( context: context, builder: (context) { @@ -284,6 +286,25 @@ class ListPage extends StatelessWidget { ); } ), + ListTile( + title: Text(Strings.userPrefersDarkMode), + onTap: () { themeProvider.setDarkMode(!themeProvider.isDark); }, + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.only(right: Constants.edgeInset), + child: Switch( + onChanged: (value) { + themeProvider.setDarkMode(value); + }, + value: themeProvider.isDark + ), + ), + Icon(Icons.dark_mode), + ], + ), + ), ], ), ); diff --git a/frontend/lib/provider/theme_provider.dart b/frontend/lib/provider/theme_provider.dart new file mode 100644 index 0000000..25e8560 --- /dev/null +++ b/frontend/lib/provider/theme_provider.dart @@ -0,0 +1,34 @@ + +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +Future saveDarkMode(bool isDark) async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool('darkMode', isDark); +} + +Future loadDarkMode() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getBool('darkMode') ?? false; // default light +} + +class ThemeProvider extends ChangeNotifier { + bool _isDark = false; + + bool get isDark => _isDark; + + ThemeProvider() { + _load(); // load saved value on startup + } + + void _load() async { + _isDark = await loadDarkMode(); + notifyListeners(); + } + + void setDarkMode(bool value) async { + _isDark = value; + notifyListeners(); + await saveDarkMode(value); + } +} \ No newline at end of file diff --git a/frontend/lib/values/strings.dart b/frontend/lib/values/strings.dart index 954035d..c7d8193 100644 --- a/frontend/lib/values/strings.dart +++ b/frontend/lib/values/strings.dart @@ -30,4 +30,6 @@ class Strings { static const String filterBy = "Filtrovat podle"; static const String exportSelectedQuotes = "Exportovat vybrané hlášky"; static const String byAttribute = "podle příznaku"; + + static const String userPrefersDarkMode = "Preferuji tmavý režim"; } \ No newline at end of file diff --git a/frontend/macos/Flutter/GeneratedPluginRegistrant.swift b/frontend/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..724bb2a 100644 --- a/frontend/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/frontend/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/frontend/pubspec.lock b/frontend/pubspec.lock index 9b40600..ad637dc 100644 --- a/frontend/pubspec.lock +++ b/frontend/pubspec.lock @@ -328,6 +328,62 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf + url: "https://pub.dev" + source: hosted + version: "2.5.5" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53 + url: "https://pub.dev" + source: hosted + version: "2.4.23" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" + url: "https://pub.dev" + source: hosted + version: "2.5.6" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" sky_engine: dependency: transitive description: flutter diff --git a/frontend/pubspec.yaml b/frontend/pubspec.yaml index 962d10f..ece9de9 100644 --- a/frontend/pubspec.yaml +++ b/frontend/pubspec.yaml @@ -38,6 +38,7 @@ dependencies: go_router: http: ^1.2.0 path_provider: + shared_preferences: ^2.1.1 dev_dependencies: flutter_test: