// Author: fruit.com.cn import 'dart:async'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../core/data/models/drama.dart'; import '../../core/data/models/watch_progress.dart'; import '../../core/data/repositories/watch_history_repository.dart'; import '../../core/data/services/auth_controller.dart'; import '../../core/data/services/catalog_controller.dart'; import '../../core/data/services/download_controller.dart'; import '../../core/data/services/wallet_controller.dart'; import '../../core/theme/app_colors.dart'; import '../../core/theme/app_theme.dart'; import '../../core/widgets/app_bottom_nav.dart'; import '../../core/widgets/auth_gate.dart'; import '../../l10n/app_localizations.dart'; import '../downloads/downloads_page.dart'; import '../favorites/favorites_page.dart'; import '../player/player_view.dart'; import '../settings/settings_page.dart'; import '../store/vip_page.dart'; import '../wallet/wallet_page.dart'; import '../watch_records/watch_records_page.dart'; import 'mine_binding.dart'; import 'mine_logic.dart'; import 'mine_types.dart'; import 'widgets/continue_watching_card.dart'; import 'widgets/mine_header.dart'; import 'widgets/mine_menu.dart'; /// 极简「我的」页:续看 + 追剧 / 历史 / 下载 / 钱包 / 会员 / 设置。 class MyPage extends StatefulWidget { const MyPage({ super.key, this.active = true, this.callbacks = const MyPageCallbacks(), }); final bool active; final MyPageCallbacks callbacks; @override State createState() => _MyPageState(); } class _MyPageState extends State { late final MineLogic logic; bool get embedded => widget.callbacks.embedded; @override void initState() { super.initState(); logic = MineBinding().create( history: context.read(), ); } @override void didUpdateWidget(covariant MyPage oldWidget) { super.didUpdateWidget(oldWidget); logic.syncPage(active: widget.active, wasActive: oldWidget.active); } @override void dispose() { logic.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return ListenableBuilder( listenable: logic, builder: (context, _) => _page(context), ); } Widget _page(BuildContext context) { final l10n = AppLocalizations.of(context)!; final content = SafeArea( bottom: !embedded, child: Column( children: [ _topBar(l10n), Expanded(child: _body(context, l10n)), if (!embedded) _bottomNav(l10n), ], ), ); return Theme( data: AppTheme.light(), child: embedded ? content : Scaffold(backgroundColor: AppColors.ink, body: content), ); } Widget _topBar(AppLocalizations l10n) { return SizedBox( height: 46, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 14), child: Row( children: [ Container( width: 24, height: 24, alignment: Alignment.center, decoration: BoxDecoration( color: AppColors.blue, borderRadius: BorderRadius.circular(7), ), child: const Icon( Icons.play_arrow_rounded, color: AppColors.onPrimary, size: 16, ), ), const SizedBox(width: 8), Text( l10n.myTab, style: const TextStyle( fontFamily: 'Outfit', color: AppColors.onSurface, fontSize: 15, fontWeight: FontWeight.w800, ), ), ], ), ), ); } Widget _body(BuildContext context, AppLocalizations l10n) { final auth = context.watch(); final wallet = context.watch(); final record = _continueRecord; final drama = _dramaFor(record); return ListView( padding: const EdgeInsets.only(bottom: 18), children: [ const MineHeader(), if (drama != null && record != null) ContinueWatchingCard( drama: drama, record: record, onResume: () => _resume(drama), ), const SizedBox(height: 14), Padding( padding: const EdgeInsets.symmetric(horizontal: 14), child: MineMenu( walletValue: _walletValue(l10n, auth, wallet), onOpen: _openDestination, ), ), ], ); } String _walletValue( AppLocalizations l10n, AuthController auth, WalletController wallet, ) { if (!auth.isSignedIn) return l10n.logInHint; if (wallet.snapshot == null) return l10n.coins; return l10n.walletBalanceShort(wallet.balance); } WatchProgress? get _continueRecord { for (final record in logic.recent) { if (!record.finished && record.hasPosition) return record; } return null; } Drama? _dramaFor(WatchProgress? record) { if (record == null) return null; for (final drama in context.read().dramas) { if (drama.id == record.dramaId) return drama; } return null; } void _resume(Drama drama) { final callback = widget.callbacks.onDramaSelected; if (callback != null) { callback(drama); return; } Navigator.of(context).push( MaterialPageRoute( builder: (_) => PlayerPage( drama: drama, initialIndex: 0, wallet: context.read(), downloads: context.read(), ), ), ); } void _openDestination(MineDestination destination) { switch (destination) { case MineDestination.watchlist: _push(const FavoritesPage()); case MineDestination.history: _push(const WatchRecordsPage()); case MineDestination.downloads: _push(const DownloadsPage()); case MineDestination.wallet: _openProtected(const WalletPage()); case MineDestination.vip: _openVip(); case MineDestination.settings: _push(const SettingsPage()); } } void _push(Widget page) { Navigator.of(context).push(MaterialPageRoute(builder: (_) => page)); } Future _openProtected(Widget page) async { if (!context.read().isSignedIn) { await ensureSignedIn(context); return; } if (mounted) _push(page); } void _openVip() { final wallet = context.read(); _openProtected(VipPage(onPurchased: () => unawaited(wallet.load()))); } Widget _bottomNav(AppLocalizations l10n) { return AppBottomNav( items: [ AppBottomNavItem( tabKey: const Key('mine-nav-home'), icon: Icons.home_outlined, selectedIcon: Icons.home_rounded, label: l10n.homeTab, onTap: () => _act(MyPageAction.home), ), AppBottomNavItem( tabKey: const Key('mine-nav-discover'), icon: Icons.explore_outlined, selectedIcon: Icons.explore_rounded, label: l10n.theaterTab, onTap: () => _act(MyPageAction.discover), ), AppBottomNavItem( tabKey: const Key('mine-nav-create'), icon: Icons.add_box_outlined, selectedIcon: Icons.add_box_rounded, label: l10n.createTab, onTap: () => _act(MyPageAction.create), ), AppBottomNavItem( tabKey: const Key('mine-nav-current'), icon: Icons.video_library_outlined, selectedIcon: Icons.video_library_rounded, label: l10n.myTab, selected: true, onTap: () {}, ), ], ); } void _act(MyPageAction action) { logic.act( context, action, embedded: embedded, onAction: widget.callbacks.onAction, ); } }