From ddb91307692978fa2b751e8b9def2a6d8c22ef4a Mon Sep 17 00:00:00 2001 From: Juan Gilsanz Polo Date: Wed, 26 Oct 2022 14:26:09 +0200 Subject: [PATCH] Added customization screen --- lib/config/theme.dart | 59 +++--- lib/constants/adguard_green_color.dart | 16 ++ lib/constants/colors.dart | 18 ++ lib/functions/generate_color_translation.dart | 22 +++ lib/l10n/app_en.arb | 17 +- lib/l10n/app_es.arb | 17 +- lib/main.dart | 35 ++-- lib/providers/app_config_provider.dart | 63 ++++++ .../settings/customization/color_item.dart | 46 +++++ .../settings/customization/customization.dart | 182 ++++++++++++++++++ .../customization/theme_mode_button.dart | 66 +++++++ lib/screens/settings/settings.dart | 42 +--- lib/services/database.dart | 26 ++- lib/widgets/custom_switch_list_tile.dart | 2 +- 14 files changed, 518 insertions(+), 93 deletions(-) create mode 100644 lib/constants/adguard_green_color.dart create mode 100644 lib/constants/colors.dart create mode 100644 lib/functions/generate_color_translation.dart create mode 100644 lib/screens/settings/customization/color_item.dart create mode 100644 lib/screens/settings/customization/customization.dart create mode 100644 lib/screens/settings/customization/theme_mode_button.dart diff --git a/lib/config/theme.dart b/lib/config/theme.dart index 9c0e8cc..04bc91c 100644 --- a/lib/config/theme.dart +++ b/lib/config/theme.dart @@ -1,24 +1,11 @@ import 'package:flutter/material.dart'; -Map swatch = { - 50: const Color.fromRGBO(25, 180, 119, .1), - 100: const Color.fromRGBO(25, 180, 119, .2), - 200: const Color.fromRGBO(25, 180, 119, .3), - 300: const Color.fromRGBO(25, 180, 119, .4), - 400: const Color.fromRGBO(25, 180, 119, .5), - 500: const Color.fromRGBO(25, 180, 119, .6), - 600: const Color.fromRGBO(25, 180, 119, .7), - 700: const Color.fromRGBO(25, 180, 119, .8), - 800: const Color.fromRGBO(25, 180, 119, .9), - 900: const Color.fromRGBO(25, 180, 119, 1), -}; - -MaterialColor primaryColor = MaterialColor(0xff19b477, swatch); +import 'package:adguard_home_manager/constants/adguard_green_color.dart'; ThemeData lightTheme(ColorScheme? dynamicColorScheme) => ThemeData( useMaterial3: true, - colorScheme: dynamicColorScheme ?? ColorScheme.fromSwatch(primarySwatch: primaryColor), - primaryColor: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor, + colorScheme: dynamicColorScheme ?? ColorScheme.fromSwatch(primarySwatch: adguardGreenColor), + primaryColor: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor, scaffoldBackgroundColor: dynamicColorScheme != null ? dynamicColorScheme.background : Colors.white, snackBarTheme: SnackBarThemeData( behavior: SnackBarBehavior.floating, @@ -39,15 +26,15 @@ ThemeData lightTheme(ColorScheme? dynamicColorScheme) => ThemeData( ), floatingActionButtonTheme: FloatingActionButtonThemeData( foregroundColor: Colors.white, - backgroundColor: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor + backgroundColor: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor ), textButtonTheme: TextButtonThemeData( style: ButtonStyle( foregroundColor: MaterialStateProperty.all( - dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor + dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor ), overlayColor: MaterialStateProperty.all( - dynamicColorScheme != null ? dynamicColorScheme.primary.withOpacity(0.1) : primaryColor.withOpacity(0.1) + dynamicColorScheme != null ? dynamicColorScheme.primary.withOpacity(0.1) : adguardGreenColor.withOpacity(0.1) ), ), ), @@ -59,22 +46,22 @@ ThemeData lightTheme(ColorScheme? dynamicColorScheme) => ThemeData( checkboxTheme: CheckboxThemeData( checkColor: MaterialStateProperty.all(Colors.white), fillColor: MaterialStateProperty.all( - dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor + dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor ), ), tabBarTheme: TabBarTheme( unselectedLabelColor: Colors.black, - labelColor: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor, + labelColor: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor, indicator: UnderlineTabIndicator( borderSide: BorderSide( - color: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor, + color: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor, width: 2 ) ) ), radioTheme: RadioThemeData( fillColor: MaterialStateProperty.all( - dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor + dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor ), ), androidOverscrollIndicator: AndroidOverscrollIndicator.stretch, @@ -82,14 +69,14 @@ ThemeData lightTheme(ColorScheme? dynamicColorScheme) => ThemeData( ThemeData darkTheme(ColorScheme? dynamicColorScheme) => ThemeData( useMaterial3: true, - colorScheme: dynamicColorScheme ?? ColorScheme.fromSwatch(primarySwatch: primaryColor).copyWith( + colorScheme: dynamicColorScheme ?? ColorScheme.fromSwatch(primarySwatch: adguardGreenColor).copyWith( brightness: Brightness.dark ), - primaryColor: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor, + primaryColor: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor, scaffoldBackgroundColor: dynamicColorScheme != null ? dynamicColorScheme.background :const Color.fromRGBO(18, 18, 18, 1), dialogBackgroundColor: dynamicColorScheme != null ? dynamicColorScheme.background : const Color.fromRGBO(44, 44, 44, 1), navigationBarTheme: NavigationBarThemeData( - indicatorColor: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor, + indicatorColor: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor, ), snackBarTheme: SnackBarThemeData( contentTextStyle: const TextStyle( @@ -103,15 +90,15 @@ ThemeData darkTheme(ColorScheme? dynamicColorScheme) => ThemeData( ), floatingActionButtonTheme: FloatingActionButtonThemeData( foregroundColor: Colors.white, - backgroundColor: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor + backgroundColor: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor ), textButtonTheme: TextButtonThemeData( style: ButtonStyle( foregroundColor: MaterialStateProperty.all( - dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor + dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor ), overlayColor: MaterialStateProperty.all( - dynamicColorScheme != null ? dynamicColorScheme.primary.withOpacity(0.1) : primaryColor.withOpacity(0.1) + dynamicColorScheme != null ? dynamicColorScheme.primary.withOpacity(0.1) : adguardGreenColor.withOpacity(0.1) ), ), ), @@ -132,28 +119,28 @@ ThemeData darkTheme(ColorScheme? dynamicColorScheme) => ThemeData( checkboxTheme: CheckboxThemeData( checkColor: MaterialStateProperty.all(Colors.white), fillColor: MaterialStateProperty.all( - dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor + dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor ), ), tabBarTheme: TabBarTheme( unselectedLabelColor: Colors.white, - labelColor: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor, + labelColor: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor, indicator: UnderlineTabIndicator( borderSide: BorderSide( - color: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor, + color: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor, width: 2 ) ) ), radioTheme: RadioThemeData( fillColor: MaterialStateProperty.all( - dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor + dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor ), ), androidOverscrollIndicator: AndroidOverscrollIndicator.stretch ); -ThemeData lightThemeOldVersions() => ThemeData( +ThemeData lightThemeOldVersions(MaterialColor primaryColor) => ThemeData( useMaterial3: true, primaryColor: primaryColor, appBarTheme: AppBarTheme( @@ -246,7 +233,7 @@ ThemeData lightThemeOldVersions() => ThemeData( androidOverscrollIndicator: AndroidOverscrollIndicator.stretch, ); -ThemeData darkThemeOldVersions() => ThemeData( +ThemeData darkThemeOldVersions(MaterialColor primaryColor) => ThemeData( useMaterial3: true, primaryColor: primaryColor, scaffoldBackgroundColor: const Color.fromRGBO(18, 18, 18, 1), @@ -260,7 +247,7 @@ ThemeData darkThemeOldVersions() => ThemeData( ) ), appBarTheme: AppBarTheme( - color: const Color.fromRGBO(18, 18, 18, 1), + color: Color.fromRGBO(18, 18, 18, 1), foregroundColor: Colors.white, elevation: 0, surfaceTintColor: primaryColor diff --git a/lib/constants/adguard_green_color.dart b/lib/constants/adguard_green_color.dart new file mode 100644 index 0000000..2ee7aa5 --- /dev/null +++ b/lib/constants/adguard_green_color.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +const Map swatch = { + 50: Color.fromRGBO(25, 180, 119, .1), + 100: Color.fromRGBO(25, 180, 119, .2), + 200: Color.fromRGBO(25, 180, 119, .3), + 300: Color.fromRGBO(25, 180, 119, .4), + 400: Color.fromRGBO(25, 180, 119, .5), + 500: Color.fromRGBO(25, 180, 119, .6), + 600: Color.fromRGBO(25, 180, 119, .7), + 700: Color.fromRGBO(25, 180, 119, .8), + 800: Color.fromRGBO(25, 180, 119, .9), + 900: Color.fromRGBO(25, 180, 119, 1), +}; + +const MaterialColor adguardGreenColor = MaterialColor(0xff19b477, swatch); \ No newline at end of file diff --git a/lib/constants/colors.dart b/lib/constants/colors.dart new file mode 100644 index 0000000..082e258 --- /dev/null +++ b/lib/constants/colors.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +import 'package:adguard_home_manager/constants/adguard_green_color.dart'; + +const List colors = [ + adguardGreenColor, + Colors.red, + Colors.green, + Colors.blue, + Colors.yellow, + Colors.orange, + Colors.brown, + Colors.cyan, + Colors.purple, + Colors.pink, + Colors.deepOrange, + Colors.indigo +]; \ No newline at end of file diff --git a/lib/functions/generate_color_translation.dart b/lib/functions/generate_color_translation.dart new file mode 100644 index 0000000..86ac12c --- /dev/null +++ b/lib/functions/generate_color_translation.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + + +String colorTranslation(BuildContext context, int index) { + // This indexes has to be in sync with colors array at constants/colors.dart + List translations = [ + "AdGuard", + AppLocalizations.of(context)!.red, + AppLocalizations.of(context)!.green, + AppLocalizations.of(context)!.blue, + AppLocalizations.of(context)!.yellow, + AppLocalizations.of(context)!.orange, + AppLocalizations.of(context)!.brown, + AppLocalizations.of(context)!.cyan, + AppLocalizations.of(context)!.purple, + AppLocalizations.of(context)!.pink, + AppLocalizations.of(context)!.deepOrange, + AppLocalizations.of(context)!.indigo, + ]; + return translations[index]; +} \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 21a73bc..656cc11 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -530,5 +530,20 @@ "clearSearch": "Clear search", "exitSearch": "Exit search", "searchClients": "Search clients", - "noClientsSearch": "No clients with that search." + "noClientsSearch": "No clients with that search.", + "customization": "Customization", + "customizationDescription": "Customize this application", + "color": "Color", + "useDynamicTheme": "Use dynamic theme", + "red": "Red", + "green": "Green", + "blue": "Blue", + "yellow": "Yellow", + "orange": "Orange", + "brown": "Brown", + "cyan": "Cyan", + "purple": "Purple", + "pink": "Pink", + "deepOrange": "Deep orange", + "indigo": "Indigo" } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 6b954d9..7dc613b 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -530,5 +530,20 @@ "clearSearch": "Limpiar búsqueda", "exitSearch": "Salir de la búsqueda", "searchClients": "Buscar clientes", - "noClientsSearch": "No hay clientes con esa búsqueda." + "noClientsSearch": "No hay clientes con esa búsqueda.", + "customization": "Personalización", + "customizationDescription": "Personaliza esta aplicación", + "color": "Color", + "useDynamicTheme": "Usar tema dinámico", + "red": "Rojo", + "green": "Verde", + "blue": "Azul", + "yellow": "Amarillo", + "orange": "Naranja", + "brown": "Marrón", + "cyan": "Cyan", + "purple": "Morado", + "pink": "Rosa", + "deepOrange": "Naranja oscuro", + "indigo": "Índigo" } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 32e874f..ca4db02 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,6 +14,7 @@ import 'package:adguard_home_manager/base.dart'; import 'package:adguard_home_manager/classes/http_override.dart'; import 'package:adguard_home_manager/services/database.dart'; +import 'package:adguard_home_manager/constants/colors.dart'; import 'package:adguard_home_manager/providers/logs_provider.dart'; import 'package:adguard_home_manager/providers/app_config_provider.dart'; import 'package:adguard_home_manager/providers/servers_provider.dart'; @@ -30,7 +31,17 @@ void main() async { ServersProvider serversProvider = ServersProvider(); LogsProvider logsProvider = LogsProvider(); - final dbData = await loadDb(); + DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + if (Platform.isAndroid) { + final androidInfo = await deviceInfo.androidInfo; + appConfigProvider.setAndroidInfo(androidInfo); + } + if (Platform.isIOS) { + final iosInfo = await deviceInfo.iosInfo; + appConfigProvider.setIosInfo(iosInfo); + } + + final dbData = await loadDb(appConfigProvider.androidDeviceInfo != null && appConfigProvider.androidDeviceInfo!.version.sdkInt! >= 31); if (dbData['appConfig']['overrideSslCheck'] == 1) { HttpOverrides.global = MyHttpOverrides(); @@ -43,16 +54,6 @@ void main() async { PackageInfo appInfo = await PackageInfo.fromPlatform(); appConfigProvider.setAppInfo(appInfo); - DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); - if (Platform.isAndroid) { - final androidInfo = await deviceInfo.androidInfo; - appConfigProvider.setAndroidInfo(androidInfo); - } - if (Platform.isIOS) { - final iosInfo = await deviceInfo.iosInfo; - appConfigProvider.setIosInfo(iosInfo); - } - runApp( MultiProvider( providers: [ @@ -103,11 +104,15 @@ class _MainState extends State
{ builder: (lightDynamic, darkDynamic) => MaterialApp( title: 'AdGuard Home Manager', theme: appConfigProvider.androidDeviceInfo != null && appConfigProvider.androidDeviceInfo!.version.sdkInt! >= 31 - ? lightTheme(lightDynamic) - : lightThemeOldVersions(), + ? appConfigProvider.useDynamicColor == true + ? lightTheme(lightDynamic) + : lightThemeOldVersions(colors[appConfigProvider.staticColor]) + : lightThemeOldVersions(colors[appConfigProvider.staticColor]), darkTheme: appConfigProvider.androidDeviceInfo != null && appConfigProvider.androidDeviceInfo!.version.sdkInt! >= 31 - ? darkTheme(darkDynamic) - : darkThemeOldVersions(), + ? appConfigProvider.useDynamicColor == true + ? darkTheme(darkDynamic) + : darkThemeOldVersions(colors[appConfigProvider.staticColor]) + : darkThemeOldVersions(colors[appConfigProvider.staticColor]), themeMode: appConfigProvider.selectedTheme, debugShowCheckedModeBanner: false, localizationsDelegates: const [ diff --git a/lib/providers/app_config_provider.dart b/lib/providers/app_config_provider.dart index 1f4fb4a..766c70d 100644 --- a/lib/providers/app_config_provider.dart +++ b/lib/providers/app_config_provider.dart @@ -4,6 +4,7 @@ import 'package:flutter/scheduler.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:sqflite/sqlite_api.dart'; +import 'package:adguard_home_manager/functions/conversions.dart'; import 'package:adguard_home_manager/models/app_log.dart'; class AppConfigProvider with ChangeNotifier { @@ -18,6 +19,8 @@ class AppConfigProvider with ChangeNotifier { bool _showingSnackbar = false; int _selectedTheme = 0; + bool _useDynamicColor = true; + int _staticColor = 0; int _selectedClientsTab = 0; int _selectedFiltersTab = 0; @@ -90,6 +93,14 @@ class AppConfigProvider with ChangeNotifier { return _showingSnackbar; } + bool get useDynamicColor { + return _useDynamicColor; + } + + int get staticColor { + return _staticColor; + } + void setDbInstance(Database db) { _dbInstance = db; } @@ -167,6 +178,30 @@ class AppConfigProvider with ChangeNotifier { } } + Future setUseDynamicColor(bool value) async { + final updated = await _updateDynamicColorDb(value == true ? 1 : 0); + if (updated == true) { + _useDynamicColor = value; + notifyListeners(); + return true; + } + else { + return false; + } + } + + Future setStaticColor(int value) async { + final updated = await _updateStaticColorDb(value); + if (updated == true) { + _staticColor = value; + notifyListeners(); + return true; + } + else { + return false; + } + } + Future _updateThemeDb(int value) async { try { return await _dbInstance!.transaction((txn) async { @@ -180,6 +215,32 @@ class AppConfigProvider with ChangeNotifier { } } + Future _updateDynamicColorDb(int value) async { + try { + return await _dbInstance!.transaction((txn) async { + await txn.rawUpdate( + 'UPDATE appConfig SET useDynamicColor = $value', + ); + return true; + }); + } catch (e) { + return false; + } + } + + Future _updateStaticColorDb(int value) async { + try { + return await _dbInstance!.transaction((txn) async { + await txn.rawUpdate( + 'UPDATE appConfig SET staticColor = $value', + ); + return true; + }); + } catch (e) { + return false; + } + } + Future _updateOverrideSslCheck(int value) async { try { return await _dbInstance!.transaction((txn) async { @@ -210,6 +271,8 @@ class AppConfigProvider with ChangeNotifier { _selectedTheme = dbData['theme']; _overrideSslCheck = dbData['overrideSslCheck']; _hideZeroValues = dbData['hideZeroValues']; + _useDynamicColor = convertFromIntToBool(dbData['useDynamicColor'])!; + _staticColor = dbData['staticColor']; _dbInstance = dbInstance; notifyListeners(); diff --git a/lib/screens/settings/customization/color_item.dart b/lib/screens/settings/customization/color_item.dart new file mode 100644 index 0000000..65e8e58 --- /dev/null +++ b/lib/screens/settings/customization/color_item.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +class ColorItem extends StatelessWidget { + final Color color; + final int numericValue; + final int? selectedValue; + final void Function(int) onChanged; + + const ColorItem({ + Key? key, + required this.color, + required this.numericValue, + required this.selectedValue, + required this.onChanged + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(10), + child: Material( + borderRadius: BorderRadius.circular(50), + child: InkWell( + onTap: () => onChanged(numericValue), + borderRadius: BorderRadius.circular(50), + overlayColor: const MaterialStatePropertyAll(Colors.grey), + child: Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: color.withOpacity(0.6), + borderRadius: BorderRadius.circular(50) + ), + child: selectedValue != null && selectedValue == numericValue + ? Icon( + Icons.check, + size: 30, + color: color.computeLuminance() > 0.5 ? Colors.black : Colors.white, + ) + : null, + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/settings/customization/customization.dart b/lib/screens/settings/customization/customization.dart new file mode 100644 index 0000000..d8c35b7 --- /dev/null +++ b/lib/screens/settings/customization/customization.dart @@ -0,0 +1,182 @@ +import 'package:adguard_home_manager/functions/generate_color_translation.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import 'package:adguard_home_manager/screens/settings/customization/color_item.dart'; +import 'package:adguard_home_manager/screens/settings/customization/theme_mode_button.dart'; + +import 'package:adguard_home_manager/widgets/custom_switch_list_tile.dart'; +import 'package:adguard_home_manager/widgets/section_label.dart'; + +import 'package:adguard_home_manager/providers/app_config_provider.dart'; +import 'package:adguard_home_manager/constants/colors.dart'; + +class Customization extends StatelessWidget { + const Customization({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final appConfigProvider = Provider.of(context); + + return CustomizationWidget( + appConfigProvider: appConfigProvider + ); + } +} + +class CustomizationWidget extends StatefulWidget { + final AppConfigProvider appConfigProvider; + + const CustomizationWidget({ + Key? key, + required this.appConfigProvider, + }) : super(key: key); + + @override + State createState() => _CustomizationWidgetState(); +} + +class _CustomizationWidgetState extends State { + int selectedTheme = 0; + bool dynamicColor = true; + int selectedColor = 0; + + @override + void initState() { + selectedTheme = widget.appConfigProvider.selectedTheme == ThemeMode.light ? 1 : 2; + dynamicColor = widget.appConfigProvider.useDynamicColor; + selectedColor = widget.appConfigProvider.staticColor; + super.initState(); + } + + @override + Widget build(BuildContext context) { + final appConfigProvider = Provider.of(context); + + return Scaffold( + appBar: AppBar( + title: Text(AppLocalizations.of(context)!.customization), + ), + body: ListView( + children: [ + SectionLabel(label: AppLocalizations.of(context)!.theme), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ThemeModeButton( + icon: Icons.light_mode, + value: 1, + selected: selectedTheme, + label: AppLocalizations.of(context)!.light, + onChanged: (value) { + selectedTheme = value; + appConfigProvider.setSelectedTheme(value); + } + ), + ThemeModeButton( + icon: Icons.dark_mode, + value: 2, + selected: selectedTheme, + label: AppLocalizations.of(context)!.dark, + onChanged: (value) { + selectedTheme = value; + appConfigProvider.setSelectedTheme(value); + } + ), + ], + ), + SectionLabel( + label: AppLocalizations.of(context)!.color, + padding: const EdgeInsets.only(top: 45, left: 25, right: 25, bottom: 5), + ), + if (appConfigProvider.androidDeviceInfo != null && appConfigProvider.androidDeviceInfo!.version.sdkInt! >= 31) CustomSwitchListTile( + value: dynamicColor, + onChanged: (value) { + setState(() => dynamicColor = value); + appConfigProvider.setUseDynamicColor(value); + }, + title: AppLocalizations.of(context)!.useDynamicTheme, + ), + if (dynamicColor == false) ...[ + SizedBox( + width: MediaQuery.of(context).size.width, + height: 70, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: colors.length, + itemBuilder: (context, index) { + if (index == 0) { + return Row( + children: [ + const SizedBox(width: 15), + ColorItem( + color: colors[index], + numericValue: index, + selectedValue: selectedColor, + onChanged: (value) { + setState(() => selectedColor = value); + appConfigProvider.setStaticColor(value); + } + ), + Container( + margin: const EdgeInsets.symmetric(horizontal: 10), + width: 1, + height: 60, + decoration: BoxDecoration( + color: Colors.grey, + borderRadius: BorderRadius.circular(1) + ), + ) + ], + ); + } + else if (index == colors.length-1) { + return Row( + children: [ + ColorItem( + color: colors[index], + numericValue: index, + selectedValue: selectedColor, + onChanged: (value) { + setState(() => selectedColor = value); + appConfigProvider.setStaticColor(value); + } + ), + const SizedBox(width: 15) + ], + ); + } + else { + return ColorItem( + color: colors[index], + numericValue: index, + selectedValue: selectedColor, + onChanged: (value) { + setState(() => selectedColor = value); + appConfigProvider.setStaticColor(value); + } + ); + } + }, + ), + ), + Padding( + padding: const EdgeInsets.only( + left: 25, + top: 10 + ), + child: Text( + colorTranslation(context, selectedColor!), + style: TextStyle( + color: Theme.of(context).listTileTheme.iconColor, + fontSize: 16 + ), + ), + ) + ] + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/settings/customization/theme_mode_button.dart b/lib/screens/settings/customization/theme_mode_button.dart new file mode 100644 index 0000000..74b1a28 --- /dev/null +++ b/lib/screens/settings/customization/theme_mode_button.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; + +class ThemeModeButton extends StatelessWidget { + final IconData icon; + final int value; + final int selected; + final String label; + final void Function(int) onChanged; + + const ThemeModeButton({ + Key? key, + required this.icon, + required this.value, + required this.selected, + required this.label, + required this.onChanged, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Material( + color: Colors.transparent, + borderRadius: BorderRadius.circular(16), + child: InkWell( + onTap: () => onChanged(value), + borderRadius: BorderRadius.circular(16), + child: AnimatedContainer( + padding: const EdgeInsets.all(10), + width: 150, + height: 150, + duration: const Duration(milliseconds: 200), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + color: value == selected + ? Theme.of(context).primaryColor + : Theme.of(context).primaryColor.withOpacity(0.1), + border: Border.all( + color: Theme.of(context).primaryColor + ) + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Icon( + icon, + color: value == selected + ? Theme.of(context).primaryColor.computeLuminance() > 0.5 ? Colors.black : Colors.white + : null, + size: 30, + ), + Text( + label, + style: TextStyle( + color: value == selected + ? Theme.of(context).primaryColor.computeLuminance() > 0.5 ? Colors.black : Colors.white + : null, + fontSize: 18 + ), + ) + ], + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/settings/settings.dart b/lib/screens/settings/settings.dart index a7efc45..95bea38 100644 --- a/lib/screens/settings/settings.dart +++ b/lib/screens/settings/settings.dart @@ -4,10 +4,10 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_web_browser/flutter_web_browser.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:adguard_home_manager/screens/settings/theme_modal.dart'; import 'package:adguard_home_manager/screens/settings/server_info/server_info.dart'; import 'package:adguard_home_manager/screens/settings/encryption/encryption.dart'; import 'package:adguard_home_manager/screens/settings/access_settings/access_settings.dart'; +import 'package:adguard_home_manager/screens/settings/customization/customization.dart'; import 'package:adguard_home_manager/screens/settings/dhcp/dhcp.dart'; import 'package:adguard_home_manager/widgets/section_label.dart'; import 'package:adguard_home_manager/screens/settings/dns/dns.dart'; @@ -32,36 +32,6 @@ class Settings extends StatelessWidget { final appConfigProvider = Provider.of(context); final serversProvider = Provider.of(context); - final statusBarHeight = MediaQuery.of(context).viewInsets.top; - - String getThemeString() { - switch (appConfigProvider.selectedThemeNumber) { - case 0: - return AppLocalizations.of(context)!.systemDefined; - - case 1: - return AppLocalizations.of(context)!.light; - - case 2: - return AppLocalizations.of(context)!.dark; - - default: - return ""; - } - } - - void openThemeModal() { - showModalBottomSheet( - context: context, - isScrollControlled: true, - builder: (context) => ThemeModal( - statusBarHeight: statusBarHeight, - selectedTheme: appConfigProvider.selectedThemeNumber, - ), - backgroundColor: Colors.transparent, - ); - } - void navigateServers() { Future.delayed(const Duration(milliseconds: 0), (() { Navigator.of(context).push( @@ -167,10 +137,12 @@ class Settings extends StatelessWidget { ], SectionLabel(label: AppLocalizations.of(context)!.appSettings), CustomListTile( - icon: Icons.light_mode_rounded, - title: AppLocalizations.of(context)!.theme, - subtitle: getThemeString(), - onTap: openThemeModal, + icon: Icons.palette_rounded, + title: AppLocalizations.of(context)!.customization, + subtitle: AppLocalizations.of(context)!.customizationDescription, + onTap: () => Navigator.push(context, MaterialPageRoute( + builder: (context) => const Customization() + )) ), CustomListTile( icon: Icons.storage_rounded, diff --git a/lib/services/database.dart b/lib/services/database.dart index 1cafa1d..ad8f523 100644 --- a/lib/services/database.dart +++ b/lib/services/database.dart @@ -1,6 +1,6 @@ import 'package:sqflite/sqflite.dart'; -Future> loadDb() async { +Future> loadDb(bool acceptsDynamicTheme) async { List>? servers; List>? appConfig; @@ -37,26 +37,44 @@ Future> loadDb() async { }); } + Future upgradeDbToV5(Database db) async { + await db.execute("ALTER TABLE appConfig ADD COLUMN useDynamicColor NUMERIC"); + await db.execute("ALTER TABLE appConfig ADD COLUMN staticColor NUMERIC"); + await db.execute("UPDATE appConfig SET useDynamicColor = ${acceptsDynamicTheme == true ? 1 : 0}, staticColor = 0"); + + await db.transaction((txn) async{ + await txn.rawQuery( + 'SELECT * FROM appConfig', + ); + }); + } + Database db = await openDatabase( 'adguard_home_manager.db', - version: 4, + version: 5, onCreate: (Database db, int version) async { await db.execute("CREATE TABLE servers (id TEXT PRIMARY KEY, name TEXT, connectionMethod TEXT, domain TEXT, path TEXT, port INTEGER, user TEXT, password TEXT, defaultServer INTEGER, authToken TEXT, runningOnHa INTEGER)"); - await db.execute("CREATE TABLE appConfig (theme NUMERIC, overrideSslCheck NUMERIC, hideZeroValues NUMERIC)"); - await db.execute("INSERT INTO appConfig (theme, overrideSslCheck, hideZeroValues) VALUES (0, 0, 0)"); + await db.execute("CREATE TABLE appConfig (theme NUMERIC, overrideSslCheck NUMERIC, hideZeroValues NUMERIC, useDynamicColor NUMERIC, staticColor NUMERIC)"); + await db.execute("INSERT INTO appConfig (theme, overrideSslCheck, hideZeroValues, useDynamicColor, staticColor) VALUES (0, 0, 0, ${acceptsDynamicTheme == true ? 1 : 0}, 0)"); }, onUpgrade: (Database db, int oldVersion, int newVersion) async { if (oldVersion == 1) { await upgradeDbToV2(db); await upgradeDbToV3(db); await upgradeDbToV4(db); + await upgradeDbToV5(db); } if (oldVersion == 2) { await upgradeDbToV3(db); await upgradeDbToV4(db); + await upgradeDbToV5(db); } if (oldVersion == 3) { await upgradeDbToV4(db); + await upgradeDbToV5(db); + } + if (oldVersion == 4) { + await upgradeDbToV5(db); } }, onOpen: (Database db) async { diff --git a/lib/widgets/custom_switch_list_tile.dart b/lib/widgets/custom_switch_list_tile.dart index 3dce6f1..d612af3 100644 --- a/lib/widgets/custom_switch_list_tile.dart +++ b/lib/widgets/custom_switch_list_tile.dart @@ -26,7 +26,7 @@ class CustomSwitchListTile extends StatelessWidget { : () => onChanged(!value), child: Padding( padding: const EdgeInsets.only( - top: 20, left: 24, right: 10, bottom: 20 + top: 15, left: 24, right: 15, bottom: 15 ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,