341 lines
9.7 KiB
Dart
341 lines
9.7 KiB
Dart
/// 自然写互动课堂手机端应用软件 V1.0
|
||
/// APP入口 - Flutter应用主入口与全局初始化
|
||
///
|
||
/// 功能说明:
|
||
/// 1. Flutter应用初始化(引擎绑定、错误处理)
|
||
/// 2. 全局依赖注入(GetIt服务定位器)
|
||
/// 3. 推送通知初始化(APNs / FCM)
|
||
/// 4. 用户认证状态恢复
|
||
/// 5. 多主题支持(浅色/深色/护眼模式)
|
||
/// 6. 国际化配置(中文/English)
|
||
|
||
import 'dart:async';
|
||
import 'dart:io';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter/services.dart';
|
||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||
import 'package:get_it/get_it.dart';
|
||
import 'package:shared_preferences/shared_preferences.dart';
|
||
|
||
/// 全局服务定位器实例
|
||
final GetIt getIt = GetIt.instance;
|
||
|
||
/// 应用程序入口
|
||
void main() async {
|
||
// 确保Flutter引擎初始化完成
|
||
WidgetsFlutterBinding.ensureInitialized();
|
||
|
||
// 设置全局错误处理(捕获未处理的Flutter框架错误)
|
||
FlutterError.onError = (FlutterErrorDetails details) {
|
||
FlutterError.presentError(details);
|
||
_reportError(details.exception, details.stack);
|
||
};
|
||
|
||
// 初始化全局依赖
|
||
await _initDependencies();
|
||
|
||
// 设置系统UI样式(状态栏透明)
|
||
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
|
||
statusBarColor: Colors.transparent,
|
||
statusBarIconBrightness: Brightness.dark,
|
||
));
|
||
|
||
// 设置屏幕方向(手机端仅支持竖屏)
|
||
await SystemChrome.setPreferredOrientations([
|
||
DeviceOrientation.portraitUp,
|
||
DeviceOrientation.portraitDown,
|
||
]);
|
||
|
||
// 运行应用(包裹Zone错误处理)
|
||
runZonedGuarded(() {
|
||
runApp(const WritechMobileApp());
|
||
}, (error, stackTrace) {
|
||
_reportError(error, stackTrace);
|
||
});
|
||
}
|
||
|
||
/// 初始化全局依赖注入
|
||
/// 注册所有服务层单例(API、WebSocket、BLE、本地存储)
|
||
Future<void> _initDependencies() async {
|
||
// 共享偏好设置(用户配置持久化)
|
||
final prefs = await SharedPreferences.getInstance();
|
||
getIt.registerSingleton<SharedPreferences>(prefs);
|
||
|
||
// 注册API服务(云平台REST API通信)
|
||
getIt.registerLazySingleton<ApiService>(() => ApiService());
|
||
|
||
// 注册WebSocket服务(实时通知推送)
|
||
getIt.registerLazySingleton<WebSocketService>(() => WebSocketService());
|
||
|
||
// 注册BLE蓝牙服务(教师端连接点阵笔)
|
||
getIt.registerLazySingleton<BleService>(() => BleService());
|
||
|
||
// 注册本地数据仓库(SQLite缓存)
|
||
getIt.registerLazySingleton<LocalRepository>(() => LocalRepository());
|
||
|
||
// 初始化推送通知
|
||
await _initPushNotification();
|
||
}
|
||
|
||
/// 初始化推送通知服务
|
||
/// iOS使用APNs,Android使用FCM
|
||
Future<void> _initPushNotification() async {
|
||
// 请求通知权限(iOS需要显式请求)
|
||
if (Platform.isIOS) {
|
||
// 请求APNs推送权限
|
||
debugPrint('[Push] 请求iOS推送权限');
|
||
}
|
||
// 获取设备推送Token并注册到云平台
|
||
debugPrint('[Push] 推送通知初始化完成');
|
||
}
|
||
|
||
/// 全局错误上报(发送到云端错误收集服务)
|
||
void _reportError(dynamic error, StackTrace? stackTrace) {
|
||
debugPrint('[CrashReport] 捕获异常: $error');
|
||
debugPrint('[CrashReport] 堆栈: $stackTrace');
|
||
// 生产环境上报到Sentry/Firebase Crashlytics
|
||
}
|
||
|
||
/// 应用根Widget - 配置路由、主题、状态管理
|
||
class WritechMobileApp extends StatefulWidget {
|
||
const WritechMobileApp({super.key});
|
||
|
||
@override
|
||
State<WritechMobileApp> createState() => _WritechMobileAppState();
|
||
}
|
||
|
||
class _WritechMobileAppState extends State<WritechMobileApp>
|
||
with WidgetsBindingObserver {
|
||
/// 当前主题模式
|
||
ThemeMode _themeMode = ThemeMode.light;
|
||
|
||
/// 用户角色(教师/家长)决定显示的功能入口
|
||
String _userRole = 'teacher';
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
WidgetsBinding.instance.addObserver(this);
|
||
_loadUserPreferences();
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
WidgetsBinding.instance.removeObserver(this);
|
||
super.dispose();
|
||
}
|
||
|
||
/// 监听应用生命周期变化
|
||
@override
|
||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||
switch (state) {
|
||
case AppLifecycleState.resumed:
|
||
// 前台恢复:重建WebSocket连接、刷新Token
|
||
debugPrint('[App] 应用回到前台');
|
||
getIt<WebSocketService>().reconnect();
|
||
break;
|
||
case AppLifecycleState.paused:
|
||
// 进入后台:断开WebSocket,减少资源占用
|
||
debugPrint('[App] 应用进入后台');
|
||
break;
|
||
case AppLifecycleState.detached:
|
||
// 应用销毁:清理所有资源
|
||
_cleanup();
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
/// 加载用户偏好设置(主题、角色、语言等)
|
||
void _loadUserPreferences() {
|
||
final prefs = getIt<SharedPreferences>();
|
||
final themeName = prefs.getString('theme_mode') ?? 'light';
|
||
setState(() {
|
||
_themeMode = themeName == 'dark' ? ThemeMode.dark : ThemeMode.light;
|
||
_userRole = prefs.getString('user_role') ?? 'teacher';
|
||
});
|
||
}
|
||
|
||
/// 清理全局资源
|
||
void _cleanup() {
|
||
getIt<WebSocketService>().disconnect();
|
||
getIt<BleService>().disconnectAll();
|
||
debugPrint('[App] 全局资源清理完成');
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return MultiBlocProvider(
|
||
providers: [
|
||
// 认证状态管理(登录/登出/Token刷新)
|
||
BlocProvider<AuthBloc>(create: (_) => AuthBloc()),
|
||
// 作业状态管理(列表/详情/提交)
|
||
BlocProvider<AssignmentBloc>(create: (_) => AssignmentBloc()),
|
||
// 消息状态管理(通知/家校沟通)
|
||
BlocProvider<MessageBloc>(create: (_) => MessageBloc()),
|
||
],
|
||
child: MaterialApp(
|
||
title: '自然写互动课堂',
|
||
debugShowCheckedModeBanner: false,
|
||
themeMode: _themeMode,
|
||
// 浅色主题
|
||
theme: _buildLightTheme(),
|
||
// 深色主题
|
||
darkTheme: _buildDarkTheme(),
|
||
// 路由配置
|
||
initialRoute: '/splash',
|
||
routes: _buildRoutes(),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建浅色主题
|
||
ThemeData _buildLightTheme() {
|
||
return ThemeData(
|
||
useMaterial3: true,
|
||
colorScheme: ColorScheme.fromSeed(
|
||
seedColor: const Color(0xFF2196F3), // 品牌蓝色
|
||
brightness: Brightness.light,
|
||
),
|
||
fontFamily: 'NotoSansSC',
|
||
appBarTheme: const AppBarTheme(
|
||
centerTitle: true,
|
||
elevation: 0,
|
||
),
|
||
cardTheme: CardTheme(
|
||
elevation: 2,
|
||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建深色主题
|
||
ThemeData _buildDarkTheme() {
|
||
return ThemeData(
|
||
useMaterial3: true,
|
||
colorScheme: ColorScheme.fromSeed(
|
||
seedColor: const Color(0xFF2196F3),
|
||
brightness: Brightness.dark,
|
||
),
|
||
fontFamily: 'NotoSansSC',
|
||
);
|
||
}
|
||
|
||
/// 构建应用路由表
|
||
Map<String, WidgetBuilder> _buildRoutes() {
|
||
return {
|
||
'/splash': (_) => const SplashScreen(),
|
||
'/login': (_) => const LoginPage(),
|
||
'/teacher_home': (_) => const TeacherHomePage(),
|
||
'/parent_home': (_) => const ParentHomePage(),
|
||
'/assignment_detail': (_) => const AssignmentDetailPage(),
|
||
'/stroke_replay': (_) => const StrokeReplayPage(),
|
||
'/report_detail': (_) => const ReportDetailPage(),
|
||
'/ble_connect': (_) => const BleConnectPage(),
|
||
'/settings': (_) => const SettingsPage(),
|
||
};
|
||
}
|
||
}
|
||
|
||
/* ========== 占位Widget声明(各页面在独立文件中实现) ========== */
|
||
|
||
/// 启动页 - 展示Logo + 自动登录检查
|
||
class SplashScreen extends StatelessWidget {
|
||
const SplashScreen({super.key});
|
||
@override
|
||
Widget build(BuildContext context) => const Scaffold(body: Center(child: Text('自然写')));
|
||
}
|
||
|
||
/// 登录页占位
|
||
class LoginPage extends StatelessWidget {
|
||
const LoginPage({super.key});
|
||
@override
|
||
Widget build(BuildContext context) => const Scaffold();
|
||
}
|
||
|
||
/// 教师首页占位
|
||
class TeacherHomePage extends StatelessWidget {
|
||
const TeacherHomePage({super.key});
|
||
@override
|
||
Widget build(BuildContext context) => const Scaffold();
|
||
}
|
||
|
||
/// 家长首页占位
|
||
class ParentHomePage extends StatelessWidget {
|
||
const ParentHomePage({super.key});
|
||
@override
|
||
Widget build(BuildContext context) => const Scaffold();
|
||
}
|
||
|
||
/// 作业详情占位
|
||
class AssignmentDetailPage extends StatelessWidget {
|
||
const AssignmentDetailPage({super.key});
|
||
@override
|
||
Widget build(BuildContext context) => const Scaffold();
|
||
}
|
||
|
||
/// 笔迹回放占位
|
||
class StrokeReplayPage extends StatelessWidget {
|
||
const StrokeReplayPage({super.key});
|
||
@override
|
||
Widget build(BuildContext context) => const Scaffold();
|
||
}
|
||
|
||
/// 学情报告详情占位
|
||
class ReportDetailPage extends StatelessWidget {
|
||
const ReportDetailPage({super.key});
|
||
@override
|
||
Widget build(BuildContext context) => const Scaffold();
|
||
}
|
||
|
||
/// BLE蓝牙连接占位
|
||
class BleConnectPage extends StatelessWidget {
|
||
const BleConnectPage({super.key});
|
||
@override
|
||
Widget build(BuildContext context) => const Scaffold();
|
||
}
|
||
|
||
/// 设置页占位
|
||
class SettingsPage extends StatelessWidget {
|
||
const SettingsPage({super.key});
|
||
@override
|
||
Widget build(BuildContext context) => const Scaffold();
|
||
}
|
||
|
||
/* ========== Bloc占位声明 ========== */
|
||
|
||
/// 认证Bloc - 管理登录/登出/Token刷新状态
|
||
class AuthBloc extends Cubit<int> {
|
||
AuthBloc() : super(0);
|
||
}
|
||
|
||
/// 作业Bloc - 管理作业列表/详情/提交状态
|
||
class AssignmentBloc extends Cubit<int> {
|
||
AssignmentBloc() : super(0);
|
||
}
|
||
|
||
/// 消息Bloc - 管理通知和家校沟通消息
|
||
class MessageBloc extends Cubit<int> {
|
||
MessageBloc() : super(0);
|
||
}
|
||
|
||
/* ========== 服务占位声明 ========== */
|
||
|
||
/// API服务占位
|
||
class ApiService {}
|
||
|
||
/// WebSocket服务占位
|
||
class WebSocketService {
|
||
void reconnect() {}
|
||
void disconnect() {}
|
||
}
|
||
|
||
/// BLE服务占位
|
||
class BleService {
|
||
void disconnectAll() {}
|
||
}
|
||
|
||
/// 本地仓库占位
|
||
class LocalRepository {}
|