Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 50 additions & 72 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import 'dart:ui';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

import 'package:dynamic_color/dynamic_color.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'package:fladder/bootstrap/app_bootstrap.dart';
Expand All @@ -16,14 +14,14 @@ import 'package:fladder/providers/settings/client_settings_provider.dart';
import 'package:fladder/providers/shared_provider.dart';
import 'package:fladder/providers/sync_provider.dart';
import 'package:fladder/routes/auto_router.dart';
import 'package:fladder/theme.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/application_info.dart';
import 'package:fladder/util/deep_link_helper.dart';
import 'package:fladder/util/localization_helper.dart';
import 'package:fladder/util/themes_data.dart';
import 'package:fladder/widgets/media_query_scaler.dart';
import 'package:fladder/widgets/pip_lifecycle_controller.dart';
import 'package:fladder/widgets/shared/adaptive_color.dart';

void main(List<String> args) async {
WidgetsFlutterBinding.ensureInitialized();
Expand Down Expand Up @@ -70,86 +68,66 @@ class _FladderApp extends ConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
final isLinux = defaultTargetPlatform == TargetPlatform.linux;
final themeMode = ref.watch(clientSettingsProvider.select((value) => value.themeMode));
final themeColor = ref.watch(clientSettingsProvider.select((value) => value.themeColor));
final amoledBlack = ref.watch(clientSettingsProvider.select((value) => value.amoledBlack));
final mouseDrag = ref.watch(clientSettingsProvider.select((value) => value.mouseDragSupport));
final schemeVariant = ref.watch(clientSettingsProvider.select((value) => value.schemeVariant));
final language = ref.watch(clientSettingsProvider
.select((value) => value.selectedLocale ?? WidgetsBinding.instance.platformDispatcher.locale));
final scrollBehaviour = const MaterialScrollBehavior();
return DynamicColorBuilder(
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
final baseLightTheme = themeColor == null
? FladderTheme.theme(lightDynamic ?? FladderTheme.defaultScheme(Brightness.light), schemeVariant)
: FladderTheme.theme(themeColor.schemeLight, schemeVariant);
final baseDarkTheme = (themeColor == null
? FladderTheme.theme(darkDynamic ?? FladderTheme.defaultScheme(Brightness.dark), schemeVariant)
: FladderTheme.theme(themeColor.schemeDark, schemeVariant));
final amoledOverwrite = amoledBlack ? Colors.black : null;

// Apply Chinese font for non-Linux platforms (Windows, macOS, Android, iOS)
final lightTheme = isLinux
? baseLightTheme
: FladderTheme.applyChineseFontToTheme(
lightTheme: baseLightTheme,
darkTheme: baseDarkTheme,
);
final darkTheme = isLinux ? baseDarkTheme : FladderTheme.applyChineseFontToDarkTheme(darkTheme: baseDarkTheme);

final amoledOverwrite = amoledBlack ? Colors.black : null;
return ThemesData(
light: lightTheme,
dark: darkTheme,
child: MaterialApp.router(
theme: lightTheme,
scrollBehavior: scrollBehaviour.copyWith(
dragDevices: {
...scrollBehaviour.dragDevices,
mouseDrag ? PointerDeviceKind.mouse : null,
}.nonNulls.toSet(),
),
localizationsDelegates: FladderLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
locale: language,
localeResolutionCallback: (locale, supportedLocales) {
const fallback = Locale('en');
if (locale == null) return fallback;
if (supportedLocales.contains(locale)) {
return locale;
}
final matchByLanguage = supportedLocales.firstWhere(
(l) => l.languageCode == locale.languageCode,
orElse: () => fallback,
);
return AdaptiveColor(
child: (darkTheme, lightTheme) => ThemesData(
light: lightTheme,
dark: darkTheme,
child: MaterialApp.router(
theme: lightTheme,
scrollBehavior: scrollBehaviour.copyWith(
dragDevices: {
...scrollBehaviour.dragDevices,
mouseDrag ? PointerDeviceKind.mouse : null,
}.nonNulls.toSet(),
),
localizationsDelegates: FladderLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
locale: language,
localeResolutionCallback: (locale, supportedLocales) {
const fallback = Locale('en');
if (locale == null) return fallback;
if (supportedLocales.contains(locale)) {
return locale;
}
final matchByLanguage = supportedLocales.firstWhere(
(l) => l.languageCode == locale.languageCode,
orElse: () => fallback,
);

return matchByLanguage;
},
builder: (context, child) => MediaQueryScaler(
child: LocalizationContextWrapper(
child: PipLifecycleController(child: child ?? Container()),
currentLocale: language,
),
enable: ref.read(argumentsStateProvider).leanBackMode,
return matchByLanguage;
},
builder: (context, child) => MediaQueryScaler(
child: LocalizationContextWrapper(
child: PipLifecycleController(child: child ?? Container()),
currentLocale: language,
),
debugShowCheckedModeBanner: false,
darkTheme: darkTheme.copyWith(
scaffoldBackgroundColor: amoledOverwrite,
cardColor: amoledOverwrite,
canvasColor: amoledOverwrite,
colorScheme: darkTheme.colorScheme.copyWith(
surface: amoledOverwrite,
surfaceContainerHighest: amoledOverwrite,
surfaceContainerLow: amoledOverwrite,
),
),
themeMode: themeMode,
routerConfig: autoRouter.config(
deepLinkBuilder: (deepLink) => deepLinkBuilder(deepLink.uri),
enable: ref.read(argumentsStateProvider).leanBackMode,
),
debugShowCheckedModeBanner: false,
darkTheme: darkTheme.copyWith(
scaffoldBackgroundColor: amoledOverwrite,
cardColor: amoledOverwrite,
canvasColor: amoledOverwrite,
colorScheme: darkTheme.colorScheme.copyWith(
surface: amoledOverwrite,
surfaceContainerHighest: amoledOverwrite,
surfaceContainerLow: amoledOverwrite,
),
),
);
},
themeMode: themeMode,
routerConfig: autoRouter.config(
deepLinkBuilder: (deepLink) => deepLinkBuilder(deepLink.uri),
),
),
),
);
}
}
110 changes: 110 additions & 0 deletions lib/widgets/shared/adaptive_color.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:dynamic_color/dynamic_color.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// ignore: depend_on_referenced_packages
import 'package:material_color_utilities/material_color_utilities.dart';

import 'package:fladder/providers/settings/client_settings_provider.dart';
import 'package:fladder/theme.dart';
import 'package:fladder/util/themes_data.dart';

class AdaptiveColor extends ConsumerStatefulWidget {
final Widget Function(ThemeData dark, ThemeData light) child;
const AdaptiveColor({required this.child, super.key});

@override
ConsumerState<ConsumerStatefulWidget> createState() => AdaptiveColorState();
}

class AdaptiveColorState extends ConsumerState<AdaptiveColor> with WidgetsBindingObserver {
ColorScheme? _light;
ColorScheme? _dark;

CorePalette? _corePalette;

@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_fetchColors();
}

@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_fetchColors();
}
}

Future<void> _fetchColors() async {
try {
final corePalette = await DynamicColorPlugin.getCorePalette();
if (corePalette == _corePalette) {
return;
}
_corePalette = corePalette;
if (corePalette != null && mounted) {
setState(() {
_light = corePalette.toColorScheme(brightness: Brightness.light);
_dark = corePalette.toColorScheme(brightness: Brightness.dark);
});
return;
}
} on PlatformException {
if (kDebugMode) debugPrint('dynamic_color: Failed to obtain core palette.');
}

try {
final accentColor = await DynamicColorPlugin.getAccentColor();
if (accentColor != null && mounted) {
setState(() {
_light = ColorScheme.fromSeed(seedColor: accentColor, brightness: Brightness.light);
_dark = ColorScheme.fromSeed(seedColor: accentColor, brightness: Brightness.dark);
});
return;
}
} on PlatformException {
if (kDebugMode) debugPrint('dynamic_color: Failed to obtain accent color.');
}
}

@override
Widget build(BuildContext context) {
final isLinux = defaultTargetPlatform == TargetPlatform.linux;
final themeColor = ref.watch(clientSettingsProvider.select((value) => value.themeColor));
final schemeVariant = ref.watch(clientSettingsProvider.select((value) => value.schemeVariant));

final fallbackLight = FladderTheme.defaultScheme(Brightness.light);
final fallbackDark = FladderTheme.defaultScheme(Brightness.dark);

final baseLightTheme = themeColor == null
? FladderTheme.theme(_light ?? fallbackLight, schemeVariant)
: FladderTheme.theme(themeColor.schemeLight, schemeVariant);

final baseDarkTheme = themeColor == null
? FladderTheme.theme(_dark ?? fallbackDark, schemeVariant)
: FladderTheme.theme(themeColor.schemeDark, schemeVariant);

// Apply fonts
final lightTheme = isLinux
? baseLightTheme
: FladderTheme.applyChineseFontToTheme(lightTheme: baseLightTheme, darkTheme: baseDarkTheme);

final darkTheme = isLinux ? baseDarkTheme : FladderTheme.applyChineseFontToDarkTheme(darkTheme: baseDarkTheme);

return ThemesData(
light: lightTheme,
dark: darkTheme,
child: widget.child(darkTheme, lightTheme),
);
}
}
Loading