176 lines
5.5 KiB
Dart
176 lines
5.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
|
|
import 'data/api_service.dart';
|
|
import 'state/app_state.dart';
|
|
import 'ui/catalog.dart';
|
|
import 'ui/library.dart';
|
|
|
|
class MyApp extends StatefulWidget {
|
|
const MyApp({super.key, this.state});
|
|
final AppState? state;
|
|
@override
|
|
State<MyApp> createState() => _MyAppState();
|
|
}
|
|
|
|
class _MyAppState extends State<MyApp> {
|
|
late final AppState state = widget.state ?? AppState(api: ApiService());
|
|
@override
|
|
void dispose() {
|
|
if (widget.state == null) state.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
ThemeData theme(Brightness brightness) {
|
|
final dark = brightness == Brightness.dark;
|
|
final colors = ColorScheme.fromSeed(
|
|
seedColor: const Color(0xFFFF675C),
|
|
brightness: brightness,
|
|
primary: dark ? const Color(0xFFFF8276) : const Color(0xFFBC3D34),
|
|
surface: dark ? const Color(0xFF0B0D12) : const Color(0xFFFAF8F5),
|
|
);
|
|
return ThemeData(
|
|
useMaterial3: true,
|
|
colorScheme: colors,
|
|
scaffoldBackgroundColor: colors.surface,
|
|
visualDensity: VisualDensity.standard,
|
|
appBarTheme: AppBarTheme(
|
|
centerTitle: false,
|
|
elevation: 0,
|
|
scrolledUnderElevation: 0,
|
|
backgroundColor: colors.surface,
|
|
titleTextStyle: TextStyle(
|
|
fontFamily: 'Roboto',
|
|
color: colors.onSurface,
|
|
fontSize: 23,
|
|
fontWeight: FontWeight.w700,
|
|
),
|
|
),
|
|
cardTheme: CardThemeData(
|
|
elevation: 0,
|
|
color: colors.surfaceContainerLow,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)),
|
|
),
|
|
inputDecorationTheme: InputDecorationTheme(
|
|
filled: true,
|
|
fillColor: colors.surfaceContainer,
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
horizontal: 18,
|
|
vertical: 16,
|
|
),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(15),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
),
|
|
chipTheme: ChipThemeData(
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
|
),
|
|
dividerTheme: DividerThemeData(
|
|
color: colors.outlineVariant.withValues(alpha: .45),
|
|
space: 1,
|
|
),
|
|
navigationBarTheme: NavigationBarThemeData(
|
|
height: 74,
|
|
backgroundColor: colors.surfaceContainerLow,
|
|
indicatorColor: colors.primary.withValues(alpha: .17),
|
|
),
|
|
snackBarTheme: SnackBarThemeData(
|
|
behavior: SnackBarBehavior.floating,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) => AppScope(
|
|
notifier: state,
|
|
child: AnimatedBuilder(
|
|
animation: state,
|
|
builder: (_, _) => MaterialApp(
|
|
title: 'Reelbox',
|
|
debugShowCheckedModeBanner: false,
|
|
theme: theme(Brightness.light),
|
|
darkTheme: theme(Brightness.dark),
|
|
themeMode: switch (state.prefs.theme) {
|
|
'light' => ThemeMode.light,
|
|
'system' => ThemeMode.system,
|
|
_ => ThemeMode.dark,
|
|
},
|
|
locale: Locale(state.locale),
|
|
supportedLocales: const [Locale('en'), Locale('fa')],
|
|
localizationsDelegates: GlobalMaterialLocalizations.delegates,
|
|
home: state.prefs.introCompleted
|
|
? const _AppShell()
|
|
: const FirstRunIntroPage(),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
class _AppShell extends StatefulWidget {
|
|
const _AppShell();
|
|
@override
|
|
State<_AppShell> createState() => _AppShellState();
|
|
}
|
|
|
|
class _AppShellState extends State<_AppShell> {
|
|
int selected = 0;
|
|
final List<Widget?> pages = [const DiscoverPage(), null, null, null, null];
|
|
Widget page(int index) => switch (index) {
|
|
0 => const DiscoverPage(),
|
|
1 => const BrowsePage(),
|
|
2 => const SearchPage(),
|
|
3 => const LibraryPage(),
|
|
_ => const MorePage(),
|
|
};
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final revision = AppScope.of(context).apiRevision;
|
|
return Scaffold(
|
|
body: IndexedStack(
|
|
index: selected,
|
|
children: [
|
|
for (var i = 0; i < pages.length; i++)
|
|
KeyedSubtree(
|
|
key: ValueKey(i < 3 ? 'catalog-$i-$revision' : 'local-$i'),
|
|
child: pages[i] ?? const SizedBox.shrink(),
|
|
),
|
|
],
|
|
),
|
|
bottomNavigationBar: NavigationBar(
|
|
selectedIndex: selected,
|
|
onDestinationSelected: (index) => setState(() {
|
|
selected = index;
|
|
pages[index] ??= page(index);
|
|
}),
|
|
destinations: [
|
|
NavigationDestination(
|
|
icon: const Icon(Icons.explore_outlined),
|
|
selectedIcon: const Icon(Icons.explore),
|
|
label: tr(context, 'Discover', 'کشف'),
|
|
),
|
|
NavigationDestination(
|
|
icon: const Icon(Icons.grid_view_outlined),
|
|
selectedIcon: const Icon(Icons.grid_view_rounded),
|
|
label: tr(context, 'Browse', 'مرور'),
|
|
),
|
|
NavigationDestination(
|
|
icon: const Icon(Icons.search_rounded),
|
|
label: tr(context, 'Search', 'جستجو'),
|
|
),
|
|
NavigationDestination(
|
|
icon: const Icon(Icons.video_library_outlined),
|
|
selectedIcon: const Icon(Icons.video_library_rounded),
|
|
label: tr(context, 'Library', 'کتابخانه'),
|
|
),
|
|
NavigationDestination(
|
|
icon: const Icon(Icons.tune_rounded),
|
|
label: tr(context, 'More', 'بیشتر'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|