/// Flutter 入口:先渲染 [AppBootstrap],再异步 [Global.init]。
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const AppBootstrap());
}
/// 可渲染的启动根组件,区分加载、失败和就绪。
class AppBootstrap extends StatefulWidget {
const AppBootstrap({super.key, this.initialize, this.readyBuilder});
/// 测试可注入初始化;生产用 [Global.init]。
final Future<void> Function()? initialize;
/// 测试可替换就绪页。
final WidgetBuilder? readyBuilder;
@override
State<AppBootstrap> createState() => _AppBootstrapState();
}
class _AppBootstrapState extends State<AppBootstrap> {
Object? _error;
bool _ready = false;
bool _loading = true;
@override
void initState() {
super.initState();
_initialize();
}
Future<void> _initialize() async {
setState(() {
_loading = true;
_error = null;
});
try {
await (widget.initialize ?? Global.init)();
if (!mounted) return;
setState(() {
_ready = true;
_loading = false;
});
} catch (error) {
if (!mounted) return;
setState(() {
_error = error;
_loading = false;
});
}
}
@override
Widget build(BuildContext context) {
if (_ready) return widget.readyBuilder?.call(context) ?? const App();
return GetMaterialApp(
debugShowCheckedModeBanner: false,
translations: AppTranslations(),
locale: AppTranslations.fallbackLocale,
fallbackLocale: AppTranslations.fallbackLocale,
supportedLocales: AppTranslations.supportedLocales,
localizationsDelegates: GlobalMaterialLocalizations.delegates,
theme: AppTheme.lightTheme,
darkTheme: AppTheme.darkTheme,
home: Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(32),
child: _loading
? const CircularProgressIndicator()
: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.error_outline_rounded, size: 48),
const SizedBox(height: 16),
Text(
TrKeys.initializationFailed.tr,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 8),
Text(_error.toString(), textAlign: TextAlign.center),
const SizedBox(height: 20),
FilledButton(
onPressed: _initialize,
child: Text(TrKeys.retry.tr),
),
],
),
),
),
),
);
}
}
/// 根组件:亮暗主题 + GoRouter。
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return AdaptiveTheme(
light: AppTheme.lightTheme,
dark: AppTheme.darkTheme,
initial: Global.savedThemeMode ?? AdaptiveThemeMode.system,
builder: (theme, darkTheme) {
return GetMaterialApp.router(
title: AppConfig.appName,
debugShowCheckedModeBanner: false,
translations: AppTranslations(),
locale: Get.isRegistered<LocaleService>()
? Get.find<LocaleService>().locale
: AppTranslations.fallbackLocale,
fallbackLocale: AppTranslations.fallbackLocale,
supportedLocales: AppTranslations.supportedLocales,
localizationsDelegates: GlobalMaterialLocalizations.delegates,
theme: theme,
darkTheme: darkTheme,
builder: (context, child) {
ScreenUtil.init(
context,
designSize: Size(
AppConfig.ui.designWidth,
AppConfig.ui.designHeight,
),
);
return child ?? const SizedBox.shrink();
},
routerDelegate: AppRouter.router.routerDelegate,
routeInformationParser: AppRouter.router.routeInformationParser,
routeInformationProvider: AppRouter.router.routeInformationProvider,
backButtonDispatcher: AppRouter.router.backButtonDispatcher,
);
},
);
}
}