petsHero-AI/lib/app.dart

209 lines
6.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:facebook_app_events/facebook_app_events.dart';
import 'package:flutter/material.dart';
import 'core/auth/auth_service.dart';
import 'core/nav/app_route_observer.dart';
import 'core/config/facebook_config.dart';
import 'core/log/app_logger.dart';
import 'core/theme/app_colors.dart';
import 'core/theme/app_theme.dart';
import 'core/user/user_state.dart';
import 'features/gallery/gallery_screen.dart';
import 'features/generate_video/generate_progress_screen.dart';
import 'features/generate_video/generate_video_screen.dart';
import 'features/gallery/models/gallery_task_item.dart';
import 'features/generate_video/generation_result_screen.dart';
import 'features/home/home_playback_resume.dart';
import 'features/home/home_screen.dart';
import 'features/home/models/task_item.dart';
import 'features/profile/profile_screen.dart';
import 'features/recharge/recharge_screen.dart';
import 'shared/widgets/bottom_nav_bar.dart';
import 'shared/tab_selector_scope.dart';
/// Root app widget with navigation
class App extends StatefulWidget {
const App({super.key});
@override
State<App> createState() => _AppState();
}
class _AppState extends State<App> with WidgetsBindingObserver {
NavTab _currentTab = NavTab.home;
static final _fbLog = AppLogger('FB');
final FacebookAppEvents _fbAppEvents = FacebookAppEvents();
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
// 冷启动时进程已在 resumeddidChangeAppLifecycleState 往往收不到「变为 resumed」需首帧后再手动打一次
WidgetsBinding.instance.addPostFrameCallback((_) {
_reportFacebookActivateApp('first_frame');
});
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
/// `AutoLogAppEventsEnabled=false` 时手动上报 Facebook **安装 + 应用激活**activateApp
void _reportFacebookActivateApp(String reason) {
_fbAppEvents.activateApp().then((_) {
if (FacebookConfig.debugLogs) {
_fbLog.d('activateApp手动: $reason');
}
});
}
/// 从后台回到前台时触发;冷启动依赖 initState 里的 addPostFrameCallback
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_reportFacebookActivateApp('lifecycle_resumed');
}
}
@override
Widget build(BuildContext context) {
return UserCreditsScope(
child: TabSelectorScope(
selectTab: (tab) => setState(() => _currentTab = tab),
child: MaterialApp(
title: 'AI Video App',
theme: AppTheme.light,
debugShowCheckedModeBanner: false,
navigatorObservers: [appRouteObserver],
initialRoute: '/',
builder: (context, child) {
return Stack(
fit: StackFit.expand,
children: [
SafeArea(
top: true,
left: false,
right: false,
bottom: false,
child: child ?? const SizedBox.shrink(),
),
FutureBuilder<void>(
future: AuthService.loginComplete,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return const SizedBox.shrink();
}
return Positioned.fill(
child: AbsorbPointer(
child: Container(
color: Colors.black.withValues(alpha: 0.2),
child: const Center(
child: CircularProgressIndicator(
color: AppColors.primary,
),
),
),
),
);
},
),
],
);
},
routes: {
'/': (_) => _MainScaffold(
currentTab: _currentTab,
onTabSelected: (tab) => setState(() => _currentTab = tab),
),
'/recharge': (_) => const RechargeScreen(),
'/generate': (ctx) {
final task = ModalRoute.of(ctx)?.settings.arguments as TaskItem?;
return GenerateVideoScreen(task: task);
},
'/progress': (ctx) {
final args = ModalRoute.of(ctx)?.settings.arguments;
final taskId = args is Map ? args['taskId'] : args;
final imagePath = args is Map ? args['imagePath'] as String? : null;
return GenerateProgressScreen(
taskId: taskId,
imagePath: imagePath,
);
},
'/result': (ctx) {
final mediaItem =
ModalRoute.of(ctx)?.settings.arguments as GalleryMediaItem?;
return GenerationResultScreen(mediaItem: mediaItem);
},
},
),
),
);
}
}
class _MainScaffold extends StatefulWidget {
const _MainScaffold({
required this.currentTab,
required this.onTabSelected,
});
final NavTab currentTab;
final ValueChanged<NavTab> onTabSelected;
@override
State<_MainScaffold> createState() => _MainScaffoldState();
}
class _MainScaffoldState extends State<_MainScaffold> with RouteAware {
bool _routeObserverSubscribed = false;
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (!_routeObserverSubscribed) {
final route = ModalRoute.of(context);
if (route is PageRoute<dynamic>) {
appRouteObserver.subscribe(this, route);
_routeObserverSubscribed = true;
}
}
}
@override
void dispose() {
if (_routeObserverSubscribed) {
appRouteObserver.unsubscribe(this);
}
super.dispose();
}
/// 挂在 `/` 的 [PageRoute] 上:任意子页面 pop 后底层重新露出时触发(比 [HomeScreen] 内订阅更可靠)
@override
void didPopNext() {
if (widget.currentTab == NavTab.home) {
homePlaybackResumeNonce.value++;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: widget.currentTab.index,
children: [
HomeScreen(isActive: widget.currentTab == NavTab.home),
GalleryScreen(isActive: widget.currentTab == NavTab.gallery),
ProfileScreen(isActive: widget.currentTab == NavTab.profile),
],
),
bottomNavigationBar: BottomNavBar(
currentTab: widget.currentTab,
onTabSelected: widget.onTabSelected,
),
);
}
}