import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../core/data/models/drama.dart'; import '../../core/data/services/catalog_controller.dart'; import '../../l10n/app_localizations.dart'; import '../discover/discover_view.dart'; import '../drama_detail/drama_detail_view.dart'; import '../mine/mine_view.dart'; import '../player/player_view.dart'; import 'home_binding.dart'; import 'home_logic.dart'; import 'widgets/home_quick_drawer.dart'; class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { final catalog = context.watch(); final l10n = AppLocalizations.of(context)!; return Theme( data: ThemeData.dark(useMaterial3: true), child: _buildHome(context, catalog, l10n), ); } Widget _buildHome( BuildContext context, CatalogController catalog, AppLocalizations l10n, ) { if (catalog.loading) { return const Scaffold( backgroundColor: Colors.black, body: Center(child: CircularProgressIndicator()), ); } if (catalog.error != null || catalog.dramas.isEmpty) { return Scaffold( backgroundColor: Colors.black, body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(catalog.error == null ? l10n.noStories : l10n.catalogError), const SizedBox(height: 12), FilledButton(onPressed: catalog.load, child: Text(l10n.retry)), ], ), ), ); } return _HomeTabs( key: ValueKey(catalog.dramas.first.id), dramas: catalog.dramas, ); } } /// The three primary destinations share a single route and keep their state. class _HomeTabs extends StatefulWidget { const _HomeTabs({super.key, required this.dramas}); final List dramas; @override State<_HomeTabs> createState() => _HomeTabsState(); } class _HomeTabsState extends State<_HomeTabs> { late final HomeLogic logic; @override void initState() { super.initState(); logic = HomeBinding().create(widget.dramas); } void _select( int tab, { bool search = false, bool watchlist = false, bool resume = false, int? mySection, }) { setState( () => logic.select( tab, search: search, watchlist: watchlist, resume: resume, mySection: mySection, ), ); } void _openMenu() { setState(() => logic.drawerOpen = true); logic.scaffoldKey.currentState?.openDrawer(); } void _selectFromMenu(int tab, {int? mySection}) { logic.scaffoldKey.currentState?.closeDrawer(); _select(tab, mySection: mySection); } Future _openCurrentSeries() async { setState(() => logic.detailOpen = true); logic.scaffoldKey.currentState?.closeDrawer(); await WidgetsBinding.instance.endOfFrame; if (!mounted) return; try { await Navigator.of(context).push( MaterialPageRoute( builder: (_) => DramaDetailPage(drama: logic.currentDrama), ), ); } finally { if (mounted) setState(() => logic.detailOpen = false); } } void _discoverAction(DiscoverPageAction action) { switch (action) { case DiscoverPageAction.home: _select(0); case DiscoverPageAction.my: _select(2); case DiscoverPageAction.watchlist: _select(2, watchlist: true); case DiscoverPageAction.resume: _select(0, resume: true); case DiscoverPageAction.detail: Navigator.of(context).push( MaterialPageRoute( builder: (_) => DramaDetailPage(drama: logic.currentDrama), ), ); } } void _myAction(MyPageAction action) { switch (action) { case MyPageAction.home: _select(0); case MyPageAction.discover: _select(1, search: true); case MyPageAction.resume: _select(0, resume: true); } } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; return PopScope( canPop: false, onPopInvokedWithResult: (didPop, _) { if (didPop) return; if (logic.drawerOpen) { logic.scaffoldKey.currentState?.closeDrawer(); } else if (logic.videoImmersive) { setState(() => logic.backRequest++); } else if (logic.tab != 0) { _select(0); } else { setState(() => logic.backRequest++); } }, child: Scaffold( key: logic.scaffoldKey, backgroundColor: const Color(0xFF0B0C10), drawer: HomeQuickDrawer( drama: logic.currentDrama, onOpenSeries: _openCurrentSeries, onDiscover: () => _selectFromMenu(1), onHistory: () => _selectFromMenu(2, mySection: 0), onWatchlist: () => _selectFromMenu(2, mySection: 1), onLiked: () => _selectFromMenu(2, mySection: 2), ), drawerEnableOpenDragGesture: false, onDrawerChanged: (opened) { if (logic.drawerOpen != opened) { setState(() => logic.drawerOpen = opened); } }, body: IndexedStack( index: logic.tab, children: [ PlayerPage( drama: widget.dramas.first, feedDramas: widget.dramas, onDramaChanged: (drama) => setState(() => logic.currentDrama = drama), initialIndex: 0, homeMode: true, embedded: true, active: logic.tab == 0 && !logic.drawerOpen && !logic.detailOpen, resumeRequest: logic.resumeRequest, backRequest: logic.backRequest, onDiscover: () => _select(1), onMy: () => _select(2), onSearch: () => _select(1, search: true), onMenu: _openMenu, onImmersiveChanged: (value) => setState(() => logic.videoImmersive = value), ), DiscoverPage( drama: logic.currentDrama, dramas: widget.dramas, onDramaSelected: (drama) => Navigator.of(context).push( MaterialPageRoute( builder: (_) => DramaDetailPage(drama: drama), ), ), duration: Duration.zero, embedded: true, active: logic.tab == 1, searchRequest: logic.searchRequest, onAction: _discoverAction, ), MyPage( drama: logic.currentDrama, dramas: widget.dramas, onDramaSelected: (drama) => Navigator.of(context).push( MaterialPageRoute( builder: (_) => DramaDetailPage(drama: drama), ), ), duration: Duration.zero, initialSection: logic.mySection, sectionRequest: logic.sectionRequest, embedded: true, active: logic.tab == 2, onAction: _myAction, ), ], ), bottomNavigationBar: logic.videoImmersive ? null : SafeArea( top: false, child: Container( key: const Key('home-tabs-bar'), height: 63, decoration: const BoxDecoration( color: Color(0xFF0D0D11), border: Border(top: BorderSide(color: Color(0xFF242329))), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _TabButton( key: const Key('tab-home'), icon: Icons.play_circle_outline_rounded, label: l10n.homeTab, selected: logic.tab == 0, onTap: () => _select(0), ), _TabButton( key: const Key('tab-discover'), icon: Icons.explore_outlined, label: l10n.discoverTab, selected: logic.tab == 1, onTap: () => _select(1), ), _TabButton( key: const Key('tab-my'), icon: Icons.video_library_outlined, label: l10n.myTab, selected: logic.tab == 2, onTap: () => _select(2), ), ], ), ), ), ), ); } } class _TabButton extends StatelessWidget { const _TabButton({ super.key, required this.icon, required this.label, required this.selected, required this.onTap, }); final IconData icon; final String label; final bool selected; final VoidCallback onTap; @override Widget build(BuildContext context) => InkWell( onTap: onTap, child: SizedBox( width: 80, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon, size: 21, color: selected ? const Color(0xFFFF5C37) : const Color(0xFFE6E1DE), ), Text( label, style: TextStyle( fontSize: 10, color: selected ? const Color(0xFFFF5C37) : const Color(0xFFE6E1DE), ), ), ], ), ), ); }