Theme configuration
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
class SharedPreferencesKeys {
|
||||
static const serversInstances = "serversInstances";
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
enum LoadStatus { loading, loaded, error }
|
||||
|
||||
enum ConnectionMethod { http, https }
|
||||
|
||||
enum SelectedTheme { system, light, dark }
|
||||
|
||||
6
lib/constants/shared_preferences_keys.dart
Normal file
6
lib/constants/shared_preferences_keys.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
class SharedPreferencesKeys {
|
||||
static const serversInstances = "serversInstances";
|
||||
static const selectedTheme = "selectedTheme";
|
||||
static const useDynamicTheme = "useDynamicTheme";
|
||||
static const selectedColor = "selectedColor";
|
||||
}
|
||||
3
lib/constants/strings.dart
Normal file
3
lib/constants/strings.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
class Strings {
|
||||
static const createdBy = "JGeek00";
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
class Urls {
|
||||
static const linkdingInstallationInstructions =
|
||||
"https://github.com/sissbruecker/linkding?tab=readme-ov-file#installation";
|
||||
static const gitHubRepo = "https://github.com/JGeek00/linkdy";
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
/// To regenerate, run: `dart run slang`
|
||||
///
|
||||
/// Locales: 2
|
||||
/// Strings: 68 (34 per locale)
|
||||
/// Strings: 122 (61 per locale)
|
||||
///
|
||||
/// Built on 2024-02-22 at 21:21 UTC
|
||||
/// Built on 2024-02-23 at 00:06 UTC
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
@@ -153,6 +153,7 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
|
||||
late final _StringsSearchEn search = _StringsSearchEn._(_root);
|
||||
late final _StringsSettingsEn settings = _StringsSettingsEn._(_root);
|
||||
late final _StringsWebviewEn webview = _StringsWebviewEn._(_root);
|
||||
late final _StringsColorsEn colors = _StringsColorsEn._(_root);
|
||||
}
|
||||
|
||||
// Path: onboarding
|
||||
@@ -214,7 +215,17 @@ class _StringsSettingsEn {
|
||||
|
||||
// Translations
|
||||
String get settings => 'Settings';
|
||||
String get appSettings => 'App settings';
|
||||
String get disconnectFromServer => 'Disconnect from server';
|
||||
String get aboutApp => 'About the app';
|
||||
String get appVersion => 'App version';
|
||||
String get createdBy => 'Created by';
|
||||
String get visitGooglePlay => 'Visit the application page at Google Play';
|
||||
String get visitGitHubRepo => 'Visit the application repository at GitHub';
|
||||
String get customizationDescription => 'Set the app theme and color palette.';
|
||||
String get generalSettings => 'General settings';
|
||||
String get generalSettingsDescription => 'General tweaks for the application.';
|
||||
late final _StringsSettingsCustomizationEn customization = _StringsSettingsCustomizationEn._(_root);
|
||||
}
|
||||
|
||||
// Path: webview
|
||||
@@ -233,6 +244,26 @@ class _StringsWebviewEn {
|
||||
String get share => 'Share';
|
||||
}
|
||||
|
||||
// Path: colors
|
||||
class _StringsColorsEn {
|
||||
_StringsColorsEn._(this._root);
|
||||
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
String get red => 'Red';
|
||||
String get green => 'Green';
|
||||
String get blue => 'Blue';
|
||||
String get yellow => 'Yellow';
|
||||
String get orange => 'Orange';
|
||||
String get brown => 'Brown';
|
||||
String get cyan => 'Cyan';
|
||||
String get purple => 'Purple';
|
||||
String get pink => 'Pink';
|
||||
String get deepOrange => 'Deep orange';
|
||||
String get indigo => 'Indigo';
|
||||
}
|
||||
|
||||
// Path: links.dates
|
||||
class _StringsLinksDatesEn {
|
||||
_StringsLinksDatesEn._(this._root);
|
||||
@@ -244,6 +275,22 @@ class _StringsLinksDatesEn {
|
||||
String yesterdayAt({required Object time}) => 'Yesterday, ${time}';
|
||||
}
|
||||
|
||||
// Path: settings.customization
|
||||
class _StringsSettingsCustomizationEn {
|
||||
_StringsSettingsCustomizationEn._(this._root);
|
||||
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
String get customization => 'Customization';
|
||||
String get theme => 'Theme';
|
||||
String get light => 'Light';
|
||||
String get dark => 'Dark';
|
||||
String get systemDefined => 'System defined';
|
||||
String get color => 'Color';
|
||||
String get useDynamicTheme => 'Use dynamic theme';
|
||||
}
|
||||
|
||||
// Path: <root>
|
||||
class _StringsEs implements Translations {
|
||||
/// You can call this constructor and build your own translation instance of this locale.
|
||||
@@ -273,6 +320,7 @@ class _StringsEs implements Translations {
|
||||
@override late final _StringsSearchEs search = _StringsSearchEs._(_root);
|
||||
@override late final _StringsSettingsEs settings = _StringsSettingsEs._(_root);
|
||||
@override late final _StringsWebviewEs webview = _StringsWebviewEs._(_root);
|
||||
@override late final _StringsColorsEs colors = _StringsColorsEs._(_root);
|
||||
}
|
||||
|
||||
// Path: onboarding
|
||||
@@ -334,7 +382,17 @@ class _StringsSettingsEs implements _StringsSettingsEn {
|
||||
|
||||
// Translations
|
||||
@override String get settings => 'Ajustes';
|
||||
@override String get appSettings => 'Ajustes de la aplicación';
|
||||
@override String get disconnectFromServer => 'Desconectar del servidor';
|
||||
@override String get aboutApp => 'Sobre la aplicación';
|
||||
@override String get appVersion => 'Versión de la aplicación';
|
||||
@override String get createdBy => 'Creado por';
|
||||
@override String get visitGooglePlay => 'Visita la página de la aplicación en Google Play';
|
||||
@override String get visitGitHubRepo => 'Visita el repositorio de la aplicación en GitHub';
|
||||
@override String get customizationDescription => 'Configura el tema y la paleta de colores.';
|
||||
@override String get generalSettings => 'Ajustes generales';
|
||||
@override String get generalSettingsDescription => 'Ajustes generales para la aplicación.';
|
||||
@override late final _StringsSettingsCustomizationEs customization = _StringsSettingsCustomizationEs._(_root);
|
||||
}
|
||||
|
||||
// Path: webview
|
||||
@@ -353,6 +411,26 @@ class _StringsWebviewEs implements _StringsWebviewEn {
|
||||
@override String get share => 'Compartir';
|
||||
}
|
||||
|
||||
// Path: colors
|
||||
class _StringsColorsEs implements _StringsColorsEn {
|
||||
_StringsColorsEs._(this._root);
|
||||
|
||||
@override final _StringsEs _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
@override String get red => 'Rojo';
|
||||
@override String get green => 'Verde';
|
||||
@override String get blue => 'Azul';
|
||||
@override String get yellow => 'Amarillo';
|
||||
@override String get orange => 'Naranja';
|
||||
@override String get brown => 'Marron';
|
||||
@override String get cyan => 'Cian';
|
||||
@override String get purple => 'Morado';
|
||||
@override String get pink => 'Rosa';
|
||||
@override String get deepOrange => 'Naranja oscuro';
|
||||
@override String get indigo => 'Índigo';
|
||||
}
|
||||
|
||||
// Path: links.dates
|
||||
class _StringsLinksDatesEs implements _StringsLinksDatesEn {
|
||||
_StringsLinksDatesEs._(this._root);
|
||||
@@ -364,6 +442,22 @@ class _StringsLinksDatesEs implements _StringsLinksDatesEn {
|
||||
@override String yesterdayAt({required Object time}) => 'Ayer, ${time}';
|
||||
}
|
||||
|
||||
// Path: settings.customization
|
||||
class _StringsSettingsCustomizationEs implements _StringsSettingsCustomizationEn {
|
||||
_StringsSettingsCustomizationEs._(this._root);
|
||||
|
||||
@override final _StringsEs _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
@override String get customization => 'Personalización';
|
||||
@override String get theme => 'Tema';
|
||||
@override String get light => 'Claro';
|
||||
@override String get dark => 'Oscuro';
|
||||
@override String get systemDefined => 'Definido por el sistema';
|
||||
@override String get color => 'Color';
|
||||
@override String get useDynamicTheme => 'Usar tema dinámico';
|
||||
}
|
||||
|
||||
/// Flat map(s) containing all translations.
|
||||
/// Only for edge cases! For simple maps, use the map function of this library.
|
||||
|
||||
@@ -396,7 +490,23 @@ extension on Translations {
|
||||
case 'links.dates.yesterdayAt': return ({required Object time}) => 'Yesterday, ${time}';
|
||||
case 'search.search': return 'Search';
|
||||
case 'settings.settings': return 'Settings';
|
||||
case 'settings.appSettings': return 'App settings';
|
||||
case 'settings.disconnectFromServer': return 'Disconnect from server';
|
||||
case 'settings.aboutApp': return 'About the app';
|
||||
case 'settings.appVersion': return 'App version';
|
||||
case 'settings.createdBy': return 'Created by';
|
||||
case 'settings.visitGooglePlay': return 'Visit the application page at Google Play';
|
||||
case 'settings.visitGitHubRepo': return 'Visit the application repository at GitHub';
|
||||
case 'settings.customizationDescription': return 'Set the app theme and color palette.';
|
||||
case 'settings.generalSettings': return 'General settings';
|
||||
case 'settings.generalSettingsDescription': return 'General tweaks for the application.';
|
||||
case 'settings.customization.customization': return 'Customization';
|
||||
case 'settings.customization.theme': return 'Theme';
|
||||
case 'settings.customization.light': return 'Light';
|
||||
case 'settings.customization.dark': return 'Dark';
|
||||
case 'settings.customization.systemDefined': return 'System defined';
|
||||
case 'settings.customization.color': return 'Color';
|
||||
case 'settings.customization.useDynamicTheme': return 'Use dynamic theme';
|
||||
case 'webview.goBack': return 'Go back';
|
||||
case 'webview.goForward': return 'Go forward';
|
||||
case 'webview.reload': return 'Reload';
|
||||
@@ -404,6 +514,17 @@ extension on Translations {
|
||||
case 'webview.copyLinkClipboard': return 'Copy link to clipboard';
|
||||
case 'webview.linkCopiedClipboard': return 'Link copied to the clipboard.';
|
||||
case 'webview.share': return 'Share';
|
||||
case 'colors.red': return 'Red';
|
||||
case 'colors.green': return 'Green';
|
||||
case 'colors.blue': return 'Blue';
|
||||
case 'colors.yellow': return 'Yellow';
|
||||
case 'colors.orange': return 'Orange';
|
||||
case 'colors.brown': return 'Brown';
|
||||
case 'colors.cyan': return 'Cyan';
|
||||
case 'colors.purple': return 'Purple';
|
||||
case 'colors.pink': return 'Pink';
|
||||
case 'colors.deepOrange': return 'Deep orange';
|
||||
case 'colors.indigo': return 'Indigo';
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
@@ -438,7 +559,23 @@ extension on _StringsEs {
|
||||
case 'links.dates.yesterdayAt': return ({required Object time}) => 'Ayer, ${time}';
|
||||
case 'search.search': return 'Buscar';
|
||||
case 'settings.settings': return 'Ajustes';
|
||||
case 'settings.appSettings': return 'Ajustes de la aplicación';
|
||||
case 'settings.disconnectFromServer': return 'Desconectar del servidor';
|
||||
case 'settings.aboutApp': return 'Sobre la aplicación';
|
||||
case 'settings.appVersion': return 'Versión de la aplicación';
|
||||
case 'settings.createdBy': return 'Creado por';
|
||||
case 'settings.visitGooglePlay': return 'Visita la página de la aplicación en Google Play';
|
||||
case 'settings.visitGitHubRepo': return 'Visita el repositorio de la aplicación en GitHub';
|
||||
case 'settings.customizationDescription': return 'Configura el tema y la paleta de colores.';
|
||||
case 'settings.generalSettings': return 'Ajustes generales';
|
||||
case 'settings.generalSettingsDescription': return 'Ajustes generales para la aplicación.';
|
||||
case 'settings.customization.customization': return 'Personalización';
|
||||
case 'settings.customization.theme': return 'Tema';
|
||||
case 'settings.customization.light': return 'Claro';
|
||||
case 'settings.customization.dark': return 'Oscuro';
|
||||
case 'settings.customization.systemDefined': return 'Definido por el sistema';
|
||||
case 'settings.customization.color': return 'Color';
|
||||
case 'settings.customization.useDynamicTheme': return 'Usar tema dinámico';
|
||||
case 'webview.goBack': return 'Ir atrás';
|
||||
case 'webview.goForward': return 'Ir adelante';
|
||||
case 'webview.reload': return 'Recargar';
|
||||
@@ -446,6 +583,17 @@ extension on _StringsEs {
|
||||
case 'webview.copyLinkClipboard': return 'Copiar enlace al portapapeles';
|
||||
case 'webview.linkCopiedClipboard': return 'Enlace copiado al portapapeles.';
|
||||
case 'webview.share': return 'Compartir';
|
||||
case 'colors.red': return 'Rojo';
|
||||
case 'colors.green': return 'Verde';
|
||||
case 'colors.blue': return 'Azul';
|
||||
case 'colors.yellow': return 'Amarillo';
|
||||
case 'colors.orange': return 'Naranja';
|
||||
case 'colors.brown': return 'Marron';
|
||||
case 'colors.cyan': return 'Cian';
|
||||
case 'colors.purple': return 'Morado';
|
||||
case 'colors.pink': return 'Rosa';
|
||||
case 'colors.deepOrange': return 'Naranja oscuro';
|
||||
case 'colors.indigo': return 'Índigo';
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,25 @@
|
||||
},
|
||||
"settings": {
|
||||
"settings": "Settings",
|
||||
"disconnectFromServer": "Disconnect from server"
|
||||
"appSettings": "App settings",
|
||||
"disconnectFromServer": "Disconnect from server",
|
||||
"aboutApp": "About the app",
|
||||
"appVersion": "App version",
|
||||
"createdBy": "Created by",
|
||||
"visitGooglePlay": "Visit the application page at Google Play",
|
||||
"visitGitHubRepo": "Visit the application repository at GitHub",
|
||||
"customizationDescription": "Set the app theme and color palette.",
|
||||
"generalSettings": "General settings",
|
||||
"generalSettingsDescription": "General tweaks for the application.",
|
||||
"customization": {
|
||||
"customization": "Customization",
|
||||
"theme": "Theme",
|
||||
"light": "Light",
|
||||
"dark": "Dark",
|
||||
"systemDefined": "System defined",
|
||||
"color": "Color",
|
||||
"useDynamicTheme": "Use dynamic theme"
|
||||
}
|
||||
},
|
||||
"webview": {
|
||||
"goBack": "Go back",
|
||||
@@ -44,5 +62,18 @@
|
||||
"copyLinkClipboard": "Copy link to clipboard",
|
||||
"linkCopiedClipboard": "Link copied to the clipboard.",
|
||||
"share": "Share"
|
||||
},
|
||||
"colors": {
|
||||
"red": "Red",
|
||||
"green": "Green",
|
||||
"blue": "Blue",
|
||||
"yellow": "Yellow",
|
||||
"orange": "Orange",
|
||||
"brown": "Brown",
|
||||
"cyan": "Cyan",
|
||||
"purple": "Purple",
|
||||
"pink": "Pink",
|
||||
"deepOrange": "Deep orange",
|
||||
"indigo": "Indigo"
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,25 @@
|
||||
},
|
||||
"settings": {
|
||||
"settings": "Ajustes",
|
||||
"disconnectFromServer": "Desconectar del servidor"
|
||||
"appSettings": "Ajustes de la aplicación",
|
||||
"disconnectFromServer": "Desconectar del servidor",
|
||||
"aboutApp": "Sobre la aplicación",
|
||||
"appVersion": "Versión de la aplicación",
|
||||
"createdBy": "Creado por",
|
||||
"visitGooglePlay": "Visita la página de la aplicación en Google Play",
|
||||
"visitGitHubRepo": "Visita el repositorio de la aplicación en GitHub",
|
||||
"customizationDescription": "Configura el tema y la paleta de colores.",
|
||||
"generalSettings": "Ajustes generales",
|
||||
"generalSettingsDescription": "Ajustes generales para la aplicación.",
|
||||
"customization": {
|
||||
"customization": "Personalización",
|
||||
"theme": "Tema",
|
||||
"light": "Claro",
|
||||
"dark": "Oscuro",
|
||||
"systemDefined": "Definido por el sistema",
|
||||
"color": "Color",
|
||||
"useDynamicTheme": "Usar tema dinámico"
|
||||
}
|
||||
},
|
||||
"webview": {
|
||||
"goBack": "Ir atrás",
|
||||
@@ -44,5 +62,18 @@
|
||||
"copyLinkClipboard": "Copiar enlace al portapapeles",
|
||||
"linkCopiedClipboard": "Enlace copiado al portapapeles.",
|
||||
"share": "Compartir"
|
||||
},
|
||||
"colors": {
|
||||
"red": "Rojo",
|
||||
"green": "Verde",
|
||||
"blue": "Azul",
|
||||
"yellow": "Amarillo",
|
||||
"orange": "Naranja",
|
||||
"brown": "Marron",
|
||||
"cyan": "Cian",
|
||||
"purple": "Morado",
|
||||
"pink": "Rosa",
|
||||
"deepOrange": "Naranja oscuro",
|
||||
"indigo": "Índigo"
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,11 @@ import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:linkdy/constants/global_keys.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'package:linkdy/providers/app_status_provider.dart';
|
||||
import 'package:linkdy/providers/app_info_provider.dart';
|
||||
import 'package:linkdy/config/theme.dart';
|
||||
import 'package:linkdy/constants/colors.dart';
|
||||
import 'package:linkdy/i18n/strings.g.dart';
|
||||
@@ -25,11 +28,14 @@ void main() async {
|
||||
|
||||
final sharedPreferences = await SharedPreferences.getInstance();
|
||||
|
||||
final appInfo = await PackageInfo.fromPlatform();
|
||||
|
||||
runApp(
|
||||
TranslationProvider(
|
||||
child: ProviderScope(
|
||||
overrides: [
|
||||
sharedPreferencesProvider.overrideWithValue(sharedPreferences),
|
||||
appInfoProvider.overrideWithValue(appInfo),
|
||||
],
|
||||
child: const MyApp(),
|
||||
),
|
||||
@@ -42,18 +48,33 @@ class MyApp extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final selectedColor = ref.watch(appStatusProvider).selectedColor;
|
||||
final useDynamicColor = ref.watch(appStatusProvider).useDynamicTheme;
|
||||
|
||||
return DynamicColorBuilder(
|
||||
builder: (lightDynamic, darkDynamic) => MaterialApp.router(
|
||||
title: 'Linkdy',
|
||||
theme: lightDynamic != null ? lightTheme(lightDynamic) : lightThemeOldVersions(colors[0]),
|
||||
darkTheme: darkDynamic != null ? darkTheme(darkDynamic) : darkThemeOldVersions(colors[0]),
|
||||
locale: TranslationProvider.of(context).flutterLocale,
|
||||
supportedLocales: AppLocaleUtils.supportedLocales,
|
||||
localizationsDelegates: GlobalMaterialLocalizations.delegates,
|
||||
debugShowCheckedModeBanner: false,
|
||||
scaffoldMessengerKey: scaffoldMessengerGlobalKey,
|
||||
routerConfig: ref.watch(routerProvider),
|
||||
),
|
||||
builder: (lightDynamic, darkDynamic) {
|
||||
ref.read(appStatusProvider.notifier).setSupportsDynamicTheme(lightDynamic != null && darkDynamic != null);
|
||||
return MaterialApp.router(
|
||||
title: 'Linkdy',
|
||||
theme: lightDynamic != null
|
||||
? useDynamicColor == true
|
||||
? lightTheme(lightDynamic)
|
||||
: lightThemeOldVersions(colors[selectedColor])
|
||||
: lightThemeOldVersions(colors[selectedColor]),
|
||||
darkTheme: darkDynamic != null
|
||||
? useDynamicColor == true
|
||||
? darkTheme(darkDynamic)
|
||||
: darkThemeOldVersions(colors[selectedColor])
|
||||
: darkThemeOldVersions(colors[selectedColor]),
|
||||
themeMode: ref.watch(selectedThemeProvider),
|
||||
locale: TranslationProvider.of(context).flutterLocale,
|
||||
supportedLocales: AppLocaleUtils.supportedLocales,
|
||||
localizationsDelegates: GlobalMaterialLocalizations.delegates,
|
||||
debugShowCheckedModeBanner: false,
|
||||
scaffoldMessengerKey: scaffoldMessengerGlobalKey,
|
||||
routerConfig: ref.watch(routerProvider),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import 'package:linkdy/models/server_instance.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:linkdy/constants/enums.dart';
|
||||
|
||||
class AppStatus {
|
||||
AppStatus();
|
||||
class AppStatusModel {
|
||||
SelectedTheme selectedTheme;
|
||||
bool supportsDynamicTheme;
|
||||
bool useDynamicTheme;
|
||||
int selectedColor;
|
||||
|
||||
AppStatus copyWith({
|
||||
SharedPreferences? sharedPreferences,
|
||||
ServerInstance? serverInstance,
|
||||
}) =>
|
||||
AppStatus();
|
||||
AppStatusModel({
|
||||
this.selectedTheme = SelectedTheme.system,
|
||||
this.supportsDynamicTheme = false,
|
||||
this.useDynamicTheme = true,
|
||||
this.selectedColor = 0,
|
||||
});
|
||||
}
|
||||
|
||||
9
lib/providers/app_info_provider.dart
Normal file
9
lib/providers/app_info_provider.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'app_info_provider.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
PackageInfo appInfo(AppInfoRef ref) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
24
lib/providers/app_info_provider.g.dart
Normal file
24
lib/providers/app_info_provider.g.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'app_info_provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$appInfoHash() => r'c1b99fe8e0ba1b1ba04aa2c1ae066a3de9f1dac1';
|
||||
|
||||
/// See also [appInfo].
|
||||
@ProviderFor(appInfo)
|
||||
final appInfoProvider = Provider<PackageInfo>.internal(
|
||||
appInfo,
|
||||
name: r'appInfoProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$appInfoHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef AppInfoRef = ProviderRef<PackageInfo>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
@@ -1,18 +1,62 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:linkdy/constants/shared_preferences_keys.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import 'package:linkdy/providers/shared_preferences_provider.dart';
|
||||
import 'package:linkdy/constants/enums.dart';
|
||||
import 'package:linkdy/models/app_status.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
part 'app_status_provider.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class AppStatusProvider extends _$AppStatusProvider {
|
||||
class AppStatus extends _$AppStatus {
|
||||
@override
|
||||
AppStatus build() {
|
||||
return AppStatus();
|
||||
AppStatusModel build() {
|
||||
final sharedPreferences = ref.watch(sharedPreferencesProvider);
|
||||
return AppStatusModel(
|
||||
selectedColor: sharedPreferences.getInt(SharedPreferencesKeys.selectedColor) ?? 0,
|
||||
selectedTheme: SelectedTheme.values[sharedPreferences.getInt(SharedPreferencesKeys.selectedTheme) ?? 0],
|
||||
useDynamicTheme: sharedPreferences.getBool(SharedPreferencesKeys.useDynamicTheme) ?? true,
|
||||
);
|
||||
}
|
||||
|
||||
void setSharedPreferencesInstance(SharedPreferences instance) {
|
||||
state = state.copyWith(sharedPreferences: instance);
|
||||
void setTheme(SelectedTheme theme) {
|
||||
state.selectedTheme = theme;
|
||||
ref.read(sharedPreferencesProvider).setInt(SharedPreferencesKeys.selectedTheme, theme.index);
|
||||
ref.notifyListeners();
|
||||
}
|
||||
|
||||
void setSupportsDynamicTheme(bool value) {
|
||||
state.supportsDynamicTheme = value;
|
||||
}
|
||||
|
||||
void setUseDynamicTheme(bool value) {
|
||||
state.useDynamicTheme = value;
|
||||
ref.read(sharedPreferencesProvider).setBool(SharedPreferencesKeys.useDynamicTheme, value);
|
||||
ref.notifyListeners();
|
||||
}
|
||||
|
||||
void setSelectedColor(int value) {
|
||||
state.selectedColor = value;
|
||||
ref.read(sharedPreferencesProvider).setInt(SharedPreferencesKeys.selectedColor, value);
|
||||
ref.notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
ThemeMode selectedTheme(SelectedThemeRef ref) {
|
||||
switch (ref.watch(appStatusProvider).selectedTheme) {
|
||||
case SelectedTheme.system:
|
||||
return SchedulerBinding.instance.window.platformBrightness == Brightness.light ? ThemeMode.light : ThemeMode.dark;
|
||||
|
||||
case SelectedTheme.light:
|
||||
return ThemeMode.light;
|
||||
|
||||
case SelectedTheme.dark:
|
||||
return ThemeMode.dark;
|
||||
|
||||
default:
|
||||
return ThemeMode.light;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,21 +6,34 @@ part of 'app_status_provider.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$appStatusProviderHash() => r'ba585a0cf3cf46f88f5895eacec3e992bf0cd42f';
|
||||
String _$selectedThemeHash() => r'b820484a59ed72419c5b4cad0b894a030b54c702';
|
||||
|
||||
/// See also [AppStatusProvider].
|
||||
@ProviderFor(AppStatusProvider)
|
||||
final appStatusProviderProvider =
|
||||
NotifierProvider<AppStatusProvider, AppStatus>.internal(
|
||||
AppStatusProvider.new,
|
||||
name: r'appStatusProviderProvider',
|
||||
/// See also [selectedTheme].
|
||||
@ProviderFor(selectedTheme)
|
||||
final selectedThemeProvider = AutoDisposeProvider<ThemeMode>.internal(
|
||||
selectedTheme,
|
||||
name: r'selectedThemeProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$appStatusProviderHash,
|
||||
: _$selectedThemeHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$AppStatusProvider = Notifier<AppStatus>;
|
||||
typedef SelectedThemeRef = AutoDisposeProviderRef<ThemeMode>;
|
||||
String _$appStatusHash() => r'cde4567157dd5a89f842a2818efc199db3585232';
|
||||
|
||||
/// See also [AppStatus].
|
||||
@ProviderFor(AppStatus)
|
||||
final appStatusProvider = NotifierProvider<AppStatus, AppStatusModel>.internal(
|
||||
AppStatus.new,
|
||||
name: r'appStatusProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$appStatusHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$AppStatus = Notifier<AppStatusModel>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:linkdy/config/shared_preferences_keys.dart';
|
||||
import 'package:linkdy/constants/shared_preferences_keys.dart';
|
||||
import 'package:linkdy/providers/shared_preferences_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
final sharedPreferencesProvider = Provider<SharedPreferences>((ref) {
|
||||
part 'shared_preferences_provider.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
SharedPreferences sharedPreferences(SharedPreferencesRef ref) {
|
||||
throw UnimplementedError();
|
||||
});
|
||||
}
|
||||
|
||||
25
lib/providers/shared_preferences_provider.g.dart
Normal file
25
lib/providers/shared_preferences_provider.g.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'shared_preferences_provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$sharedPreferencesHash() => r'3a9f8412df34c1653d08100c9826aa2125b80f7f';
|
||||
|
||||
/// See also [sharedPreferences].
|
||||
@ProviderFor(sharedPreferences)
|
||||
final sharedPreferencesProvider = Provider<SharedPreferences>.internal(
|
||||
sharedPreferences,
|
||||
name: r'sharedPreferencesProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$sharedPreferencesHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef SharedPreferencesRef = ProviderRef<SharedPreferences>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
@@ -4,4 +4,5 @@ class RoutesPaths {
|
||||
static const webview = "/webview";
|
||||
static const search = "/search";
|
||||
static const settings = "/settings";
|
||||
static const customization = "/settings/customization";
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:linkdy/screens/links/ui/links.dart';
|
||||
import 'package:linkdy/screens/onboarding/ui/onboarding.dart';
|
||||
import 'package:linkdy/screens/search/ui/search.dart';
|
||||
import 'package:linkdy/screens/settings/ui/customization/customization.dart';
|
||||
import 'package:linkdy/screens/settings/ui/settings.dart';
|
||||
import 'package:linkdy/screens/webview/ui/webview.dart';
|
||||
import 'package:linkdy/widgets/layout.dart';
|
||||
@@ -58,6 +59,10 @@ final List<RouteBase> appRoutes = [
|
||||
path: RoutesPaths.settings,
|
||||
builder: (context, state) => const Settings(),
|
||||
),
|
||||
GoRoute(
|
||||
path: RoutesPaths.customization,
|
||||
builder: (context, state) => const Customization(),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
9
lib/screens/settings/model/customization.model.dart
Normal file
9
lib/screens/settings/model/customization.model.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomizationModel {
|
||||
ScrollController? scrollController;
|
||||
|
||||
CustomizationModel({
|
||||
this.scrollController,
|
||||
});
|
||||
}
|
||||
16
lib/screens/settings/provider/customization.provider.dart
Normal file
16
lib/screens/settings/provider/customization.provider.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import 'package:linkdy/screens/settings/model/customization.model.dart';
|
||||
|
||||
part 'customization.provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
class Customization extends _$Customization {
|
||||
@override
|
||||
CustomizationModel build() {
|
||||
return CustomizationModel(
|
||||
scrollController: ScrollController(),
|
||||
);
|
||||
}
|
||||
}
|
||||
26
lib/screens/settings/provider/customization.provider.g.dart
Normal file
26
lib/screens/settings/provider/customization.provider.g.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'customization.provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$customizationHash() => r'c4cf4b566abbe697ec14c5024e1576f230d3e9fb';
|
||||
|
||||
/// See also [Customization].
|
||||
@ProviderFor(Customization)
|
||||
final customizationProvider =
|
||||
AutoDisposeNotifierProvider<Customization, CustomizationModel>.internal(
|
||||
Customization.new,
|
||||
name: r'customizationProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$customizationHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$Customization = AutoDisposeNotifier<CustomizationModel>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
57
lib/screens/settings/ui/customization/color_item.dart
Normal file
57
lib/screens/settings/ui/customization/color_item.dart
Normal file
@@ -0,0 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ColorItem extends StatelessWidget {
|
||||
final int index;
|
||||
final int total;
|
||||
final Color color;
|
||||
final int numericValue;
|
||||
final int? selectedValue;
|
||||
final void Function(int) onChanged;
|
||||
|
||||
const ColorItem({
|
||||
super.key,
|
||||
required this.index,
|
||||
required this.total,
|
||||
required this.color,
|
||||
required this.numericValue,
|
||||
required this.selectedValue,
|
||||
required this.onChanged
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: index == 0
|
||||
? const EdgeInsets.only(top: 10, right: 10, bottom: 10)
|
||||
: index == total-1
|
||||
? const EdgeInsets.only(top: 10, bottom: 10, left: 10)
|
||||
: 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,
|
||||
borderRadius: BorderRadius.circular(50)
|
||||
),
|
||||
child: AnimatedOpacity(
|
||||
opacity: numericValue == selectedValue ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeInOut,
|
||||
child: Icon(
|
||||
Icons.check,
|
||||
size: 30,
|
||||
color: color.computeLuminance() > 0.5 ? Colors.black : Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
156
lib/screens/settings/ui/customization/customization.dart
Normal file
156
lib/screens/settings/ui/customization/customization.dart
Normal file
@@ -0,0 +1,156 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:linkdy/screens/settings/provider/customization.provider.dart';
|
||||
import 'package:linkdy/screens/settings/ui/customization/color_item.dart';
|
||||
import 'package:linkdy/screens/settings/ui/customization/theme_mode_button.dart';
|
||||
import 'package:linkdy/utils/color_translation.dart';
|
||||
import 'package:linkdy/widgets/custom_switch_list_tile.dart';
|
||||
import 'package:linkdy/widgets/section_label.dart';
|
||||
|
||||
import 'package:linkdy/constants/colors.dart';
|
||||
import 'package:linkdy/constants/enums.dart';
|
||||
import 'package:linkdy/i18n/strings.g.dart';
|
||||
import 'package:linkdy/providers/app_status_provider.dart';
|
||||
|
||||
class Customization extends ConsumerWidget {
|
||||
const Customization({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final width = MediaQuery.of(context).size.width;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(t.settings.customization.customization),
|
||||
centerTitle: false,
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
children: [
|
||||
SectionLabel(
|
||||
label: t.settings.customization.theme,
|
||||
padding: const EdgeInsets.only(top: 10, left: 16, right: 16, bottom: 5),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
CustomSwitchListTile(
|
||||
value: ref.watch(appStatusProvider).selectedTheme == SelectedTheme.system ? true : false,
|
||||
onChanged: (value) => ref
|
||||
.read(appStatusProvider.notifier)
|
||||
.setTheme(value == true ? SelectedTheme.system : SelectedTheme.light),
|
||||
title: t.settings.customization.systemDefined,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
ThemeModeButton(
|
||||
icon: Icons.light_mode,
|
||||
value: 1,
|
||||
selected: ref.watch(appStatusProvider).selectedTheme.index,
|
||||
label: t.settings.customization.light,
|
||||
onChanged: (value) => ref.read(appStatusProvider.notifier).setTheme(SelectedTheme.values[value]),
|
||||
disabled: ref.watch(appStatusProvider).selectedTheme == SelectedTheme.system ? true : false,
|
||||
),
|
||||
ThemeModeButton(
|
||||
icon: Icons.dark_mode,
|
||||
value: 2,
|
||||
selected: ref.watch(appStatusProvider).selectedTheme.index,
|
||||
label: t.settings.customization.dark,
|
||||
onChanged: (value) => ref.read(appStatusProvider.notifier).setTheme(SelectedTheme.values[value]),
|
||||
disabled: ref.watch(appStatusProvider).selectedTheme == SelectedTheme.system ? true : false,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
SectionLabel(
|
||||
label: t.settings.customization.color,
|
||||
padding: const EdgeInsets.only(top: 45, left: 16, right: 16, bottom: 5),
|
||||
),
|
||||
if (ref.watch(appStatusProvider).supportsDynamicTheme)
|
||||
CustomSwitchListTile(
|
||||
value: ref.watch(appStatusProvider).useDynamicTheme,
|
||||
onChanged: ref.read(appStatusProvider.notifier).setUseDynamicTheme,
|
||||
title: t.settings.customization.useDynamicTheme,
|
||||
),
|
||||
if (ref.watch(appStatusProvider).supportsDynamicTheme == false ||
|
||||
(ref.watch(appStatusProvider).supportsDynamicTheme == true &&
|
||||
ref.watch(appStatusProvider).useDynamicTheme == false))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8, left: 16, right: 16),
|
||||
child: Scrollbar(
|
||||
controller: ref.watch(customizationProvider).scrollController,
|
||||
thumbVisibility: Platform.isMacOS || Platform.isLinux || Platform.isWindows,
|
||||
interactive: Platform.isMacOS || Platform.isLinux || Platform.isWindows,
|
||||
thickness: Platform.isMacOS || Platform.isLinux || Platform.isWindows ? 8 : 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: 70,
|
||||
child: ListView.builder(
|
||||
controller: ref.watch(customizationProvider).scrollController,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: colors.length,
|
||||
padding: const EdgeInsets.all(0),
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0) {
|
||||
return Row(
|
||||
children: [
|
||||
ColorItem(
|
||||
index: index,
|
||||
total: colors.length,
|
||||
color: colors[index],
|
||||
numericValue: index,
|
||||
selectedValue: ref.watch(appStatusProvider).selectedColor,
|
||||
onChanged: ref.read(appStatusProvider.notifier).setSelectedColor,
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 10),
|
||||
width: 1,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey,
|
||||
borderRadius: BorderRadius.circular(1),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return ColorItem(
|
||||
index: index,
|
||||
total: colors.length,
|
||||
color: colors[index],
|
||||
numericValue: index,
|
||||
selectedValue: ref.watch(appStatusProvider).selectedColor,
|
||||
onChanged: ref.read(appStatusProvider.notifier).setSelectedColor,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text(
|
||||
colorTranslation(ref.watch(appStatusProvider).selectedColor),
|
||||
style: TextStyle(color: Theme.of(context).listTileTheme.iconColor, fontSize: 16),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
88
lib/screens/settings/ui/customization/theme_mode_button.dart
Normal file
88
lib/screens/settings/ui/customization/theme_mode_button.dart
Normal file
@@ -0,0 +1,88 @@
|
||||
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;
|
||||
final bool? disabled;
|
||||
|
||||
const ThemeModeButton({
|
||||
Key? key,
|
||||
required this.icon,
|
||||
required this.value,
|
||||
required this.selected,
|
||||
required this.label,
|
||||
required this.onChanged,
|
||||
this.disabled
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Color greyBackgroundColor = Theme.of(context).brightness == Brightness.light
|
||||
?const Color.fromRGBO(200, 200, 200, 1)
|
||||
:const Color.fromRGBO(50, 50, 50, 1);
|
||||
final Color greyIconColor = Theme.of(context).brightness == Brightness.light
|
||||
? const Color.fromRGBO(130, 130, 130, 1)
|
||||
: const Color.fromRGBO(100, 100, 100, 1);
|
||||
|
||||
return ElevatedButton(
|
||||
onPressed: disabled == null || disabled == false
|
||||
? () => onChanged(value)
|
||||
: null,
|
||||
style: ButtonStyle(
|
||||
elevation: MaterialStateProperty.all(0),
|
||||
shape: MaterialStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
)
|
||||
),
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
value == selected
|
||||
? disabled == null || disabled == false
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: greyBackgroundColor
|
||||
: disabled == null || disabled == false
|
||||
? Theme.of(context).colorScheme.surfaceVariant
|
||||
: greyBackgroundColor,
|
||||
)
|
||||
),
|
||||
child: AnimatedContainer(
|
||||
width: 118,
|
||||
height: 150,
|
||||
curve: Curves.easeInOut,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
color: value == selected
|
||||
? disabled == null || disabled == false
|
||||
? Theme.of(context).colorScheme.primary.computeLuminance() > 0.5 ? Colors.black : Colors.white
|
||||
: greyIconColor
|
||||
: disabled == null || disabled == false
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: greyIconColor,
|
||||
size: 30,
|
||||
),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: value == selected
|
||||
? disabled == null || disabled == false
|
||||
? Theme.of(context).colorScheme.primary.computeLuminance() > 0.5 ? Colors.black : Colors.white
|
||||
: greyIconColor
|
||||
: disabled == null || disabled == false
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: greyIconColor,
|
||||
fontSize: 18
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:linkdy/providers/router_provider.dart';
|
||||
import 'package:linkdy/router/paths.dart';
|
||||
|
||||
import 'package:linkdy/widgets/section_label.dart';
|
||||
import 'package:linkdy/widgets/custom_list_tile.dart';
|
||||
|
||||
import 'package:linkdy/providers/app_info_provider.dart';
|
||||
import 'package:linkdy/utils/open_url.dart';
|
||||
import 'package:linkdy/constants/strings.dart';
|
||||
import 'package:linkdy/constants/urls.dart';
|
||||
import 'package:linkdy/i18n/strings.g.dart';
|
||||
import 'package:linkdy/providers/api_client_provider.dart';
|
||||
|
||||
@@ -15,6 +25,18 @@ class Settings extends ConsumerWidget {
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
SectionLabel(label: t.settings.appSettings),
|
||||
CustomListTile(
|
||||
icon: Icons.palette_rounded,
|
||||
title: t.settings.customization.customization,
|
||||
subtitle: t.settings.customizationDescription,
|
||||
onTap: () => ref.read(routerProvider).push(RoutesPaths.customization),
|
||||
),
|
||||
CustomListTile(
|
||||
icon: Icons.settings_rounded,
|
||||
title: t.settings.generalSettings,
|
||||
subtitle: t.settings.generalSettingsDescription,
|
||||
),
|
||||
FilledButton.icon(
|
||||
onPressed: () => ref.read(apiClientProviderProvider.notifier).disconnectApiClient(),
|
||||
icon: const Icon(Icons.clear_rounded),
|
||||
@@ -22,6 +44,44 @@ class Settings extends ConsumerWidget {
|
||||
t.settings.disconnectFromServer,
|
||||
),
|
||||
),
|
||||
SectionLabel(label: t.settings.aboutApp),
|
||||
CustomListTile(
|
||||
title: t.settings.appVersion,
|
||||
subtitle: ref.watch(appInfoProvider).version,
|
||||
),
|
||||
CustomListTile(
|
||||
title: t.settings.createdBy,
|
||||
subtitle: Strings.createdBy,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// if (Platform.isAndroid)
|
||||
// IconButton(
|
||||
// onPressed: () => openUrl(Urls.playStore),
|
||||
// icon: SvgPicture.asset(
|
||||
// 'assets/resources/google-play.svg',
|
||||
// color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
// width: 30,
|
||||
// height: 30,
|
||||
// ),
|
||||
// tooltip: t.settings.visitGooglePlay,
|
||||
// ),
|
||||
IconButton(
|
||||
onPressed: () => openUrl(Urls.gitHubRepo),
|
||||
icon: SvgPicture.asset(
|
||||
'assets/resources/github.svg',
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
width: 30,
|
||||
height: 30,
|
||||
),
|
||||
tooltip: t.settings.visitGitHubRepo,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
22
lib/utils/color_translation.dart
Normal file
22
lib/utils/color_translation.dart
Normal file
@@ -0,0 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:linkdy/i18n/strings.g.dart';
|
||||
|
||||
String colorTranslation(int index) {
|
||||
// This indexes has to be in sync with colors array at constants/colors.dart
|
||||
List<String> translations = [
|
||||
"Linkding",
|
||||
t.colors.red,
|
||||
t.colors.green,
|
||||
t.colors.blue,
|
||||
t.colors.yellow,
|
||||
t.colors.orange,
|
||||
t.colors.brown,
|
||||
t.colors.cyan,
|
||||
t.colors.purple,
|
||||
t.colors.pink,
|
||||
t.colors.deepOrange,
|
||||
t.colors.indigo,
|
||||
];
|
||||
return translations[index];
|
||||
}
|
||||
99
lib/widgets/custom_list_tile.dart
Normal file
99
lib/widgets/custom_list_tile.dart
Normal file
@@ -0,0 +1,99 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomListTile extends StatelessWidget {
|
||||
final String title;
|
||||
final String? subtitle;
|
||||
final Widget? subtitleWidget;
|
||||
final void Function()? onTap;
|
||||
final IconData? icon;
|
||||
final Widget? trailing;
|
||||
final EdgeInsets? padding;
|
||||
final void Function()? onLongPress;
|
||||
final bool? disabled;
|
||||
final void Function(bool)? onHover;
|
||||
final Color? color;
|
||||
|
||||
const CustomListTile({
|
||||
Key? key,
|
||||
required this.title,
|
||||
this.subtitle,
|
||||
this.subtitleWidget,
|
||||
this.onTap,
|
||||
this.icon,
|
||||
this.trailing,
|
||||
this.padding,
|
||||
this.onLongPress,
|
||||
this.disabled,
|
||||
this.onHover,
|
||||
this.color,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: color ?? Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
onHover: onHover,
|
||||
onLongPress: onLongPress,
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (icon != null) ...[
|
||||
Icon(
|
||||
icon,
|
||||
size: 24,
|
||||
color: disabled == true
|
||||
? Theme.of(context).colorScheme.onSurface.withOpacity(0.38)
|
||||
: Theme.of(context).listTileTheme.iconColor,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
],
|
||||
Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: disabled == true
|
||||
? Theme.of(context).colorScheme.onSurface.withOpacity(0.38)
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
if (subtitle != null || subtitleWidget != null) ...[
|
||||
const SizedBox(height: 5),
|
||||
if (subtitle == null && subtitleWidget != null) subtitleWidget!,
|
||||
if (subtitle != null && subtitleWidget == null)
|
||||
Text(
|
||||
subtitle!,
|
||||
style: TextStyle(
|
||||
color: disabled == true
|
||||
? Theme.of(context).colorScheme.onSurfaceVariant.withOpacity(0.38)
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
if (trailing != null) ...[const SizedBox(width: 16), trailing!]
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
89
lib/widgets/custom_switch_list_tile.dart
Normal file
89
lib/widgets/custom_switch_list_tile.dart
Normal file
@@ -0,0 +1,89 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomSwitchListTile extends StatelessWidget {
|
||||
final bool value;
|
||||
final void Function(bool) onChanged;
|
||||
final String title;
|
||||
final String? subtitle;
|
||||
final bool? disabled;
|
||||
final EdgeInsets? padding;
|
||||
final IconData? leadingIcon;
|
||||
|
||||
const CustomSwitchListTile({
|
||||
super.key,
|
||||
required this.value,
|
||||
required this.onChanged,
|
||||
required this.title,
|
||||
this.disabled,
|
||||
this.subtitle,
|
||||
this.padding,
|
||||
this.leadingIcon,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: disabled != null && disabled == true
|
||||
? null
|
||||
: () => onChanged(!value),
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.only(
|
||||
top: 12, left: 16, right: 18, bottom: 16
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
if (leadingIcon != null) ...[
|
||||
Icon(
|
||||
leadingIcon,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
],
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: disabled != null && disabled == true
|
||||
? Theme.of(context).colorScheme.onSurface.withOpacity(0.38)
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
if (subtitle != null) ... [
|
||||
const SizedBox(height: 5),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width-110,
|
||||
child: Text(
|
||||
subtitle!,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: disabled != null && disabled == true
|
||||
? Theme.of(context).listTileTheme.textColor!.withOpacity(0.38)
|
||||
: Theme.of(context).listTileTheme.textColor
|
||||
),
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Switch(
|
||||
value: value,
|
||||
onChanged: disabled != null && disabled == true
|
||||
? null
|
||||
: onChanged,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user