mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2024-11-09 18:33:11 +00:00
feat: introduced app_controller, rehooked dependencies from app_settings_cubit, added language picker to settings_page
This commit is contained in:
parent
0ad15061a3
commit
ea2cc28ac9
183
lib/config/app_controller/app_controller.dart
Normal file
183
lib/config/app_controller/app_controller.dart
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:material_color_utilities/material_color_utilities.dart'
|
||||||
|
as color_utils;
|
||||||
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
|
import 'package:selfprivacy/config/preferences_repository/preferences_repository.dart';
|
||||||
|
|
||||||
|
/// A class that many Widgets can interact with to read current app
|
||||||
|
/// configuration, update it, or listen to its changes.
|
||||||
|
///
|
||||||
|
/// AppController uses repo to change persistent data.
|
||||||
|
class AppController with ChangeNotifier {
|
||||||
|
AppController(this._repo);
|
||||||
|
|
||||||
|
/// repo encapsulates retrieval and storage of preferences
|
||||||
|
final PreferencesRepository _repo;
|
||||||
|
|
||||||
|
/// TODO: to be removed or changed
|
||||||
|
late final ApiConfigModel _apiConfigModel = getIt.get<ApiConfigModel>();
|
||||||
|
|
||||||
|
bool _loaded = false;
|
||||||
|
bool get loaded => _loaded;
|
||||||
|
|
||||||
|
// localization
|
||||||
|
late Locale _locale;
|
||||||
|
Locale get locale => _locale;
|
||||||
|
late List<Locale> _supportedLocales;
|
||||||
|
List<Locale> get supportedLocales => _supportedLocales;
|
||||||
|
|
||||||
|
// theme
|
||||||
|
late ThemeData _lightTheme;
|
||||||
|
ThemeData get lightTheme => _lightTheme;
|
||||||
|
late ThemeData _darkTheme;
|
||||||
|
ThemeData get darkTheme => _darkTheme;
|
||||||
|
late color_utils.CorePalette _corePalette;
|
||||||
|
color_utils.CorePalette get corePalette => _corePalette;
|
||||||
|
|
||||||
|
late bool _systemThemeModeActive;
|
||||||
|
bool get systemThemeModeActive => _systemThemeModeActive;
|
||||||
|
|
||||||
|
late bool _darkThemeModeActive;
|
||||||
|
bool get darkThemeModeActive => _darkThemeModeActive;
|
||||||
|
|
||||||
|
ThemeMode get themeMode => systemThemeModeActive
|
||||||
|
? ThemeMode.system
|
||||||
|
: darkThemeModeActive
|
||||||
|
? ThemeMode.dark
|
||||||
|
: ThemeMode.light;
|
||||||
|
// // Make ThemeMode a private variable so it is not updated directly without
|
||||||
|
// // also persisting the changes with the repo..
|
||||||
|
// late ThemeMode _themeMode;
|
||||||
|
// // Allow Widgets to read the user's preferred ThemeMode.
|
||||||
|
// ThemeMode get themeMode => _themeMode;
|
||||||
|
|
||||||
|
late bool _shouldShowOnboarding;
|
||||||
|
bool get shouldShowOnboarding => _shouldShowOnboarding;
|
||||||
|
|
||||||
|
/// Load the user's settings from the SettingsService. It may load from a
|
||||||
|
/// local database or the internet. The controller only knows it can load the
|
||||||
|
/// settings from the service.
|
||||||
|
Future<void> init({
|
||||||
|
// required final AppPreferencesRepository repo,
|
||||||
|
required final ThemeData lightThemeData,
|
||||||
|
required final ThemeData darkThemeData,
|
||||||
|
required final color_utils.CorePalette colorPalette,
|
||||||
|
}) async {
|
||||||
|
// _repo = repo;
|
||||||
|
|
||||||
|
await Future.wait(<Future>[
|
||||||
|
// load locale
|
||||||
|
() async {
|
||||||
|
_supportedLocales = await _repo.getSupportedLocales();
|
||||||
|
|
||||||
|
_locale = await _repo.getActiveLocale();
|
||||||
|
// preset value to other state holders
|
||||||
|
await _apiConfigModel.setLocaleCode(_locale.languageCode);
|
||||||
|
await _repo.setDelegateLocale(_locale);
|
||||||
|
}(),
|
||||||
|
|
||||||
|
// load theme mode && initialize theme
|
||||||
|
() async {
|
||||||
|
_lightTheme = lightThemeData;
|
||||||
|
_darkTheme = darkThemeData;
|
||||||
|
_corePalette = colorPalette;
|
||||||
|
// _themeMode = await _repo.getThemeMode();
|
||||||
|
_darkThemeModeActive = await _repo.getDarkThemeModeFlag();
|
||||||
|
_systemThemeModeActive = await _repo.getSystemThemeModeFlag();
|
||||||
|
}(),
|
||||||
|
|
||||||
|
// load onboarding flag
|
||||||
|
() async {
|
||||||
|
_shouldShowOnboarding = await _repo.getShouldShowOnboarding();
|
||||||
|
}(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
_loaded = true;
|
||||||
|
// Important! Inform listeners a change has occurred.
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateRepoReference
|
||||||
|
|
||||||
|
Future<void> setShouldShowOnboarding(final bool newValue) async {
|
||||||
|
// Do not perform any work if new and old flag values are identical
|
||||||
|
if (newValue == shouldShowOnboarding) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the flag in memory
|
||||||
|
_shouldShowOnboarding = newValue;
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
// Persist the change
|
||||||
|
await _repo.setShouldShowOnboarding(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setSystemThemeModeFlag(final bool newValue) async {
|
||||||
|
// Do not perform any work if new and old ThemeMode are identical
|
||||||
|
if (systemThemeModeActive == newValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the new ThemeMode in memory
|
||||||
|
_systemThemeModeActive = newValue;
|
||||||
|
|
||||||
|
// Inform listeners a change has occurred.
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
// Persist the change
|
||||||
|
await _repo.setSystemModeFlag(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setDarkThemeModeFlag(final bool newValue) async {
|
||||||
|
// Do not perform any work if new and old ThemeMode are identical
|
||||||
|
if (darkThemeModeActive == newValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the new ThemeMode in memory
|
||||||
|
_darkThemeModeActive = newValue;
|
||||||
|
|
||||||
|
// Inform listeners a change has occurred.
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
// Persist the change
|
||||||
|
await _repo.setDarkThemeModeFlag(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// /// Update and persist the ThemeMode based on the user's selection.
|
||||||
|
// Future<void> setThemeMode(final ThemeMode newThemeMode) async {
|
||||||
|
// // Do not perform any work if new and old ThemeMode are identical
|
||||||
|
// if (newThemeMode == themeMode) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Store the new ThemeMode in memory
|
||||||
|
// _themeMode = newThemeMode;
|
||||||
|
|
||||||
|
// // Inform listeners a change has occurred.
|
||||||
|
// notifyListeners();
|
||||||
|
|
||||||
|
// // Persist the change
|
||||||
|
// await _repo.setThemeMode(newThemeMode);
|
||||||
|
// }
|
||||||
|
|
||||||
|
Future<void> setLocale(final Locale newLocale) async {
|
||||||
|
// Do not perform any work if new and old Locales are identical
|
||||||
|
if (newLocale == _locale) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the new Locale in memory
|
||||||
|
_locale = newLocale;
|
||||||
|
|
||||||
|
/// update locale delegate, which in turn should update deps
|
||||||
|
await _repo.setDelegateLocale(newLocale);
|
||||||
|
|
||||||
|
// Persist the change
|
||||||
|
await _repo.setActiveLocale(newLocale);
|
||||||
|
// Update other locale holders
|
||||||
|
await _apiConfigModel.setLocaleCode(newLocale.languageCode);
|
||||||
|
}
|
||||||
|
}
|
106
lib/config/app_controller/inherited_app_controller.dart
Normal file
106
lib/config/app_controller/inherited_app_controller.dart
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:material_color_utilities/material_color_utilities.dart'
|
||||||
|
as color_utils;
|
||||||
|
import 'package:selfprivacy/config/app_controller/app_controller.dart';
|
||||||
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
import 'package:selfprivacy/config/preferences_repository/inherited_preferences_repository.dart';
|
||||||
|
import 'package:selfprivacy/config/preferences_repository/preferences_repository.dart';
|
||||||
|
import 'package:selfprivacy/theming/factory/app_theme_factory.dart';
|
||||||
|
|
||||||
|
class _AppControllerInjector extends InheritedNotifier<AppController> {
|
||||||
|
const _AppControllerInjector({
|
||||||
|
required super.child,
|
||||||
|
required super.notifier,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class InheritedAppController extends StatefulWidget {
|
||||||
|
const InheritedAppController({
|
||||||
|
required this.child,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<InheritedAppController> createState() => _InheritedAppControllerState();
|
||||||
|
|
||||||
|
static AppController of(final BuildContext context) => context
|
||||||
|
.dependOnInheritedWidgetOfExactType<_AppControllerInjector>()!
|
||||||
|
.notifier!;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InheritedAppControllerState extends State<InheritedAppController> {
|
||||||
|
// actual state provider
|
||||||
|
late AppController controller;
|
||||||
|
// hold local reference to active repo
|
||||||
|
late PreferencesRepository _repo;
|
||||||
|
|
||||||
|
bool initTriggerred = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
/// update reference on dependency change
|
||||||
|
_repo = InheritedPreferencesRepository.of(context)!;
|
||||||
|
|
||||||
|
if (!initTriggerred) {
|
||||||
|
/// hook controller repo to local reference
|
||||||
|
controller = AppController(_repo);
|
||||||
|
initialize();
|
||||||
|
initTriggerred = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.didChangeDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> initialize() async {
|
||||||
|
late final ThemeData lightThemeData;
|
||||||
|
late final ThemeData darkThemeData;
|
||||||
|
late final color_utils.CorePalette colorPalette;
|
||||||
|
|
||||||
|
await Future.wait(
|
||||||
|
<Future<void>>[
|
||||||
|
() async {
|
||||||
|
lightThemeData = await AppThemeFactory.create(
|
||||||
|
isDark: false,
|
||||||
|
fallbackColor: BrandColors.primary,
|
||||||
|
);
|
||||||
|
}(),
|
||||||
|
() async {
|
||||||
|
darkThemeData = await AppThemeFactory.create(
|
||||||
|
isDark: true,
|
||||||
|
fallbackColor: BrandColors.primary,
|
||||||
|
);
|
||||||
|
}(),
|
||||||
|
() async {
|
||||||
|
colorPalette = (await AppThemeFactory.getCorePalette()) ??
|
||||||
|
color_utils.CorePalette.of(BrandColors.primary.value);
|
||||||
|
}(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
await controller.init(
|
||||||
|
colorPalette: colorPalette,
|
||||||
|
lightThemeData: lightThemeData,
|
||||||
|
darkThemeData: darkThemeData,
|
||||||
|
);
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((final _) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => _AppControllerInjector(
|
||||||
|
notifier: controller,
|
||||||
|
child: widget.child,
|
||||||
|
);
|
||||||
|
}
|
|
@ -8,7 +8,6 @@ import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
|
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/bloc/users/users_bloc.dart';
|
import 'package:selfprivacy/logic/bloc/users/users_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/bloc/volumes/volumes_bloc.dart';
|
import 'package:selfprivacy/logic/bloc/volumes/volumes_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
|
||||||
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart';
|
||||||
|
@ -56,58 +55,46 @@ class BlocAndProviderConfigState extends State<BlocAndProviderConfig> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) {
|
Widget build(final BuildContext context) => MultiProvider(
|
||||||
const isDark = false;
|
providers: [
|
||||||
const isAutoDark = true;
|
BlocProvider(
|
||||||
|
create: (final _) => supportSystemCubit,
|
||||||
return MultiProvider(
|
),
|
||||||
providers: [
|
BlocProvider(
|
||||||
BlocProvider(
|
create: (final _) => serverInstallationCubit,
|
||||||
create: (final _) => AppSettingsCubit(
|
lazy: false,
|
||||||
isDarkModeOn: isDark,
|
),
|
||||||
isAutoDarkModeOn: isAutoDark,
|
BlocProvider(
|
||||||
isOnboardingShowing: true,
|
create: (final _) => usersBloc,
|
||||||
)..load(),
|
lazy: false,
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (final _) => supportSystemCubit,
|
create: (final _) => servicesBloc,
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (final _) => serverInstallationCubit,
|
create: (final _) => backupsBloc,
|
||||||
lazy: false,
|
),
|
||||||
),
|
BlocProvider(
|
||||||
BlocProvider(
|
create: (final _) => dnsRecordsCubit,
|
||||||
create: (final _) => usersBloc,
|
),
|
||||||
lazy: false,
|
BlocProvider(
|
||||||
),
|
create: (final _) => recoveryKeyBloc,
|
||||||
BlocProvider(
|
),
|
||||||
create: (final _) => servicesBloc,
|
BlocProvider(
|
||||||
),
|
create: (final _) => devicesBloc,
|
||||||
BlocProvider(
|
),
|
||||||
create: (final _) => backupsBloc,
|
BlocProvider(
|
||||||
),
|
create: (final _) => serverJobsBloc,
|
||||||
BlocProvider(
|
),
|
||||||
create: (final _) => dnsRecordsCubit,
|
BlocProvider(create: (final _) => connectionStatusBloc),
|
||||||
),
|
BlocProvider(
|
||||||
BlocProvider(
|
create: (final _) => serverDetailsCubit,
|
||||||
create: (final _) => recoveryKeyBloc,
|
),
|
||||||
),
|
BlocProvider(create: (final _) => volumesBloc),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (final _) => devicesBloc,
|
create: (final _) => JobsCubit(),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
],
|
||||||
create: (final _) => serverJobsBloc,
|
child: widget.child,
|
||||||
),
|
);
|
||||||
BlocProvider(create: (final _) => connectionStatusBloc),
|
|
||||||
BlocProvider(
|
|
||||||
create: (final _) => serverDetailsCubit,
|
|
||||||
),
|
|
||||||
BlocProvider(create: (final _) => volumesBloc),
|
|
||||||
BlocProvider(
|
|
||||||
create: (final _) => JobsCubit(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: widget.child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,17 +60,20 @@ class HiveConfig {
|
||||||
|
|
||||||
/// Mappings for the different boxes and their keys
|
/// Mappings for the different boxes and their keys
|
||||||
class BNames {
|
class BNames {
|
||||||
/// App settings box. Contains app settings like [isDarkModeOn], [isOnboardingShowing]
|
/// App settings box. Contains app settings like [darkThemeModeOn], [shouldShowOnboarding]
|
||||||
static String appSettingsBox = 'appSettings';
|
static String appSettingsBox = 'appSettings';
|
||||||
|
|
||||||
/// A boolean field of [appSettingsBox] box.
|
/// A boolean field of [appSettingsBox] box.
|
||||||
static String isDarkModeOn = 'isDarkModeOn';
|
static String darkThemeModeOn = 'isDarkModeOn';
|
||||||
|
|
||||||
/// A boolean field of [appSettingsBox] box.
|
/// A boolean field of [appSettingsBox] box.
|
||||||
static String isAutoDarkModeOn = 'isAutoDarkModeOn';
|
static String systemThemeModeOn = 'isAutoDarkModeOn';
|
||||||
|
|
||||||
/// A boolean field of [appSettingsBox] box.
|
/// A boolean field of [appSettingsBox] box.
|
||||||
static String isOnboardingShowing = 'isOnboardingShowing';
|
static String shouldShowOnboarding = 'isOnboardingShowing';
|
||||||
|
|
||||||
|
/// A string field
|
||||||
|
static String appLocale = 'appLocale';
|
||||||
|
|
||||||
/// Encryption key to decrypt [serverInstallationBox] and [usersBox] box.
|
/// Encryption key to decrypt [serverInstallationBox] and [usersBox] box.
|
||||||
static String serverInstallationEncryptionKey = 'key';
|
static String serverInstallationEncryptionKey = 'key';
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/// abstraction for manipulation of stored app preferences
|
||||||
|
abstract class PreferencesDataSource {
|
||||||
|
/// should onboarding be shown
|
||||||
|
Future<bool> getOnboardingFlag();
|
||||||
|
|
||||||
|
/// should onboarding be shown
|
||||||
|
Future<void> setOnboardingFlag(final bool newValue);
|
||||||
|
|
||||||
|
// TODO: should probably deprecate the following, instead add the
|
||||||
|
// getThemeMode and setThemeMode methods, which store one value instead of
|
||||||
|
// flags.
|
||||||
|
|
||||||
|
/// should system theme mode be enabled
|
||||||
|
Future<bool?> getSystemThemeModeFlag();
|
||||||
|
|
||||||
|
/// should system theme mode be enabled
|
||||||
|
Future<void> setSystemThemeModeFlag(final bool newValue);
|
||||||
|
|
||||||
|
/// should dark theme be enabled
|
||||||
|
Future<bool?> getDarkThemeModeFlag();
|
||||||
|
|
||||||
|
/// should dark theme be enabled
|
||||||
|
Future<void> setDarkThemeModeFlag(final bool newValue);
|
||||||
|
|
||||||
|
/// locale, as set by user
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// when null, app takes system locale
|
||||||
|
Future<String?> getLocale();
|
||||||
|
|
||||||
|
/// locale, as set by user
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// when null, app takes system locale
|
||||||
|
Future<void> setLocale(final String newLocale);
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:selfprivacy/config/hive_config.dart';
|
||||||
|
import 'package:selfprivacy/config/preferences_repository/datasources/preferences_datasource.dart';
|
||||||
|
|
||||||
|
/// app preferences data source hive implementation
|
||||||
|
class PreferencesHiveDataSource implements PreferencesDataSource {
|
||||||
|
final Box _appSettingsBox = Hive.box(BNames.appSettingsBox);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> getOnboardingFlag() async =>
|
||||||
|
_appSettingsBox.get(BNames.shouldShowOnboarding, defaultValue: true);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setOnboardingFlag(final bool newValue) async =>
|
||||||
|
_appSettingsBox.put(BNames.shouldShowOnboarding, newValue);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool?> getSystemThemeModeFlag() async =>
|
||||||
|
_appSettingsBox.get(BNames.systemThemeModeOn);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setSystemThemeModeFlag(final bool newValue) async =>
|
||||||
|
_appSettingsBox.put(BNames.systemThemeModeOn, newValue);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool?> getDarkThemeModeFlag() async =>
|
||||||
|
_appSettingsBox.get(BNames.darkThemeModeOn);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setDarkThemeModeFlag(final bool newValue) async =>
|
||||||
|
_appSettingsBox.put(BNames.darkThemeModeOn, newValue);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> getLocale() async => _appSettingsBox.get(BNames.appLocale);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setLocale(final String newLocale) async =>
|
||||||
|
_appSettingsBox.put(BNames.appLocale, newLocale);
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:selfprivacy/config/preferences_repository/datasources/preferences_datasource.dart';
|
||||||
|
import 'package:selfprivacy/config/preferences_repository/preferences_repository.dart';
|
||||||
|
|
||||||
|
class _PreferencesRepositoryInjector extends InheritedWidget {
|
||||||
|
const _PreferencesRepositoryInjector({
|
||||||
|
required this.settingsRepository,
|
||||||
|
required super.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
final PreferencesRepository settingsRepository;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateShouldNotify(
|
||||||
|
covariant final _PreferencesRepositoryInjector oldWidget,
|
||||||
|
) =>
|
||||||
|
oldWidget.settingsRepository != settingsRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates and injects app preferences repository inside widget tree.
|
||||||
|
class InheritedPreferencesRepository extends StatefulWidget {
|
||||||
|
const InheritedPreferencesRepository({
|
||||||
|
required this.child,
|
||||||
|
required this.dataSource,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final PreferencesDataSource dataSource;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<InheritedPreferencesRepository> createState() =>
|
||||||
|
_InheritedPreferencesRepositoryState();
|
||||||
|
|
||||||
|
static PreferencesRepository? of(final BuildContext context) => context
|
||||||
|
.dependOnInheritedWidgetOfExactType<_PreferencesRepositoryInjector>()
|
||||||
|
?.settingsRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InheritedPreferencesRepositoryState
|
||||||
|
extends State<InheritedPreferencesRepository> {
|
||||||
|
late PreferencesRepository repo;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
|
||||||
|
/// recreate repo each time dependencies change
|
||||||
|
repo = PreferencesRepository(
|
||||||
|
dataSource: widget.dataSource,
|
||||||
|
setDelegateLocale: EasyLocalization.of(context)!.setLocale,
|
||||||
|
getDelegateLocale: () => EasyLocalization.of(context)!.locale,
|
||||||
|
getSupportedLocales: () => EasyLocalization.of(context)!.supportedLocales,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => _PreferencesRepositoryInjector(
|
||||||
|
settingsRepository: repo,
|
||||||
|
child: widget.child,
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:selfprivacy/config/preferences_repository/datasources/preferences_datasource.dart';
|
||||||
|
|
||||||
|
class PreferencesRepository {
|
||||||
|
const PreferencesRepository({
|
||||||
|
required this.dataSource,
|
||||||
|
required this.getSupportedLocales,
|
||||||
|
required this.getDelegateLocale,
|
||||||
|
required this.setDelegateLocale,
|
||||||
|
});
|
||||||
|
|
||||||
|
final PreferencesDataSource dataSource;
|
||||||
|
|
||||||
|
/// easy localizations don't expose type of localization provider,
|
||||||
|
/// so it needs to be this crutchy (I could've created one more class-wrapper,
|
||||||
|
/// containing needed functions, but perceive it as boilerplate, because we
|
||||||
|
/// don't need additional encapsulation level here)
|
||||||
|
final FutureOr<void> Function(Locale) setDelegateLocale;
|
||||||
|
final FutureOr<List<Locale>> Function() getSupportedLocales;
|
||||||
|
final FutureOr<Locale> Function() getDelegateLocale;
|
||||||
|
|
||||||
|
Future<bool> getSystemThemeModeFlag() async =>
|
||||||
|
(await dataSource.getSystemThemeModeFlag()) ?? true;
|
||||||
|
|
||||||
|
Future<void> setSystemThemeModeFlag(final bool newValue) async =>
|
||||||
|
dataSource.setSystemThemeModeFlag(newValue);
|
||||||
|
|
||||||
|
Future<bool> getDarkThemeModeFlag() async =>
|
||||||
|
(await dataSource.getDarkThemeModeFlag()) ?? false;
|
||||||
|
|
||||||
|
Future<void> setDarkThemeModeFlag(final bool newValue) async =>
|
||||||
|
dataSource.setDarkThemeModeFlag(newValue);
|
||||||
|
|
||||||
|
Future<void> setSystemModeFlag(final bool newValue) async =>
|
||||||
|
dataSource.setSystemThemeModeFlag(newValue);
|
||||||
|
|
||||||
|
// Future<ThemeMode> getThemeMode() async {
|
||||||
|
// final themeMode = await dataSource.getThemeMode()?? ThemeMode.system;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Future<void> setThemeMode(final ThemeMode newThemeMode) =>
|
||||||
|
// dataSource.setThemeMode(newThemeMode);
|
||||||
|
|
||||||
|
Future<List<Locale>> supportedLocales() async => getSupportedLocales();
|
||||||
|
|
||||||
|
Future<Locale> getActiveLocale() async {
|
||||||
|
Locale? chosenLocale;
|
||||||
|
|
||||||
|
final String? storedLocaleCode = await dataSource.getLocale();
|
||||||
|
if (storedLocaleCode != null) {
|
||||||
|
chosenLocale = Locale(storedLocaleCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// when it's null fallback on delegate locale
|
||||||
|
chosenLocale ??= await getDelegateLocale();
|
||||||
|
|
||||||
|
return chosenLocale;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setActiveLocale(final Locale newLocale) async {
|
||||||
|
await dataSource.setLocale(newLocale.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// true when we need to show onboarding
|
||||||
|
Future<bool> getShouldShowOnboarding() async =>
|
||||||
|
dataSource.getOnboardingFlag();
|
||||||
|
|
||||||
|
/// true when we need to show onboarding
|
||||||
|
Future<void> setShouldShowOnboarding(final bool newValue) =>
|
||||||
|
dataSource.setOnboardingFlag(newValue);
|
||||||
|
}
|
|
@ -1,66 +0,0 @@
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:hive/hive.dart';
|
|
||||||
import 'package:material_color_utilities/material_color_utilities.dart'
|
|
||||||
as color_utils;
|
|
||||||
import 'package:selfprivacy/config/brand_colors.dart';
|
|
||||||
import 'package:selfprivacy/config/hive_config.dart';
|
|
||||||
import 'package:selfprivacy/theming/factory/app_theme_factory.dart';
|
|
||||||
|
|
||||||
export 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
part 'app_settings_state.dart';
|
|
||||||
|
|
||||||
class AppSettingsCubit extends Cubit<AppSettingsState> {
|
|
||||||
AppSettingsCubit({
|
|
||||||
required final bool isDarkModeOn,
|
|
||||||
required final bool isAutoDarkModeOn,
|
|
||||||
required final bool isOnboardingShowing,
|
|
||||||
}) : super(
|
|
||||||
AppSettingsState(
|
|
||||||
isDarkModeOn: isDarkModeOn,
|
|
||||||
isAutoDarkModeOn: isAutoDarkModeOn,
|
|
||||||
isOnboardingShowing: isOnboardingShowing,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Box box = Hive.box(BNames.appSettingsBox);
|
|
||||||
|
|
||||||
void load() async {
|
|
||||||
final bool? isDarkModeOn = box.get(BNames.isDarkModeOn);
|
|
||||||
final bool? isAutoDarkModeOn = box.get(BNames.isAutoDarkModeOn);
|
|
||||||
final bool? isOnboardingShowing = box.get(BNames.isOnboardingShowing);
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
isDarkModeOn: isDarkModeOn,
|
|
||||||
isAutoDarkModeOn: isAutoDarkModeOn,
|
|
||||||
isOnboardingShowing: isOnboardingShowing,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
|
||||||
final color_utils.CorePalette? colorPalette =
|
|
||||||
await AppThemeFactory.getCorePalette();
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
corePalette: colorPalette,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateDarkMode({required final bool isDarkModeOn}) {
|
|
||||||
box.put(BNames.isDarkModeOn, isDarkModeOn);
|
|
||||||
emit(state.copyWith(isDarkModeOn: isDarkModeOn));
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateAutoDarkMode({required final bool isAutoDarkModeOn}) {
|
|
||||||
box.put(BNames.isAutoDarkModeOn, isAutoDarkModeOn);
|
|
||||||
emit(state.copyWith(isAutoDarkModeOn: isAutoDarkModeOn));
|
|
||||||
}
|
|
||||||
|
|
||||||
void turnOffOnboarding({final bool isOnboardingShowing = false}) {
|
|
||||||
box.put(BNames.isOnboardingShowing, isOnboardingShowing);
|
|
||||||
|
|
||||||
emit(state.copyWith(isOnboardingShowing: isOnboardingShowing));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
part of 'app_settings_cubit.dart';
|
|
||||||
|
|
||||||
class AppSettingsState extends Equatable {
|
|
||||||
const AppSettingsState({
|
|
||||||
required this.isDarkModeOn,
|
|
||||||
required this.isAutoDarkModeOn,
|
|
||||||
required this.isOnboardingShowing,
|
|
||||||
this.corePalette,
|
|
||||||
});
|
|
||||||
|
|
||||||
final bool isDarkModeOn;
|
|
||||||
final bool isAutoDarkModeOn;
|
|
||||||
final bool isOnboardingShowing;
|
|
||||||
final color_utils.CorePalette? corePalette;
|
|
||||||
|
|
||||||
AppSettingsState copyWith({
|
|
||||||
final bool? isDarkModeOn,
|
|
||||||
final bool? isAutoDarkModeOn,
|
|
||||||
final bool? isOnboardingShowing,
|
|
||||||
final color_utils.CorePalette? corePalette,
|
|
||||||
}) =>
|
|
||||||
AppSettingsState(
|
|
||||||
isDarkModeOn: isDarkModeOn ?? this.isDarkModeOn,
|
|
||||||
isAutoDarkModeOn: isAutoDarkModeOn ?? this.isAutoDarkModeOn,
|
|
||||||
isOnboardingShowing: isOnboardingShowing ?? this.isOnboardingShowing,
|
|
||||||
corePalette: corePalette ?? this.corePalette,
|
|
||||||
);
|
|
||||||
|
|
||||||
color_utils.CorePalette get corePaletteOrDefault =>
|
|
||||||
corePalette ?? color_utils.CorePalette.of(BrandColors.primary.value);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<dynamic> get props =>
|
|
||||||
[isDarkModeOn, isAutoDarkModeOn, isOnboardingShowing, corePalette];
|
|
||||||
}
|
|
165
lib/main.dart
165
lib/main.dart
|
@ -1,21 +1,19 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:selfprivacy/config/app_controller/inherited_app_controller.dart';
|
||||||
import 'package:selfprivacy/config/bloc_config.dart';
|
import 'package:selfprivacy/config/bloc_config.dart';
|
||||||
import 'package:selfprivacy/config/bloc_observer.dart';
|
import 'package:selfprivacy/config/bloc_observer.dart';
|
||||||
import 'package:selfprivacy/config/brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/config/hive_config.dart';
|
import 'package:selfprivacy/config/hive_config.dart';
|
||||||
import 'package:selfprivacy/config/localization.dart';
|
import 'package:selfprivacy/config/localization.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
import 'package:selfprivacy/config/preferences_repository/datasources/preferences_hive_datasource.dart';
|
||||||
import 'package:selfprivacy/theming/factory/app_theme_factory.dart';
|
import 'package:selfprivacy/config/preferences_repository/inherited_preferences_repository.dart';
|
||||||
import 'package:selfprivacy/ui/router/router.dart';
|
import 'package:selfprivacy/ui/router/router.dart';
|
||||||
// import 'package:wakelock/wakelock.dart';
|
|
||||||
import 'package:timezone/data/latest.dart' as tz;
|
import 'package:timezone/data/latest.dart' as tz;
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
|
||||||
await HiveConfig.init();
|
|
||||||
// await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
// await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||||
|
|
||||||
// try {
|
// try {
|
||||||
|
@ -26,86 +24,111 @@ void main() async {
|
||||||
// print(e);
|
// print(e);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
await Future.wait(
|
||||||
|
<Future<void>>[
|
||||||
|
HiveConfig.init(),
|
||||||
|
EasyLocalization.ensureInitialized(),
|
||||||
|
],
|
||||||
|
);
|
||||||
await getItSetup();
|
await getItSetup();
|
||||||
await EasyLocalization.ensureInitialized();
|
|
||||||
tz.initializeTimeZones();
|
|
||||||
|
|
||||||
final ThemeData lightThemeData = await AppThemeFactory.create(
|
tz.initializeTimeZones();
|
||||||
isDark: false,
|
|
||||||
fallbackColor: BrandColors.primary,
|
|
||||||
);
|
|
||||||
final ThemeData darkThemeData = await AppThemeFactory.create(
|
|
||||||
isDark: true,
|
|
||||||
fallbackColor: BrandColors.primary,
|
|
||||||
);
|
|
||||||
|
|
||||||
Bloc.observer = SimpleBlocObserver();
|
Bloc.observer = SimpleBlocObserver();
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
SelfprivacyApp(
|
Localization(
|
||||||
lightThemeData: lightThemeData,
|
child: InheritedPreferencesRepository(
|
||||||
darkThemeData: darkThemeData,
|
dataSource: PreferencesHiveDataSource(),
|
||||||
|
child: const InheritedAppController(
|
||||||
|
child: AppBuilder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SelfprivacyApp extends StatelessWidget {
|
class AppBuilder extends StatelessWidget {
|
||||||
SelfprivacyApp({
|
const AppBuilder({super.key});
|
||||||
required this.lightThemeData,
|
|
||||||
required this.darkThemeData,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
final ThemeData lightThemeData;
|
|
||||||
final ThemeData darkThemeData;
|
|
||||||
|
|
||||||
final _appRouter = RootRouter(getIt.get<NavigationService>().navigatorKey);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) => Localization(
|
Widget build(final BuildContext context) {
|
||||||
child: BlocAndProviderConfig(
|
final appController = InheritedAppController.of(context);
|
||||||
child: BlocBuilder<AppSettingsCubit, AppSettingsState>(
|
|
||||||
builder: (
|
|
||||||
final BuildContext context,
|
|
||||||
final AppSettingsState appSettings,
|
|
||||||
) {
|
|
||||||
getIt.get<ApiConfigModel>().setLocaleCode(
|
|
||||||
context.locale.languageCode,
|
|
||||||
);
|
|
||||||
return MaterialApp.router(
|
|
||||||
routeInformationParser: _appRouter.defaultRouteParser(),
|
|
||||||
routerDelegate: _appRouter.delegate(),
|
|
||||||
scaffoldMessengerKey:
|
|
||||||
getIt.get<NavigationService>().scaffoldMessengerKey,
|
|
||||||
localizationsDelegates: context.localizationDelegates,
|
|
||||||
supportedLocales: context.supportedLocales,
|
|
||||||
locale: context.locale,
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
title: 'SelfPrivacy',
|
|
||||||
theme: lightThemeData,
|
|
||||||
darkTheme: darkThemeData,
|
|
||||||
themeMode: appSettings.isAutoDarkModeOn
|
|
||||||
? ThemeMode.system
|
|
||||||
: appSettings.isDarkModeOn
|
|
||||||
? ThemeMode.dark
|
|
||||||
: ThemeMode.light,
|
|
||||||
scrollBehavior: const MaterialScrollBehavior().copyWith(
|
|
||||||
scrollbars: false,
|
|
||||||
),
|
|
||||||
builder: (final BuildContext context, final Widget? widget) {
|
|
||||||
Widget error =
|
|
||||||
const Center(child: Text('...rendering error...'));
|
|
||||||
if (widget is Scaffold || widget is Navigator) {
|
|
||||||
error = Scaffold(body: error);
|
|
||||||
}
|
|
||||||
ErrorWidget.builder =
|
|
||||||
(final FlutterErrorDetails errorDetails) => error;
|
|
||||||
|
|
||||||
return widget ?? error;
|
if (appController.loaded) {
|
||||||
},
|
return const SelfprivacyApp();
|
||||||
);
|
}
|
||||||
},
|
|
||||||
|
return const SplashScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Widget to be shown
|
||||||
|
/// until essential app initialization is completed
|
||||||
|
class SplashScreen extends StatelessWidget {
|
||||||
|
const SplashScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => const ColoredBox(
|
||||||
|
color: Colors.white,
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator.adaptive(
|
||||||
|
valueColor: AlwaysStoppedAnimation(BrandColors.primary),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SelfprivacyApp extends StatefulWidget {
|
||||||
|
const SelfprivacyApp({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SelfprivacyApp> createState() => _SelfprivacyAppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SelfprivacyAppState extends State<SelfprivacyApp> {
|
||||||
|
final appKey = UniqueKey();
|
||||||
|
final _appRouter = RootRouter(getIt.get<NavigationService>().navigatorKey);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) {
|
||||||
|
final appController = InheritedAppController.of(context);
|
||||||
|
|
||||||
|
return BlocAndProviderConfig(
|
||||||
|
child: MaterialApp.router(
|
||||||
|
key: appKey,
|
||||||
|
title: 'SelfPrivacy',
|
||||||
|
// routing
|
||||||
|
routeInformationParser: _appRouter.defaultRouteParser(),
|
||||||
|
routerDelegate: _appRouter.delegate(),
|
||||||
|
scaffoldMessengerKey:
|
||||||
|
getIt.get<NavigationService>().scaffoldMessengerKey,
|
||||||
|
// localization settings
|
||||||
|
locale: context.locale,
|
||||||
|
supportedLocales: context.supportedLocales,
|
||||||
|
localizationsDelegates: context.localizationDelegates,
|
||||||
|
// theme settings
|
||||||
|
themeMode: appController.themeMode,
|
||||||
|
theme: appController.lightTheme,
|
||||||
|
darkTheme: appController.darkTheme,
|
||||||
|
// other preferences
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
scrollBehavior:
|
||||||
|
const MaterialScrollBehavior().copyWith(scrollbars: false),
|
||||||
|
builder: _builder,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _builder(final BuildContext context, final Widget? widget) {
|
||||||
|
Widget error = const Center(child: Text('...rendering error...'));
|
||||||
|
if (widget is Scaffold || widget is Navigator) {
|
||||||
|
error = Scaffold(body: error);
|
||||||
|
}
|
||||||
|
ErrorWidget.builder = (final FlutterErrorDetails errorDetails) => error;
|
||||||
|
|
||||||
|
return widget ?? error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:selfprivacy/config/app_controller/inherited_app_controller.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/ui/components/buttons/dialog_action_button.dart';
|
import 'package:selfprivacy/ui/components/buttons/dialog_action_button.dart';
|
||||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||||
|
import 'package:selfprivacy/ui/router/router.dart';
|
||||||
|
|
||||||
|
part 'language_picker.dart';
|
||||||
|
part 'reset_app_button.dart';
|
||||||
|
part 'theme_picker.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class AppSettingsPage extends StatefulWidget {
|
class AppSettingsPage extends StatefulWidget {
|
||||||
|
@ -16,82 +22,36 @@ class AppSettingsPage extends StatefulWidget {
|
||||||
|
|
||||||
class _AppSettingsPageState extends State<AppSettingsPage> {
|
class _AppSettingsPageState extends State<AppSettingsPage> {
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) {
|
Widget build(final BuildContext context) => BrandHeroScreen(
|
||||||
final bool isDarkModeOn =
|
hasBackButton: true,
|
||||||
context.watch<AppSettingsCubit>().state.isDarkModeOn;
|
hasFlashButton: false,
|
||||||
|
bodyPadding: const EdgeInsets.symmetric(
|
||||||
final bool isSystemDarkModeOn =
|
horizontal: 12,
|
||||||
context.watch<AppSettingsCubit>().state.isAutoDarkModeOn;
|
vertical: 16,
|
||||||
|
|
||||||
return BrandHeroScreen(
|
|
||||||
hasBackButton: true,
|
|
||||||
hasFlashButton: false,
|
|
||||||
bodyPadding: const EdgeInsets.symmetric(vertical: 16),
|
|
||||||
heroTitle: 'application_settings.title'.tr(),
|
|
||||||
children: [
|
|
||||||
SwitchListTile.adaptive(
|
|
||||||
title: Text('application_settings.system_dark_theme_title'.tr()),
|
|
||||||
subtitle:
|
|
||||||
Text('application_settings.system_dark_theme_description'.tr()),
|
|
||||||
value: isSystemDarkModeOn,
|
|
||||||
onChanged: (final value) => context
|
|
||||||
.read<AppSettingsCubit>()
|
|
||||||
.updateAutoDarkMode(isAutoDarkModeOn: !isSystemDarkModeOn),
|
|
||||||
),
|
),
|
||||||
SwitchListTile.adaptive(
|
heroTitle: 'application_settings.title'.tr(),
|
||||||
title: Text('application_settings.dark_theme_title'.tr()),
|
children: [
|
||||||
subtitle: Text('application_settings.dark_theme_description'.tr()),
|
_ThemePicker(
|
||||||
value: Theme.of(context).brightness == Brightness.dark,
|
key: ValueKey('theme_picker'.tr()),
|
||||||
onChanged: isSystemDarkModeOn
|
|
||||||
? null
|
|
||||||
: (final value) => context
|
|
||||||
.read<AppSettingsCubit>()
|
|
||||||
.updateDarkMode(isDarkModeOn: !isDarkModeOn),
|
|
||||||
),
|
|
||||||
const Divider(height: 0),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Text(
|
|
||||||
'application_settings.dangerous_settings'.tr(),
|
|
||||||
style: Theme.of(context).textTheme.labelLarge!.copyWith(
|
|
||||||
color: Theme.of(context).colorScheme.error,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
const Divider(height: 5, thickness: 0),
|
||||||
const _ResetAppTile(),
|
_LanguagePicker(
|
||||||
],
|
key: ValueKey('language_picker'.tr()),
|
||||||
);
|
),
|
||||||
}
|
const Divider(height: 5, thickness: 0),
|
||||||
}
|
const Gap(4),
|
||||||
|
Padding(
|
||||||
class _ResetAppTile extends StatelessWidget {
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
const _ResetAppTile();
|
child: Text(
|
||||||
|
'application_settings.dangerous_settings'.tr(),
|
||||||
@override
|
style: Theme.of(context).textTheme.titleLarge!.copyWith(
|
||||||
Widget build(final BuildContext context) => ListTile(
|
color: Theme.of(context).colorScheme.error,
|
||||||
title: Text('application_settings.reset_config_title'.tr()),
|
),
|
||||||
subtitle: Text('application_settings.reset_config_description'.tr()),
|
|
||||||
onTap: () {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (final _) => AlertDialog(
|
|
||||||
title: Text('modals.are_you_sure'.tr()),
|
|
||||||
content: Text('modals.purge_all_keys'.tr()),
|
|
||||||
actions: [
|
|
||||||
DialogActionButton(
|
|
||||||
text: 'modals.purge_all_keys_confirm'.tr(),
|
|
||||||
isRed: true,
|
|
||||||
onPressed: () {
|
|
||||||
context.read<ServerInstallationCubit>().clearAppConfig();
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
DialogActionButton(
|
|
||||||
text: 'basis.cancel'.tr(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
_ResetAppTile(
|
||||||
|
key: ValueKey('reset_app'.tr()),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:selfprivacy/config/app_controller/inherited_app_controller.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/tls_options.dart';
|
import 'package:selfprivacy/logic/api_maps/tls_options.dart';
|
||||||
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
|
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/bloc/volumes/volumes_bloc.dart';
|
import 'package:selfprivacy/logic/bloc/volumes/volumes_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
|
||||||
import 'package:selfprivacy/ui/components/list_tiles/section_title.dart';
|
import 'package:selfprivacy/ui/components/list_tiles/section_title.dart';
|
||||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||||
import 'package:selfprivacy/ui/router/router.dart';
|
import 'package:selfprivacy/ui/router/router.dart';
|
||||||
|
@ -60,17 +61,14 @@ class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
|
||||||
title: Text('developer_settings.reset_onboarding'.tr()),
|
title: Text('developer_settings.reset_onboarding'.tr()),
|
||||||
subtitle:
|
subtitle:
|
||||||
Text('developer_settings.reset_onboarding_description'.tr()),
|
Text('developer_settings.reset_onboarding_description'.tr()),
|
||||||
enabled:
|
enabled: !InheritedAppController.of(context).shouldShowOnboarding,
|
||||||
!context.watch<AppSettingsCubit>().state.isOnboardingShowing,
|
onTap: () => InheritedAppController.of(context)
|
||||||
onTap: () => context
|
.setShouldShowOnboarding(true),
|
||||||
.read<AppSettingsCubit>()
|
|
||||||
.turnOffOnboarding(isOnboardingShowing: true),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text('storage.start_migration_button'.tr()),
|
title: Text('storage.start_migration_button'.tr()),
|
||||||
subtitle: Text('storage.data_migration_notice'.tr()),
|
subtitle: Text('storage.data_migration_notice'.tr()),
|
||||||
enabled:
|
enabled: InheritedAppController.of(context).shouldShowOnboarding,
|
||||||
!context.watch<AppSettingsCubit>().state.isOnboardingShowing,
|
|
||||||
onTap: () => context.pushRoute(
|
onTap: () => context.pushRoute(
|
||||||
ServicesMigrationRoute(
|
ServicesMigrationRoute(
|
||||||
diskStatus: context.read<VolumesBloc>().state.diskStatus,
|
diskStatus: context.read<VolumesBloc>().state.diskStatus,
|
||||||
|
|
53
lib/ui/pages/more/app_settings/language_picker.dart
Normal file
53
lib/ui/pages/more/app_settings/language_picker.dart
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
part of 'app_settings.dart';
|
||||||
|
|
||||||
|
class _LanguagePicker extends StatelessWidget {
|
||||||
|
const _LanguagePicker({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => ListTile(
|
||||||
|
title: Text(
|
||||||
|
'application_settings.language'.tr(),
|
||||||
|
),
|
||||||
|
subtitle: Text('application_settings.click_to_change_locale'.tr()),
|
||||||
|
trailing: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
|
child: Text(
|
||||||
|
context.locale.toString(),
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
final appController = InheritedAppController.of(context);
|
||||||
|
final Locale? newLocale = await showDialog<Locale?>(
|
||||||
|
context: context,
|
||||||
|
builder: (final context) => const _LanguagePickerDialog(),
|
||||||
|
routeSettings: _LanguagePickerDialog.routeSettings,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (newLocale != null) {
|
||||||
|
await appController.setLocale(newLocale);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LanguagePickerDialog extends StatelessWidget {
|
||||||
|
const _LanguagePickerDialog();
|
||||||
|
static const routeSettings = RouteSettings(name: 'LanguagePickerDialog');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => SimpleDialog(
|
||||||
|
title: Text('application_settings.language'.tr()),
|
||||||
|
children: [
|
||||||
|
for (final locale
|
||||||
|
in InheritedAppController.of(context).supportedLocales)
|
||||||
|
ListTile(
|
||||||
|
// TODO: add locale to language name matcher
|
||||||
|
title: Text(locale.toString()),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop(locale);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
42
lib/ui/pages/more/app_settings/reset_app_button.dart
Normal file
42
lib/ui/pages/more/app_settings/reset_app_button.dart
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
part of 'app_settings.dart';
|
||||||
|
|
||||||
|
class _ResetAppTile extends StatelessWidget {
|
||||||
|
const _ResetAppTile({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => ListTile(
|
||||||
|
title: Text('application_settings.reset_config_title'.tr()),
|
||||||
|
subtitle: Text('application_settings.reset_config_description'.tr()),
|
||||||
|
onTap: () => showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (final context) => const _ResetAppDialog(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ResetAppDialog extends StatelessWidget {
|
||||||
|
const _ResetAppDialog();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => AlertDialog(
|
||||||
|
title: Text('modals.are_you_sure'.tr()),
|
||||||
|
content: Text('modals.purge_all_keys'.tr()),
|
||||||
|
actions: [
|
||||||
|
DialogActionButton(
|
||||||
|
text: 'modals.purge_all_keys_confirm'.tr(),
|
||||||
|
isRed: true,
|
||||||
|
onPressed: () {
|
||||||
|
context.read<ServerInstallationCubit>().clearAppConfig();
|
||||||
|
|
||||||
|
context.router.maybePop([
|
||||||
|
const RootRoute(),
|
||||||
|
]);
|
||||||
|
context.resetLocale();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
DialogActionButton(
|
||||||
|
text: 'basis.cancel'.tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
44
lib/ui/pages/more/app_settings/theme_picker.dart
Normal file
44
lib/ui/pages/more/app_settings/theme_picker.dart
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
part of 'app_settings.dart';
|
||||||
|
|
||||||
|
class _ThemePicker extends StatelessWidget {
|
||||||
|
const _ThemePicker({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) {
|
||||||
|
final appController = InheritedAppController.of(context);
|
||||||
|
// final themeMode = appController.themeMode;
|
||||||
|
// final bool isSystemThemeModeEnabled = themeMode == ThemeMode.system;
|
||||||
|
// final bool isDarkModeOn = themeMode == ThemeMode.dark;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
SwitchListTile.adaptive(
|
||||||
|
title: Text('application_settings.system_theme_mode_title'.tr()),
|
||||||
|
subtitle:
|
||||||
|
Text('application_settings.system_theme_mode_description'.tr()),
|
||||||
|
value: appController.systemThemeModeActive,
|
||||||
|
onChanged: appController.setSystemThemeModeFlag,
|
||||||
|
// onChanged: (final newValue) => appController.setThemeMode(
|
||||||
|
// newValue
|
||||||
|
// ? ThemeMode.system
|
||||||
|
// : (isDarkModeOn ? ThemeMode.dark : ThemeMode.light),
|
||||||
|
// ),
|
||||||
|
),
|
||||||
|
SwitchListTile.adaptive(
|
||||||
|
title: Text('application_settings.dark_theme_title'.tr()),
|
||||||
|
subtitle: Text('application_settings.change_application_theme'.tr()),
|
||||||
|
value: appController.darkThemeModeActive,
|
||||||
|
onChanged: appController.systemThemeModeActive
|
||||||
|
? null
|
||||||
|
: appController.setDarkThemeModeFlag,
|
||||||
|
// onChanged: isSystemThemeModeEnabled
|
||||||
|
// ? null
|
||||||
|
// : (final newValue) => appController.setThemeMode(
|
||||||
|
// newValue ? ThemeMode.dark : ThemeMode.light,
|
||||||
|
// ),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
import 'package:selfprivacy/config/app_controller/inherited_app_controller.dart';
|
||||||
import 'package:selfprivacy/ui/pages/onboarding/views/views.dart';
|
import 'package:selfprivacy/ui/pages/onboarding/views/views.dart';
|
||||||
import 'package:selfprivacy/ui/router/router.dart';
|
import 'package:selfprivacy/ui/router/router.dart';
|
||||||
|
|
||||||
|
@ -37,7 +37,8 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||||
),
|
),
|
||||||
OnboardingSecondView(
|
OnboardingSecondView(
|
||||||
onProceed: () {
|
onProceed: () {
|
||||||
context.read<AppSettingsCubit>().turnOffOnboarding();
|
InheritedAppController.of(context)
|
||||||
|
.setShouldShowOnboarding(false);
|
||||||
context.router.replaceAll([
|
context.router.replaceAll([
|
||||||
const RootRoute(),
|
const RootRoute(),
|
||||||
const InitializingRoute(),
|
const InitializingRoute(),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
import 'package:selfprivacy/config/app_controller/inherited_app_controller.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/ui/layouts/root_scaffold_with_navigation.dart';
|
import 'package:selfprivacy/ui/layouts/root_scaffold_with_navigation.dart';
|
||||||
import 'package:selfprivacy/ui/router/root_destinations.dart';
|
import 'package:selfprivacy/ui/router/root_destinations.dart';
|
||||||
|
@ -19,31 +19,33 @@ class RootPage extends StatefulWidget implements AutoRouteWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RootPageState extends State<RootPage> with TickerProviderStateMixin {
|
class _RootPageState extends State<RootPage> with TickerProviderStateMixin {
|
||||||
bool shouldUseSplitView() => false;
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
if (InheritedAppController.of(context).shouldShowOnboarding) {
|
||||||
|
context.router.replace(const OnboardingRoute());
|
||||||
|
}
|
||||||
|
|
||||||
final destinations = rootDestinations;
|
super.didChangeDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
final bool isReady = context.watch<ServerInstallationCubit>().state
|
final bool isReady = context.watch<ServerInstallationCubit>().state
|
||||||
is ServerInstallationFinished;
|
is ServerInstallationFinished;
|
||||||
|
|
||||||
if (context.read<AppSettingsCubit>().state.isOnboardingShowing) {
|
|
||||||
context.router.replace(const OnboardingRoute());
|
|
||||||
}
|
|
||||||
|
|
||||||
return AutoRouter(
|
return AutoRouter(
|
||||||
builder: (final context, final child) {
|
builder: (final context, final child) {
|
||||||
final currentDestinationIndex = destinations.indexWhere(
|
final currentDestinationIndex = rootDestinations.indexWhere(
|
||||||
(final destination) =>
|
(final destination) =>
|
||||||
context.router.isRouteActive(destination.route.routeName),
|
context.router.isRouteActive(destination.route.routeName),
|
||||||
);
|
);
|
||||||
final isOtherRouterActive =
|
final isOtherRouterActive =
|
||||||
context.router.root.current.name != RootRoute.name;
|
context.router.root.current.name != RootRoute.name;
|
||||||
|
|
||||||
final routeName = getRouteTitle(context.router.current.name).tr();
|
final routeName = getRouteTitle(context.router.current.name).tr();
|
||||||
return RootScaffoldWithNavigation(
|
return RootScaffoldWithNavigation(
|
||||||
title: routeName,
|
title: routeName,
|
||||||
destinations: destinations,
|
destinations: rootDestinations,
|
||||||
showBottomBar:
|
showBottomBar:
|
||||||
!(currentDestinationIndex == -1 && !isOtherRouterActive),
|
!(currentDestinationIndex == -1 && !isOtherRouterActive),
|
||||||
showFab: isReady,
|
showFab: isReady,
|
||||||
|
@ -53,99 +55,3 @@ class _RootPageState extends State<RootPage> with TickerProviderStateMixin {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainScreenNavigationRail extends StatelessWidget {
|
|
||||||
const MainScreenNavigationRail({
|
|
||||||
required this.destinations,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
final List<RouteDestination> destinations;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(final BuildContext context) {
|
|
||||||
int? activeIndex = destinations.indexWhere(
|
|
||||||
(final destination) =>
|
|
||||||
context.router.isRouteActive(destination.route.routeName),
|
|
||||||
);
|
|
||||||
if (activeIndex == -1) {
|
|
||||||
activeIndex = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: SizedBox(
|
|
||||||
height: MediaQuery.of(context).size.height,
|
|
||||||
width: 72,
|
|
||||||
child: LayoutBuilder(
|
|
||||||
builder: (final context, final constraints) => SingleChildScrollView(
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(minHeight: constraints.maxHeight),
|
|
||||||
child: IntrinsicHeight(
|
|
||||||
child: NavigationRail(
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
labelType: NavigationRailLabelType.all,
|
|
||||||
destinations: destinations
|
|
||||||
.map(
|
|
||||||
(final destination) => NavigationRailDestination(
|
|
||||||
icon: Icon(destination.icon),
|
|
||||||
label: Text(destination.label),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
selectedIndex: activeIndex,
|
|
||||||
onDestinationSelected: (final index) {
|
|
||||||
context.router.replaceAll([destinations[index].route]);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MainScreenNavigationDrawer extends StatelessWidget {
|
|
||||||
const MainScreenNavigationDrawer({
|
|
||||||
required this.destinations,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
final List<RouteDestination> destinations;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(final BuildContext context) {
|
|
||||||
int? activeIndex = destinations.indexWhere(
|
|
||||||
(final destination) =>
|
|
||||||
context.router.isRouteActive(destination.route.routeName),
|
|
||||||
);
|
|
||||||
if (activeIndex == -1) {
|
|
||||||
activeIndex = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SizedBox(
|
|
||||||
height: MediaQuery.of(context).size.height,
|
|
||||||
width: 296,
|
|
||||||
child: LayoutBuilder(
|
|
||||||
builder: (final context, final constraints) => NavigationDrawer(
|
|
||||||
key: const Key('PrimaryNavigationDrawer'),
|
|
||||||
selectedIndex: activeIndex,
|
|
||||||
onDestinationSelected: (final index) {
|
|
||||||
context.router.replaceAll([destinations[index].route]);
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 18),
|
|
||||||
...destinations.map(
|
|
||||||
(final destination) => NavigationDrawerDestination(
|
|
||||||
icon: Icon(destination.icon),
|
|
||||||
label: Text(destination.label),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:selfprivacy/config/app_controller/inherited_app_controller.dart';
|
||||||
import 'package:selfprivacy/illustrations/stray_deer.dart';
|
import 'package:selfprivacy/illustrations/stray_deer.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/price.dart';
|
import 'package:selfprivacy/logic/models/price.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_provider_location.dart';
|
import 'package:selfprivacy/logic/models/server_provider_location.dart';
|
||||||
|
@ -205,10 +205,8 @@ class SelectTypePage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
painter: StrayDeerPainter(
|
painter: StrayDeerPainter(
|
||||||
colorScheme: Theme.of(context).colorScheme,
|
colorScheme: Theme.of(context).colorScheme,
|
||||||
colorPalette: context
|
colorPalette:
|
||||||
.read<AppSettingsCubit>()
|
InheritedAppController.of(context).corePalette,
|
||||||
.state
|
|
||||||
.corePaletteOrDefault,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue