Files
live_recorder/mobile/lib/app/app.dart
T

162 lines
4.5 KiB
Dart

import 'package:flutter/material.dart';
import 'package:live_recorder_mobile/app/app_bootstrap_controller.dart';
import 'package:live_recorder_mobile/app/app_dependencies.dart';
import 'package:live_recorder_mobile/app/app_scope.dart';
import 'package:live_recorder_mobile/app/app_theme.dart';
import 'package:live_recorder_mobile/core/config/api_config.dart';
import 'package:live_recorder_mobile/core/persistence/app_config_storage.dart';
import 'package:live_recorder_mobile/core/widgets/app_error_card.dart';
import 'package:live_recorder_mobile/features/live_recorder/presentation/pages/backend_setup_page.dart';
import 'package:live_recorder_mobile/features/live_recorder/presentation/pages/login_page.dart';
import 'package:live_recorder_mobile/features/live_recorder/presentation/pages/mobile_shell_page.dart';
class LiveRecorderBootstrap extends StatefulWidget {
const LiveRecorderBootstrap({
super.key,
required this.config,
});
final ApiConfig config;
@override
State<LiveRecorderBootstrap> createState() => _LiveRecorderBootstrapState();
}
class _LiveRecorderBootstrapState extends State<LiveRecorderBootstrap> {
late final AppBootstrapController<AppDependencies> _bootstrapController =
AppBootstrapController<AppDependencies>(
config: widget.config,
configStorage: AppConfigStorage(),
dependenciesFactory: (String baseUrl) => AppDependencies.create(baseUrl: baseUrl),
);
@override
void initState() {
super.initState();
_bootstrapController.initialize();
}
@override
void dispose() {
_bootstrapController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _bootstrapController,
builder: (BuildContext context, _) {
return AppScope(
backendConfig: _bootstrapController,
dependencies: _bootstrapController.dependencies,
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'LiveRecorder',
theme: buildLiveRecorderTheme(),
home: _BootstrapHome(
bootstrapController: _bootstrapController,
),
),
);
},
);
}
}
class _BootstrapHome extends StatelessWidget {
const _BootstrapHome({
required this.bootstrapController,
});
final AppBootstrapController<AppDependencies> bootstrapController;
@override
Widget build(BuildContext context) {
if (bootstrapController.isInitializing) {
return const _LoadingSplashPage();
}
if (bootstrapController.initializationErrorMessage != null) {
return _BootstrapErrorPage(
message: bootstrapController.initializationErrorMessage!,
onRetry: bootstrapController.initialize,
);
}
if (!bootstrapController.hasConfiguredBackend) {
return BackendSetupPage(
bootstrapController: bootstrapController,
);
}
final dependencies = bootstrapController.dependencies;
if (dependencies == null) {
return _BootstrapErrorPage(
message: '后端配置未能正确加载,请重试',
onRetry: bootstrapController.initialize,
);
}
return ListenableBuilder(
listenable: dependencies.sessionController,
builder: (BuildContext context, _) {
if (dependencies.sessionController.isRestoring) {
return const _LoadingSplashPage();
}
if (dependencies.sessionController.isLoggedIn) {
return MobileShellPage(
dependencies: dependencies,
);
}
return LoginPage(
sessionController: dependencies.sessionController,
);
},
);
}
}
class _LoadingSplashPage extends StatelessWidget {
const _LoadingSplashPage();
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
}
class _BootstrapErrorPage extends StatelessWidget {
const _BootstrapErrorPage({
required this.message,
required this.onRetry,
});
final String message;
final VoidCallback onRetry;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 420),
child: AppErrorCard(
message: message,
onRetry: onRetry,
),
),
),
),
);
}
}