🎉 Initial Commit
This commit is contained in:
171
lib/pods/config.dart
Normal file
171
lib/pods/config.dart
Normal file
@ -0,0 +1,171 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:responsive_framework/responsive_framework.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
const kAtkStoreKey = 'nex_user_atk';
|
||||
const kRtkStoreKey = 'nex_user_rtk';
|
||||
|
||||
const kNetworkServerDefault = 'https://api.sn.solsynth.dev';
|
||||
const kNetworkServerStoreKey = 'app_server_url';
|
||||
|
||||
const kAppbarTransparentStoreKey = 'app_bar_transparent';
|
||||
const kAppBackgroundStoreKey = 'app_has_background';
|
||||
const kAppColorSchemeStoreKey = 'app_color_scheme';
|
||||
const kAppNotifyWithHaptic = 'app_notify_with_haptic';
|
||||
const kAppExpandPostLink = 'app_expand_post_link';
|
||||
const kAppExpandChatLink = 'app_expand_chat_link';
|
||||
const kAppRealmCompactView = 'app_realm_compact_view';
|
||||
const kAppCustomFonts = 'app_custom_fonts';
|
||||
const kAppMixedFeed = 'app_mixed_feed';
|
||||
const kAppAutoTranslate = 'app_auto_translate';
|
||||
const kAppHideBottomNav = 'app_hide_bottom_nav';
|
||||
const kAppSoundEffects = 'app_sound_effects';
|
||||
const kAppAprilFoolFeatures = 'app_april_fool_features';
|
||||
const kAppWindowSize = 'app_window_size';
|
||||
|
||||
const Map<String, FilterQuality> kImageQualityLevel = {
|
||||
'settingsImageQualityLowest': FilterQuality.none,
|
||||
'settingsImageQualityLow': FilterQuality.low,
|
||||
'settingsImageQualityMedium': FilterQuality.medium,
|
||||
'settingsImageQualityHigh': FilterQuality.high,
|
||||
};
|
||||
|
||||
final sharedPreferencesProvider = Provider<SharedPreferences>((ref) {
|
||||
throw UnimplementedError();
|
||||
});
|
||||
|
||||
final imageQualityProvider = Provider<FilterQuality>((ref) {
|
||||
final prefs = ref.watch(sharedPreferencesProvider);
|
||||
return kImageQualityLevel.values.elementAtOrNull(
|
||||
prefs.getInt('app_image_quality') ?? 3,
|
||||
) ??
|
||||
FilterQuality.high;
|
||||
});
|
||||
|
||||
final serverUrlProvider = Provider<String>((ref) {
|
||||
final prefs = ref.watch(sharedPreferencesProvider);
|
||||
return prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
|
||||
});
|
||||
|
||||
class AppSettings {
|
||||
final bool realmCompactView;
|
||||
final bool mixedFeed;
|
||||
final bool autoTranslate;
|
||||
final bool hideBottomNav;
|
||||
final bool soundEffects;
|
||||
final bool aprilFoolFeatures;
|
||||
|
||||
AppSettings({
|
||||
required this.realmCompactView,
|
||||
required this.mixedFeed,
|
||||
required this.autoTranslate,
|
||||
required this.hideBottomNav,
|
||||
required this.soundEffects,
|
||||
required this.aprilFoolFeatures,
|
||||
});
|
||||
|
||||
AppSettings copyWith({
|
||||
bool? realmCompactView,
|
||||
bool? mixedFeed,
|
||||
bool? autoTranslate,
|
||||
bool? hideBottomNav,
|
||||
bool? soundEffects,
|
||||
bool? aprilFoolFeatures,
|
||||
}) {
|
||||
return AppSettings(
|
||||
realmCompactView: realmCompactView ?? this.realmCompactView,
|
||||
mixedFeed: mixedFeed ?? this.mixedFeed,
|
||||
autoTranslate: autoTranslate ?? this.autoTranslate,
|
||||
hideBottomNav: hideBottomNav ?? this.hideBottomNav,
|
||||
soundEffects: soundEffects ?? this.soundEffects,
|
||||
aprilFoolFeatures: aprilFoolFeatures ?? this.aprilFoolFeatures,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppSettingsNotifier extends StateNotifier<AppSettings> {
|
||||
final SharedPreferences prefs;
|
||||
|
||||
AppSettingsNotifier(this.prefs)
|
||||
: super(
|
||||
AppSettings(
|
||||
realmCompactView: prefs.getBool(kAppRealmCompactView) ?? false,
|
||||
mixedFeed: prefs.getBool(kAppMixedFeed) ?? true,
|
||||
autoTranslate: prefs.getBool(kAppAutoTranslate) ?? false,
|
||||
hideBottomNav: prefs.getBool(kAppHideBottomNav) ?? false,
|
||||
soundEffects: prefs.getBool(kAppSoundEffects) ?? true,
|
||||
aprilFoolFeatures: prefs.getBool(kAppAprilFoolFeatures) ?? true,
|
||||
),
|
||||
);
|
||||
|
||||
void setRealmCompactView(bool value) {
|
||||
prefs.setBool(kAppRealmCompactView, value);
|
||||
state = state.copyWith(realmCompactView: value);
|
||||
}
|
||||
|
||||
void setMixedFeed(bool value) {
|
||||
prefs.setBool(kAppMixedFeed, value);
|
||||
state = state.copyWith(mixedFeed: value);
|
||||
}
|
||||
|
||||
void setAutoTranslate(bool value) {
|
||||
prefs.setBool(kAppAutoTranslate, value);
|
||||
state = state.copyWith(autoTranslate: value);
|
||||
}
|
||||
|
||||
void setHideBottomNav(bool value) {
|
||||
prefs.setBool(kAppHideBottomNav, value);
|
||||
state = state.copyWith(hideBottomNav: value);
|
||||
}
|
||||
|
||||
void setSoundEffects(bool value) {
|
||||
prefs.setBool(kAppSoundEffects, value);
|
||||
state = state.copyWith(soundEffects: value);
|
||||
}
|
||||
|
||||
void setAprilFoolFeatures(bool value) {
|
||||
prefs.setBool(kAppAprilFoolFeatures, value);
|
||||
state = state.copyWith(aprilFoolFeatures: value);
|
||||
}
|
||||
}
|
||||
|
||||
final appSettingsProvider =
|
||||
StateNotifierProvider<AppSettingsNotifier, AppSettings>((ref) {
|
||||
final prefs = ref.watch(sharedPreferencesProvider);
|
||||
return AppSettingsNotifier(prefs);
|
||||
});
|
||||
|
||||
final updateInfoProvider =
|
||||
StateNotifierProvider<UpdateInfoNotifier, (String?, String?)>((ref) {
|
||||
return UpdateInfoNotifier();
|
||||
});
|
||||
|
||||
class UpdateInfoNotifier extends StateNotifier<(String?, String?)> {
|
||||
UpdateInfoNotifier() : super((null, null));
|
||||
|
||||
void setUpdate(String newVersion, String newChangelog) {
|
||||
state = (newVersion, newChangelog);
|
||||
}
|
||||
}
|
||||
|
||||
final drawerCollapsedProvider = StateProvider<bool>((ref) => false);
|
||||
|
||||
void calcDrawerSize(
|
||||
WidgetRef ref,
|
||||
BuildContext context, {
|
||||
bool withMediaQuery = false,
|
||||
}) {
|
||||
bool newDrawerIsCollapsed;
|
||||
if (withMediaQuery) {
|
||||
newDrawerIsCollapsed = MediaQuery.of(context).size.width < 600;
|
||||
} else {
|
||||
final rpb = ResponsiveBreakpoints.of(context);
|
||||
newDrawerIsCollapsed = rpb.smallerOrEqualTo(MOBILE);
|
||||
}
|
||||
|
||||
final current = ref.read(drawerCollapsedProvider);
|
||||
if (newDrawerIsCollapsed != current) {
|
||||
ref.read(drawerCollapsedProvider.notifier).state = newDrawerIsCollapsed;
|
||||
}
|
||||
}
|
145
lib/pods/theme.dart
Normal file
145
lib/pods/theme.dart
Normal file
@ -0,0 +1,145 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:island/pods/config.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
final themeProvider = StateNotifierProvider<ThemeNotifier, ThemeSet?>((ref) {
|
||||
return ThemeNotifier();
|
||||
});
|
||||
|
||||
class ThemeNotifier extends StateNotifier<ThemeSet?> {
|
||||
ThemeNotifier() : super(null) {
|
||||
_loadTheme();
|
||||
}
|
||||
|
||||
Future<void> _loadTheme() async {
|
||||
final theme = await createAppThemeSet();
|
||||
state = theme;
|
||||
}
|
||||
|
||||
void reloadTheme({
|
||||
Color? seedColorOverride,
|
||||
bool? useMaterial3,
|
||||
String? customFonts,
|
||||
}) async {
|
||||
final theme = await createAppThemeSet(
|
||||
seedColorOverride: seedColorOverride,
|
||||
useMaterial3: useMaterial3,
|
||||
customFonts: customFonts,
|
||||
);
|
||||
state = theme;
|
||||
}
|
||||
}
|
||||
|
||||
const kMaterialYouToggleStoreKey = 'app_theme_material_you';
|
||||
|
||||
class ThemeSet {
|
||||
ThemeData light;
|
||||
ThemeData dark;
|
||||
|
||||
ThemeSet({required this.light, required this.dark});
|
||||
}
|
||||
|
||||
Future<ThemeSet> createAppThemeSet({
|
||||
Color? seedColorOverride,
|
||||
bool? useMaterial3,
|
||||
String? customFonts,
|
||||
}) async {
|
||||
return ThemeSet(
|
||||
light: await createAppTheme(
|
||||
Brightness.light,
|
||||
useMaterial3: useMaterial3,
|
||||
customFonts: customFonts,
|
||||
),
|
||||
dark: await createAppTheme(
|
||||
Brightness.dark,
|
||||
useMaterial3: useMaterial3,
|
||||
customFonts: customFonts,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<ThemeData> createAppTheme(
|
||||
Brightness brightness, {
|
||||
Color? seedColorOverride,
|
||||
bool? useMaterial3,
|
||||
String? customFonts,
|
||||
}) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
final seedColorString = prefs.getInt(kAppColorSchemeStoreKey);
|
||||
final seedColor =
|
||||
seedColorString != null ? Color(seedColorString) : Colors.indigo;
|
||||
|
||||
final colorScheme = ColorScheme.fromSeed(
|
||||
seedColor: seedColorOverride ?? seedColor,
|
||||
brightness: brightness,
|
||||
);
|
||||
|
||||
final hasAppBarTransparent =
|
||||
prefs.getBool(kAppbarTransparentStoreKey) ?? false;
|
||||
final useM3 =
|
||||
useMaterial3 ?? (prefs.getBool(kMaterialYouToggleStoreKey) ?? true);
|
||||
|
||||
final inUseFonts =
|
||||
(customFonts ?? prefs.getString(kAppCustomFonts))
|
||||
?.split(',')
|
||||
.map((ele) => ele.trim())
|
||||
.toList() ??
|
||||
['Nunito'];
|
||||
|
||||
return ThemeData(
|
||||
useMaterial3: useM3,
|
||||
colorScheme: colorScheme,
|
||||
brightness: brightness,
|
||||
fontFamily: inUseFonts.firstOrNull,
|
||||
fontFamilyFallback: inUseFonts.sublist(1),
|
||||
iconTheme: IconThemeData(
|
||||
fill: 0,
|
||||
weight: 400,
|
||||
opticalSize: 20,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
snackBarTheme: SnackBarThemeData(
|
||||
behavior: useM3 ? SnackBarBehavior.floating : SnackBarBehavior.fixed,
|
||||
),
|
||||
appBarTheme: AppBarTheme(
|
||||
centerTitle: true,
|
||||
elevation: hasAppBarTransparent ? 0 : null,
|
||||
backgroundColor:
|
||||
hasAppBarTransparent ? Colors.transparent : colorScheme.primary,
|
||||
foregroundColor:
|
||||
hasAppBarTransparent ? colorScheme.onSurface : colorScheme.onPrimary,
|
||||
),
|
||||
pageTransitionsTheme: PageTransitionsTheme(
|
||||
builders: {
|
||||
TargetPlatform.android: ZoomPageTransitionsBuilder(),
|
||||
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
||||
TargetPlatform.macOS: ZoomPageTransitionsBuilder(),
|
||||
TargetPlatform.fuchsia: ZoomPageTransitionsBuilder(),
|
||||
TargetPlatform.linux: ZoomPageTransitionsBuilder(),
|
||||
TargetPlatform.windows: ZoomPageTransitionsBuilder(),
|
||||
},
|
||||
),
|
||||
progressIndicatorTheme: ProgressIndicatorThemeData(year2023: false),
|
||||
sliderTheme: SliderThemeData(year2023: false),
|
||||
);
|
||||
}
|
||||
|
||||
extension HexColor on Color {
|
||||
/// String is in the format "aabbcc" or "ffaabbcc" with an optional leading "#".
|
||||
static Color fromHex(String hexString) {
|
||||
final buffer = StringBuffer();
|
||||
if (hexString.length == 6 || hexString.length == 7) buffer.write('ff');
|
||||
buffer.write(hexString.replaceFirst('#', ''));
|
||||
return Color(int.parse(buffer.toString(), radix: 16));
|
||||
}
|
||||
|
||||
/// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`).
|
||||
String toHex({bool leadingHashSign = true}) =>
|
||||
'${leadingHashSign ? '#' : ''}'
|
||||
'${alpha.toRadixString(16).padLeft(2, '0')}'
|
||||
'${red.toRadixString(16).padLeft(2, '0')}'
|
||||
'${green.toRadixString(16).padLeft(2, '0')}'
|
||||
'${blue.toRadixString(16).padLeft(2, '0')}';
|
||||
}
|
Reference in New Issue
Block a user