Files
oott/frontend/lib/main.dart
T
rzuastiandClaude Opus 4.8 dce683e396 Add Catppuccin Latte, Dracula, Alucard, Nord and Tokyo Night themes
Add five new selectable themes alongside the existing Catppuccin Mocha
and Gruvbox Dark, each built through the shared buildAppTheme so they
stay visually consistent. Palettes are taken from each project's
official source; a few intermediate surface shades are interpolated
where the canonical palette omits them (noted in the theme files).

Register the new themes in main.dart and add their dropdown entries in
settings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 10:19:56 -04:00

110 lines
3.3 KiB
Dart

import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:frontend/theme/alucard_theme.dart';
import 'package:frontend/theme/catppuccin_latte_theme.dart';
import 'package:frontend/theme/catppuccin_mocha_theme.dart';
import 'package:frontend/theme/dracula_theme.dart';
import 'package:frontend/theme/nord_theme.dart';
import 'package:frontend/theme/tokyo_night_theme.dart';
import 'package:frontend/utils/local_network_permission.dart';
import 'package:frontend/utils/pref_utils.dart';
import 'package:frontend/utils/push_service.dart';
import 'package:frontend/utils/ui_snackbars.dart';
import 'package:provider/provider.dart';
import 'navigation.dart';
import 'theme/gruvbox_theme.dart';
void main() {
runZonedGuarded(
() async {
WidgetsFlutterBinding.ensureInitialized();
FlutterError.onError = (details) {
FlutterError.presentError(details);
debugPrint('FlutterError: ${details.exceptionAsString()}');
};
PlatformDispatcher.instance.onError = (error, stack) {
debugPrint('Uncaught platform error: $error\n$stack');
return true;
};
try {
await PrefUtil.init();
} catch (e, stack) {
debugPrint(
'PrefUtil.init failed, continuing with defaults: $e\n$stack',
);
}
// Resume push at launch (Firebase init + foreground handler + token
// re-registration when already enabled); see initPushOnLaunch.
try {
await initPushOnLaunch();
} catch (e, stack) {
debugPrint('Push launch init failed, continuing: $e\n$stack');
}
// Settle iOS's local-network permission now, at launch, so it isn't
// still being decided when the user first taps "Test" in settings.
unawaited(requestLocalNetworkPermission());
runApp(const MainApp());
},
(error, stack) {
debugPrint('Uncaught zone error: $error\n$stack');
},
);
}
final class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => AppState(),
child: Consumer<AppState>(
builder: (context, appState, _) => MaterialApp.router(
title: 'OOTT',
theme: appState.theme,
routerConfig: router,
// Host snackbars above the router's Navigator so they render on top
// of dialogs (their barrier no longer dims them). See buildSnackbarHost.
builder: buildSnackbarHost,
),
),
);
}
}
final _themes = {
'alucard': alucardLightTheme,
'catppuccin_latte': catppuccinLatteLightTheme,
'catppuccin_mocha': catppuccinMochaDarkTheme,
'dracula': draculaDarkTheme,
'gruvbox_dark': gruvboxDarkTheme,
'nord': nordDarkTheme,
'tokyo_night': tokyoNightDarkTheme,
};
class AppState extends ChangeNotifier {
AppState() {
final saved = PrefUtil.getValue('theme', 'gruvbox_dark') as String;
_themeKey = _themes.containsKey(saved) ? saved : 'gruvbox_dark';
}
late String _themeKey;
String get themeKey => _themeKey;
ThemeData get theme => _themes[_themeKey]!;
Future<bool> setTheme(String key) async {
if (!_themes.containsKey(key)) return false;
if (key == _themeKey) return true;
_themeKey = key;
notifyListeners();
return PrefUtil.setValue('theme', key);
}
}