mirror of
https://github.com/JGeek00/adguard-home-manager.git
synced 2025-04-21 06:19:10 +00:00
Added customization screen
This commit is contained in:
parent
f1a837dc42
commit
ddb9130769
14 changed files with 518 additions and 93 deletions
|
@ -1,24 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
Map<int, Color> 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
|
||||
|
|
16
lib/constants/adguard_green_color.dart
Normal file
16
lib/constants/adguard_green_color.dart
Normal 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
18
lib/constants/colors.dart
Normal 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
|
||||
];
|
22
lib/functions/generate_color_translation.dart
Normal file
22
lib/functions/generate_color_translation.dart
Normal 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];
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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<Main> {
|
|||
builder: (lightDynamic, darkDynamic) => MaterialApp(
|
||||
title: 'AdGuard Home Manager',
|
||||
theme: appConfigProvider.androidDeviceInfo != null && appConfigProvider.androidDeviceInfo!.version.sdkInt! >= 31
|
||||
? appConfigProvider.useDynamicColor == true
|
||||
? lightTheme(lightDynamic)
|
||||
: lightThemeOldVersions(),
|
||||
: lightThemeOldVersions(colors[appConfigProvider.staticColor])
|
||||
: lightThemeOldVersions(colors[appConfigProvider.staticColor]),
|
||||
darkTheme: appConfigProvider.androidDeviceInfo != null && appConfigProvider.androidDeviceInfo!.version.sdkInt! >= 31
|
||||
? appConfigProvider.useDynamicColor == true
|
||||
? darkTheme(darkDynamic)
|
||||
: darkThemeOldVersions(),
|
||||
: darkThemeOldVersions(colors[appConfigProvider.staticColor])
|
||||
: darkThemeOldVersions(colors[appConfigProvider.staticColor]),
|
||||
themeMode: appConfigProvider.selectedTheme,
|
||||
debugShowCheckedModeBanner: false,
|
||||
localizationsDelegates: const [
|
||||
|
|
|
@ -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<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 {
|
||||
try {
|
||||
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 {
|
||||
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();
|
||||
|
|
46
lib/screens/settings/customization/color_item.dart
Normal file
46
lib/screens/settings/customization/color_item.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
182
lib/screens/settings/customization/customization.dart
Normal file
182
lib/screens/settings/customization/customization.dart
Normal 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
|
||||
),
|
||||
),
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
66
lib/screens/settings/customization/theme_mode_button.dart
Normal file
66
lib/screens/settings/customization/theme_mode_button.dart
Normal 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
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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<AppConfigProvider>(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() {
|
||||
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,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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?>>? 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(
|
||||
'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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue