Added customization screen

This commit is contained in:
Juan Gilsanz Polo 2022-10-26 14:26:09 +02:00
parent f1a837dc42
commit ddb9130769
14 changed files with 518 additions and 93 deletions

View file

@ -1,24 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
Map<int, Color> swatch = { import 'package:adguard_home_manager/constants/adguard_green_color.dart';
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);
ThemeData lightTheme(ColorScheme? dynamicColorScheme) => ThemeData( ThemeData lightTheme(ColorScheme? dynamicColorScheme) => ThemeData(
useMaterial3: true, useMaterial3: true,
colorScheme: dynamicColorScheme ?? ColorScheme.fromSwatch(primarySwatch: primaryColor), colorScheme: dynamicColorScheme ?? ColorScheme.fromSwatch(primarySwatch: adguardGreenColor),
primaryColor: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor, primaryColor: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor,
scaffoldBackgroundColor: dynamicColorScheme != null ? dynamicColorScheme.background : Colors.white, scaffoldBackgroundColor: dynamicColorScheme != null ? dynamicColorScheme.background : Colors.white,
snackBarTheme: SnackBarThemeData( snackBarTheme: SnackBarThemeData(
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
@ -39,15 +26,15 @@ ThemeData lightTheme(ColorScheme? dynamicColorScheme) => ThemeData(
), ),
floatingActionButtonTheme: FloatingActionButtonThemeData( floatingActionButtonTheme: FloatingActionButtonThemeData(
foregroundColor: Colors.white, foregroundColor: Colors.white,
backgroundColor: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor backgroundColor: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor
), ),
textButtonTheme: TextButtonThemeData( textButtonTheme: TextButtonThemeData(
style: ButtonStyle( style: ButtonStyle(
foregroundColor: MaterialStateProperty.all( foregroundColor: MaterialStateProperty.all(
dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor
), ),
overlayColor: MaterialStateProperty.all( 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( checkboxTheme: CheckboxThemeData(
checkColor: MaterialStateProperty.all(Colors.white), checkColor: MaterialStateProperty.all(Colors.white),
fillColor: MaterialStateProperty.all( fillColor: MaterialStateProperty.all(
dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor
), ),
), ),
tabBarTheme: TabBarTheme( tabBarTheme: TabBarTheme(
unselectedLabelColor: Colors.black, unselectedLabelColor: Colors.black,
labelColor: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor, labelColor: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor,
indicator: UnderlineTabIndicator( indicator: UnderlineTabIndicator(
borderSide: BorderSide( borderSide: BorderSide(
color: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor, color: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor,
width: 2 width: 2
) )
) )
), ),
radioTheme: RadioThemeData( radioTheme: RadioThemeData(
fillColor: MaterialStateProperty.all( fillColor: MaterialStateProperty.all(
dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor
), ),
), ),
androidOverscrollIndicator: AndroidOverscrollIndicator.stretch, androidOverscrollIndicator: AndroidOverscrollIndicator.stretch,
@ -82,14 +69,14 @@ ThemeData lightTheme(ColorScheme? dynamicColorScheme) => ThemeData(
ThemeData darkTheme(ColorScheme? dynamicColorScheme) => ThemeData( ThemeData darkTheme(ColorScheme? dynamicColorScheme) => ThemeData(
useMaterial3: true, useMaterial3: true,
colorScheme: dynamicColorScheme ?? ColorScheme.fromSwatch(primarySwatch: primaryColor).copyWith( colorScheme: dynamicColorScheme ?? ColorScheme.fromSwatch(primarySwatch: adguardGreenColor).copyWith(
brightness: Brightness.dark 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), scaffoldBackgroundColor: dynamicColorScheme != null ? dynamicColorScheme.background :const Color.fromRGBO(18, 18, 18, 1),
dialogBackgroundColor: dynamicColorScheme != null ? dynamicColorScheme.background : const Color.fromRGBO(44, 44, 44, 1), dialogBackgroundColor: dynamicColorScheme != null ? dynamicColorScheme.background : const Color.fromRGBO(44, 44, 44, 1),
navigationBarTheme: NavigationBarThemeData( navigationBarTheme: NavigationBarThemeData(
indicatorColor: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor, indicatorColor: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor,
), ),
snackBarTheme: SnackBarThemeData( snackBarTheme: SnackBarThemeData(
contentTextStyle: const TextStyle( contentTextStyle: const TextStyle(
@ -103,15 +90,15 @@ ThemeData darkTheme(ColorScheme? dynamicColorScheme) => ThemeData(
), ),
floatingActionButtonTheme: FloatingActionButtonThemeData( floatingActionButtonTheme: FloatingActionButtonThemeData(
foregroundColor: Colors.white, foregroundColor: Colors.white,
backgroundColor: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor backgroundColor: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor
), ),
textButtonTheme: TextButtonThemeData( textButtonTheme: TextButtonThemeData(
style: ButtonStyle( style: ButtonStyle(
foregroundColor: MaterialStateProperty.all( foregroundColor: MaterialStateProperty.all(
dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor
), ),
overlayColor: MaterialStateProperty.all( 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( checkboxTheme: CheckboxThemeData(
checkColor: MaterialStateProperty.all(Colors.white), checkColor: MaterialStateProperty.all(Colors.white),
fillColor: MaterialStateProperty.all( fillColor: MaterialStateProperty.all(
dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor
), ),
), ),
tabBarTheme: TabBarTheme( tabBarTheme: TabBarTheme(
unselectedLabelColor: Colors.white, unselectedLabelColor: Colors.white,
labelColor: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor, labelColor: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor,
indicator: UnderlineTabIndicator( indicator: UnderlineTabIndicator(
borderSide: BorderSide( borderSide: BorderSide(
color: dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor, color: dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor,
width: 2 width: 2
) )
) )
), ),
radioTheme: RadioThemeData( radioTheme: RadioThemeData(
fillColor: MaterialStateProperty.all( fillColor: MaterialStateProperty.all(
dynamicColorScheme != null ? dynamicColorScheme.primary : primaryColor dynamicColorScheme != null ? dynamicColorScheme.primary : adguardGreenColor
), ),
), ),
androidOverscrollIndicator: AndroidOverscrollIndicator.stretch androidOverscrollIndicator: AndroidOverscrollIndicator.stretch
); );
ThemeData lightThemeOldVersions() => ThemeData( ThemeData lightThemeOldVersions(MaterialColor primaryColor) => ThemeData(
useMaterial3: true, useMaterial3: true,
primaryColor: primaryColor, primaryColor: primaryColor,
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
@ -246,7 +233,7 @@ ThemeData lightThemeOldVersions() => ThemeData(
androidOverscrollIndicator: AndroidOverscrollIndicator.stretch, androidOverscrollIndicator: AndroidOverscrollIndicator.stretch,
); );
ThemeData darkThemeOldVersions() => ThemeData( ThemeData darkThemeOldVersions(MaterialColor primaryColor) => ThemeData(
useMaterial3: true, useMaterial3: true,
primaryColor: primaryColor, primaryColor: primaryColor,
scaffoldBackgroundColor: const Color.fromRGBO(18, 18, 18, 1), scaffoldBackgroundColor: const Color.fromRGBO(18, 18, 18, 1),
@ -260,7 +247,7 @@ ThemeData darkThemeOldVersions() => ThemeData(
) )
), ),
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
color: const Color.fromRGBO(18, 18, 18, 1), color: Color.fromRGBO(18, 18, 18, 1),
foregroundColor: Colors.white, foregroundColor: Colors.white,
elevation: 0, elevation: 0,
surfaceTintColor: primaryColor surfaceTintColor: primaryColor

View file

@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
const Map<int, Color> 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);

18
lib/constants/colors.dart Normal file
View file

@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
import 'package:adguard_home_manager/constants/adguard_green_color.dart';
const List<MaterialColor> colors = [
adguardGreenColor,
Colors.red,
Colors.green,
Colors.blue,
Colors.yellow,
Colors.orange,
Colors.brown,
Colors.cyan,
Colors.purple,
Colors.pink,
Colors.deepOrange,
Colors.indigo
];

View file

@ -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<String> 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];
}

View file

@ -530,5 +530,20 @@
"clearSearch": "Clear search", "clearSearch": "Clear search",
"exitSearch": "Exit search", "exitSearch": "Exit search",
"searchClients": "Search clients", "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"
} }

View file

@ -530,5 +530,20 @@
"clearSearch": "Limpiar búsqueda", "clearSearch": "Limpiar búsqueda",
"exitSearch": "Salir de la búsqueda", "exitSearch": "Salir de la búsqueda",
"searchClients": "Buscar clientes", "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"
} }

View file

@ -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/classes/http_override.dart';
import 'package:adguard_home_manager/services/database.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/logs_provider.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart'; import 'package:adguard_home_manager/providers/app_config_provider.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart'; import 'package:adguard_home_manager/providers/servers_provider.dart';
@ -30,7 +31,17 @@ void main() async {
ServersProvider serversProvider = ServersProvider(); ServersProvider serversProvider = ServersProvider();
LogsProvider logsProvider = LogsProvider(); 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) { if (dbData['appConfig']['overrideSslCheck'] == 1) {
HttpOverrides.global = MyHttpOverrides(); HttpOverrides.global = MyHttpOverrides();
@ -43,16 +54,6 @@ void main() async {
PackageInfo appInfo = await PackageInfo.fromPlatform(); PackageInfo appInfo = await PackageInfo.fromPlatform();
appConfigProvider.setAppInfo(appInfo); 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( runApp(
MultiProvider( MultiProvider(
providers: [ providers: [
@ -103,11 +104,15 @@ class _MainState extends State<Main> {
builder: (lightDynamic, darkDynamic) => MaterialApp( builder: (lightDynamic, darkDynamic) => MaterialApp(
title: 'AdGuard Home Manager', title: 'AdGuard Home Manager',
theme: appConfigProvider.androidDeviceInfo != null && appConfigProvider.androidDeviceInfo!.version.sdkInt! >= 31 theme: appConfigProvider.androidDeviceInfo != null && appConfigProvider.androidDeviceInfo!.version.sdkInt! >= 31
? appConfigProvider.useDynamicColor == true
? lightTheme(lightDynamic) ? lightTheme(lightDynamic)
: lightThemeOldVersions(), : lightThemeOldVersions(colors[appConfigProvider.staticColor])
: lightThemeOldVersions(colors[appConfigProvider.staticColor]),
darkTheme: appConfigProvider.androidDeviceInfo != null && appConfigProvider.androidDeviceInfo!.version.sdkInt! >= 31 darkTheme: appConfigProvider.androidDeviceInfo != null && appConfigProvider.androidDeviceInfo!.version.sdkInt! >= 31
? appConfigProvider.useDynamicColor == true
? darkTheme(darkDynamic) ? darkTheme(darkDynamic)
: darkThemeOldVersions(), : darkThemeOldVersions(colors[appConfigProvider.staticColor])
: darkThemeOldVersions(colors[appConfigProvider.staticColor]),
themeMode: appConfigProvider.selectedTheme, themeMode: appConfigProvider.selectedTheme,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
localizationsDelegates: const [ localizationsDelegates: const [

View file

@ -4,6 +4,7 @@ import 'package:flutter/scheduler.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:sqflite/sqlite_api.dart'; import 'package:sqflite/sqlite_api.dart';
import 'package:adguard_home_manager/functions/conversions.dart';
import 'package:adguard_home_manager/models/app_log.dart'; import 'package:adguard_home_manager/models/app_log.dart';
class AppConfigProvider with ChangeNotifier { class AppConfigProvider with ChangeNotifier {
@ -18,6 +19,8 @@ class AppConfigProvider with ChangeNotifier {
bool _showingSnackbar = false; bool _showingSnackbar = false;
int _selectedTheme = 0; int _selectedTheme = 0;
bool _useDynamicColor = true;
int _staticColor = 0;
int _selectedClientsTab = 0; int _selectedClientsTab = 0;
int _selectedFiltersTab = 0; int _selectedFiltersTab = 0;
@ -90,6 +93,14 @@ class AppConfigProvider with ChangeNotifier {
return _showingSnackbar; return _showingSnackbar;
} }
bool get useDynamicColor {
return _useDynamicColor;
}
int get staticColor {
return _staticColor;
}
void setDbInstance(Database db) { void setDbInstance(Database db) {
_dbInstance = db; _dbInstance = db;
} }
@ -167,6 +178,30 @@ class AppConfigProvider with ChangeNotifier {
} }
} }
Future<bool> setUseDynamicColor(bool value) async {
final updated = await _updateDynamicColorDb(value == true ? 1 : 0);
if (updated == true) {
_useDynamicColor = value;
notifyListeners();
return true;
}
else {
return false;
}
}
Future<bool> setStaticColor(int value) async {
final updated = await _updateStaticColorDb(value);
if (updated == true) {
_staticColor = value;
notifyListeners();
return true;
}
else {
return false;
}
}
Future<bool> _updateThemeDb(int value) async { Future<bool> _updateThemeDb(int value) async {
try { try {
return await _dbInstance!.transaction((txn) async { return await _dbInstance!.transaction((txn) async {
@ -180,6 +215,32 @@ class AppConfigProvider with ChangeNotifier {
} }
} }
Future<bool> _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<bool> _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<bool> _updateOverrideSslCheck(int value) async { Future<bool> _updateOverrideSslCheck(int value) async {
try { try {
return await _dbInstance!.transaction((txn) async { return await _dbInstance!.transaction((txn) async {
@ -210,6 +271,8 @@ class AppConfigProvider with ChangeNotifier {
_selectedTheme = dbData['theme']; _selectedTheme = dbData['theme'];
_overrideSslCheck = dbData['overrideSslCheck']; _overrideSslCheck = dbData['overrideSslCheck'];
_hideZeroValues = dbData['hideZeroValues']; _hideZeroValues = dbData['hideZeroValues'];
_useDynamicColor = convertFromIntToBool(dbData['useDynamicColor'])!;
_staticColor = dbData['staticColor'];
_dbInstance = dbInstance; _dbInstance = dbInstance;
notifyListeners(); notifyListeners();

View file

@ -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,
),
),
),
);
}
}

View file

@ -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<AppConfigProvider>(context);
return CustomizationWidget(
appConfigProvider: appConfigProvider
);
}
}
class CustomizationWidget extends StatefulWidget {
final AppConfigProvider appConfigProvider;
const CustomizationWidget({
Key? key,
required this.appConfigProvider,
}) : super(key: key);
@override
State<CustomizationWidget> createState() => _CustomizationWidgetState();
}
class _CustomizationWidgetState extends State<CustomizationWidget> {
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<AppConfigProvider>(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
),
),
)
]
],
),
);
}
}

View file

@ -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
),
)
],
),
),
),
);
}
}

View file

@ -4,10 +4,10 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_web_browser/flutter_web_browser.dart'; import 'package:flutter_web_browser/flutter_web_browser.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.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/server_info/server_info.dart';
import 'package:adguard_home_manager/screens/settings/encryption/encryption.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/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/screens/settings/dhcp/dhcp.dart';
import 'package:adguard_home_manager/widgets/section_label.dart'; import 'package:adguard_home_manager/widgets/section_label.dart';
import 'package:adguard_home_manager/screens/settings/dns/dns.dart'; import 'package:adguard_home_manager/screens/settings/dns/dns.dart';
@ -32,36 +32,6 @@ class Settings extends StatelessWidget {
final appConfigProvider = Provider.of<AppConfigProvider>(context); final appConfigProvider = Provider.of<AppConfigProvider>(context);
final serversProvider = Provider.of<ServersProvider>(context); final serversProvider = Provider.of<ServersProvider>(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() { void navigateServers() {
Future.delayed(const Duration(milliseconds: 0), (() { Future.delayed(const Duration(milliseconds: 0), (() {
Navigator.of(context).push( Navigator.of(context).push(
@ -167,10 +137,12 @@ class Settings extends StatelessWidget {
], ],
SectionLabel(label: AppLocalizations.of(context)!.appSettings), SectionLabel(label: AppLocalizations.of(context)!.appSettings),
CustomListTile( CustomListTile(
icon: Icons.light_mode_rounded, icon: Icons.palette_rounded,
title: AppLocalizations.of(context)!.theme, title: AppLocalizations.of(context)!.customization,
subtitle: getThemeString(), subtitle: AppLocalizations.of(context)!.customizationDescription,
onTap: openThemeModal, onTap: () => Navigator.push(context, MaterialPageRoute(
builder: (context) => const Customization()
))
), ),
CustomListTile( CustomListTile(
icon: Icons.storage_rounded, icon: Icons.storage_rounded,

View file

@ -1,6 +1,6 @@
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
Future<Map<String, dynamic>> loadDb() async { Future<Map<String, dynamic>> loadDb(bool acceptsDynamicTheme) async {
List<Map<String, Object?>>? servers; List<Map<String, Object?>>? servers;
List<Map<String, Object?>>? appConfig; List<Map<String, Object?>>? appConfig;
@ -37,26 +37,44 @@ Future<Map<String, dynamic>> 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( Database db = await openDatabase(
'adguard_home_manager.db', 'adguard_home_manager.db',
version: 4, version: 5,
onCreate: (Database db, int version) async { 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 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("CREATE TABLE appConfig (theme NUMERIC, overrideSslCheck NUMERIC, hideZeroValues NUMERIC, useDynamicColor NUMERIC, staticColor NUMERIC)");
await db.execute("INSERT INTO appConfig (theme, overrideSslCheck, hideZeroValues) VALUES (0, 0, 0)"); 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 { onUpgrade: (Database db, int oldVersion, int newVersion) async {
if (oldVersion == 1) { if (oldVersion == 1) {
await upgradeDbToV2(db); await upgradeDbToV2(db);
await upgradeDbToV3(db); await upgradeDbToV3(db);
await upgradeDbToV4(db); await upgradeDbToV4(db);
await upgradeDbToV5(db);
} }
if (oldVersion == 2) { if (oldVersion == 2) {
await upgradeDbToV3(db); await upgradeDbToV3(db);
await upgradeDbToV4(db); await upgradeDbToV4(db);
await upgradeDbToV5(db);
} }
if (oldVersion == 3) { if (oldVersion == 3) {
await upgradeDbToV4(db); await upgradeDbToV4(db);
await upgradeDbToV5(db);
}
if (oldVersion == 4) {
await upgradeDbToV5(db);
} }
}, },
onOpen: (Database db) async { onOpen: (Database db) async {

View file

@ -26,7 +26,7 @@ class CustomSwitchListTile extends StatelessWidget {
: () => onChanged(!value), : () => onChanged(!value),
child: Padding( child: Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
top: 20, left: 24, right: 10, bottom: 20 top: 15, left: 24, right: 15, bottom: 15
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,