software copyright
This commit is contained in:
@@ -0,0 +1,340 @@
|
||||
/// 自然写互动课堂手机端应用软件 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 {}
|
||||
Reference in New Issue
Block a user