init
This commit is contained in:
496
client/lib/core/routes/app_routes.dart
Normal file
496
client/lib/core/routes/app_routes.dart
Normal file
@@ -0,0 +1,496 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../features/auth/screens/splash_screen.dart';
|
||||
import '../../features/auth/screens/login_screen.dart';
|
||||
import '../../features/auth/screens/register_screen.dart';
|
||||
import '../../features/auth/screens/forgot_password_screen.dart';
|
||||
import '../../features/main/screens/main_app_screen.dart';
|
||||
import '../../features/learning/screens/learning_home_screen.dart';
|
||||
import '../../features/vocabulary/screens/vocabulary_home_screen.dart';
|
||||
import '../../features/vocabulary/screens/vocabulary_category_screen.dart';
|
||||
|
||||
import '../../features/vocabulary/screens/vocabulary_book_screen.dart';
|
||||
import '../../features/vocabulary/screens/word_learning_screen.dart';
|
||||
import '../../features/vocabulary/screens/smart_review_screen.dart';
|
||||
import '../../features/vocabulary/screens/vocabulary_test_screen.dart';
|
||||
import '../../features/vocabulary/screens/daily_words_screen.dart';
|
||||
import '../../features/vocabulary/screens/ai_recommendation_screen.dart';
|
||||
import '../../features/vocabulary/screens/word_book_screen.dart';
|
||||
import '../../features/vocabulary/screens/study_plan_screen.dart';
|
||||
import '../../features/vocabulary/models/word_model.dart';
|
||||
import '../../features/vocabulary/models/vocabulary_book_model.dart';
|
||||
import '../../features/vocabulary/models/review_models.dart';
|
||||
import '../../features/listening/screens/listening_home_screen.dart';
|
||||
import '../../features/listening/screens/listening_category_screen.dart';
|
||||
import '../../features/listening/screens/listening_exercise_detail_screen.dart';
|
||||
import '../../features/listening/screens/listening_difficulty_screen.dart';
|
||||
import '../../features/listening/screens/listening_stats_screen.dart';
|
||||
import '../../features/listening/models/listening_exercise_model.dart';
|
||||
// 移除静态数据依赖
|
||||
import '../../features/reading/screens/reading_home_screen.dart';
|
||||
import '../../features/writing/screens/writing_home_screen.dart';
|
||||
import '../../features/speaking/screens/speaking_home_screen.dart';
|
||||
import '../../features/comprehensive_test/screens/comprehensive_test_screen.dart';
|
||||
import '../../features/profile/screens/profile_home_screen.dart';
|
||||
import '../../features/profile/screens/profile_edit_screen.dart';
|
||||
import '../../features/profile/screens/settings_screen.dart';
|
||||
import '../../features/profile/screens/help_feedback_screen.dart';
|
||||
import '../../features/ai/pages/ai_main_page.dart';
|
||||
import '../../features/ai/pages/ai_writing_page.dart';
|
||||
import '../../features/ai/pages/ai_speaking_page.dart';
|
||||
import '../../features/home/screens/learning_stats_detail_screen.dart';
|
||||
import '../../features/notification/screens/notification_list_screen.dart';
|
||||
import '../widgets/not_found_screen.dart';
|
||||
|
||||
// 学习模式枚举
|
||||
enum LearningMode {
|
||||
normal,
|
||||
review,
|
||||
test
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// 路由名称常量
|
||||
class Routes {
|
||||
static const String splash = '/splash';
|
||||
static const String login = '/login';
|
||||
static const String register = '/register';
|
||||
static const String forgotPassword = '/forgot-password';
|
||||
static const String home = '/home';
|
||||
static const String learning = '/learning';
|
||||
static const String profile = '/profile';
|
||||
static const String editProfile = '/edit-profile';
|
||||
static const String settings = '/settings';
|
||||
static const String helpFeedback = '/help-feedback';
|
||||
static const String vocabularyHome = '/vocabulary';
|
||||
static const String vocabularyCategory = '/vocabulary/category';
|
||||
static const String vocabularyList = '/vocabulary/list';
|
||||
static const String vocabularyBook = '/vocabulary/book';
|
||||
static const String wordDetail = '/vocabulary/word';
|
||||
static const String vocabularyTest = '/vocabulary/test';
|
||||
static const String wordLearning = '/vocabulary/learning';
|
||||
static const String smartReview = '/vocabulary/smart-review';
|
||||
static const String dailyWords = '/vocabulary/daily-words';
|
||||
static const String aiRecommendation = '/vocabulary/ai-recommendation';
|
||||
static const String wordBook = '/vocabulary/word-book';
|
||||
static const String studyPlan = '/vocabulary/study-plan';
|
||||
static const String listeningHome = '/listening';
|
||||
static const String listeningExercise = '/listening/exercise';
|
||||
static const String listeningCategory = '/listening/category';
|
||||
static const String listeningExerciseDetail = '/listening/exercise-detail';
|
||||
static const String listeningDifficulty = '/listening/difficulty';
|
||||
static const String listeningStats = '/listening/stats';
|
||||
static const String readingHome = '/reading';
|
||||
static const String readingExercise = '/reading/exercise';
|
||||
static const String writingHome = '/writing';
|
||||
static const String writingExercise = '/writing/exercise';
|
||||
static const String speakingHome = '/speaking';
|
||||
static const String speakingExercise = '/speaking/exercise';
|
||||
static const String comprehensiveTest = '/comprehensive-test';
|
||||
static const String ai = '/ai';
|
||||
static const String aiWriting = '/ai/writing';
|
||||
static const String aiSpeaking = '/ai/speaking';
|
||||
static const String learningStatsDetail = '/learning-stats-detail';
|
||||
static const String notifications = '/notifications';
|
||||
}
|
||||
|
||||
/// 应用路由配置
|
||||
class AppRoutes {
|
||||
/// 路由映射表
|
||||
static final Map<String, WidgetBuilder> _routes = {
|
||||
Routes.splash: (context) => const SplashScreen(),
|
||||
Routes.login: (context) => const LoginScreen(),
|
||||
Routes.register: (context) => const RegisterScreen(),
|
||||
Routes.forgotPassword: (context) => const ForgotPasswordScreen(),
|
||||
Routes.home: (context) => const MainAppScreen(),
|
||||
Routes.learning: (context) => const LearningHomeScreen(),
|
||||
Routes.vocabularyHome: (context) => const VocabularyHomeScreen(),
|
||||
// TODO: 这些路由需要参数,暂时注释掉,后续通过onGenerateRoute处理
|
||||
// Routes.vocabularyList: (context) => const VocabularyBookScreen(),
|
||||
// Routes.wordDetail: (context) => const WordLearningScreen(),
|
||||
// Routes.vocabularyTest: (context) => const VocabularyTestScreen(),
|
||||
// Routes.wordLearning: (context) => const SmartReviewScreen(),
|
||||
Routes.dailyWords: (context) => const DailyWordsScreen(),
|
||||
Routes.aiRecommendation: (context) => const AIRecommendationScreen(),
|
||||
Routes.wordBook: (context) => const WordBookScreen(),
|
||||
Routes.studyPlan: (context) => const StudyPlanScreen(),
|
||||
Routes.listeningHome: (context) => const ListeningHomeScreen(),
|
||||
Routes.listeningDifficulty: (context) => const ListeningDifficultyScreen(),
|
||||
Routes.listeningStats: (context) => const ListeningStatsScreen(),
|
||||
Routes.readingHome: (context) => const ReadingHomeScreen(),
|
||||
Routes.writingHome: (context) => const WritingHomeScreen(),
|
||||
Routes.speakingHome: (context) => const SpeakingHomeScreen(),
|
||||
Routes.comprehensiveTest: (context) => const ComprehensiveTestScreen(),
|
||||
Routes.profile: (context) => const ProfileHomeScreen(),
|
||||
Routes.editProfile: (context) => const ProfileEditScreen(),
|
||||
Routes.settings: (context) => const SettingsScreen(),
|
||||
Routes.helpFeedback: (context) => const HelpFeedbackScreen(),
|
||||
Routes.ai: (context) => const AIMainPage(),
|
||||
Routes.aiWriting: (context) => const AIWritingPage(),
|
||||
Routes.aiSpeaking: (context) => const AISpeakingPage(),
|
||||
Routes.learningStatsDetail: (context) => const LearningStatsDetailScreen(),
|
||||
Routes.notifications: (context) => const NotificationListScreen(),
|
||||
// TODO: 添加其他页面路由
|
||||
};
|
||||
|
||||
/// 获取路由映射表
|
||||
static Map<String, WidgetBuilder> get routes => _routes;
|
||||
|
||||
/// 路由生成器
|
||||
static Route<dynamic>? onGenerateRoute(RouteSettings settings) {
|
||||
final String routeName = settings.name ?? '';
|
||||
final arguments = settings.arguments;
|
||||
|
||||
// 处理带参数的词汇学习路由
|
||||
switch (routeName) {
|
||||
case Routes.vocabularyCategory:
|
||||
if (arguments is Map<String, dynamic>) {
|
||||
final category = arguments['category'];
|
||||
if (category != null) {
|
||||
return MaterialPageRoute(
|
||||
builder: (context) => VocabularyCategoryScreen(category: category),
|
||||
settings: settings,
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Routes.vocabularyList:
|
||||
if (arguments is Map<String, dynamic>) {
|
||||
final vocabularyBook = arguments['vocabularyBook'];
|
||||
if (vocabularyBook != null) {
|
||||
return MaterialPageRoute(
|
||||
builder: (context) => VocabularyBookScreen(vocabularyBook: vocabularyBook),
|
||||
settings: settings,
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Routes.wordLearning:
|
||||
if (arguments is Map<String, dynamic>) {
|
||||
final vocabularyBook = arguments['vocabularyBook'];
|
||||
final specificWords = arguments['specificWords'];
|
||||
final mode = arguments['mode'];
|
||||
if (vocabularyBook != null) {
|
||||
return MaterialPageRoute(
|
||||
builder: (context) => WordLearningScreen(
|
||||
vocabularyBook: vocabularyBook,
|
||||
specificWords: specificWords,
|
||||
mode: mode ?? LearningMode.normal,
|
||||
),
|
||||
settings: settings,
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Routes.vocabularyTest:
|
||||
// 词汇测试路由,支持带参数和不带参数的情况
|
||||
if (arguments is Map<String, dynamic>) {
|
||||
final vocabularyBook = arguments['vocabularyBook'];
|
||||
final testType = arguments['testType'];
|
||||
final questionCount = arguments['questionCount'];
|
||||
return MaterialPageRoute(
|
||||
builder: (context) => VocabularyTestScreen(
|
||||
vocabularyBook: vocabularyBook,
|
||||
testType: testType ?? TestType.vocabularyLevel,
|
||||
questionCount: questionCount ?? 20,
|
||||
),
|
||||
settings: settings,
|
||||
);
|
||||
} else {
|
||||
// 没有参数时,使用默认设置
|
||||
return MaterialPageRoute(
|
||||
builder: (context) => const VocabularyTestScreen(
|
||||
testType: TestType.vocabularyLevel,
|
||||
questionCount: 20,
|
||||
),
|
||||
settings: settings,
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case Routes.wordDetail:
|
||||
if (arguments is Map<String, dynamic>) {
|
||||
final vocabularyBook = arguments['vocabularyBook'];
|
||||
final reviewMode = arguments['reviewMode'];
|
||||
final dailyTarget = arguments['dailyTarget'];
|
||||
return MaterialPageRoute(
|
||||
builder: (context) => SmartReviewScreen(
|
||||
vocabularyBook: vocabularyBook,
|
||||
reviewMode: reviewMode ?? ReviewMode.adaptive,
|
||||
dailyTarget: dailyTarget ?? 20,
|
||||
),
|
||||
settings: settings,
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case Routes.smartReview:
|
||||
// 智能复习路由,支持带参数和不带参数的情况
|
||||
if (arguments is Map<String, dynamic>) {
|
||||
final vocabularyBook = arguments['vocabularyBook'];
|
||||
final reviewMode = arguments['reviewMode'];
|
||||
final dailyTarget = arguments['dailyTarget'];
|
||||
return MaterialPageRoute(
|
||||
builder: (context) => SmartReviewScreen(
|
||||
vocabularyBook: vocabularyBook,
|
||||
reviewMode: reviewMode ?? ReviewMode.adaptive,
|
||||
dailyTarget: dailyTarget ?? 20,
|
||||
),
|
||||
settings: settings,
|
||||
);
|
||||
} else {
|
||||
// 没有参数时,使用默认设置
|
||||
return MaterialPageRoute(
|
||||
builder: (context) => const SmartReviewScreen(
|
||||
reviewMode: ReviewMode.adaptive,
|
||||
dailyTarget: 20,
|
||||
),
|
||||
settings: settings,
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
// 听力相关路由
|
||||
case Routes.listeningCategory:
|
||||
if (arguments is Map<String, dynamic>) {
|
||||
final type = arguments['type'] as ListeningExerciseType;
|
||||
final title = arguments['title'] as String;
|
||||
final category = ListeningCategory(
|
||||
id: type.toString(),
|
||||
name: title,
|
||||
description: '${title}练习材料',
|
||||
icon: Icons.headphones,
|
||||
exerciseCount: 0,
|
||||
type: type,
|
||||
);
|
||||
return MaterialPageRoute(
|
||||
builder: (context) => ListeningCategoryScreen(
|
||||
category: category,
|
||||
),
|
||||
settings: settings,
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case Routes.listeningExerciseDetail:
|
||||
if (arguments is Map<String, dynamic>) {
|
||||
final exerciseId = arguments['exerciseId'];
|
||||
if (exerciseId != null) {
|
||||
return MaterialPageRoute(
|
||||
builder: (context) => ListeningExerciseDetailScreen(
|
||||
exerciseId: exerciseId,
|
||||
),
|
||||
settings: settings,
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 默认路由处理
|
||||
final WidgetBuilder? builder = _routes[routeName];
|
||||
if (builder != null) {
|
||||
return MaterialPageRoute(
|
||||
builder: builder,
|
||||
settings: settings,
|
||||
);
|
||||
}
|
||||
|
||||
// 未找到路由时的处理
|
||||
return MaterialPageRoute(
|
||||
builder: (context) => const NotFoundScreen(),
|
||||
settings: settings,
|
||||
);
|
||||
}
|
||||
|
||||
/// 路由守卫 - 检查是否需要认证
|
||||
static bool requiresAuth(String routeName) {
|
||||
const publicRoutes = [
|
||||
Routes.splash,
|
||||
Routes.login,
|
||||
Routes.register,
|
||||
Routes.forgotPassword,
|
||||
];
|
||||
|
||||
return !publicRoutes.contains(routeName);
|
||||
}
|
||||
|
||||
/// 获取初始路由
|
||||
static String getInitialRoute(bool isLoggedIn) {
|
||||
return isLoggedIn ? Routes.home : Routes.splash;
|
||||
}
|
||||
}
|
||||
|
||||
/// 启动页面
|
||||
class SplashScreen extends StatefulWidget {
|
||||
const SplashScreen({super.key});
|
||||
|
||||
@override
|
||||
State<SplashScreen> createState() => _SplashScreenState();
|
||||
}
|
||||
|
||||
class _SplashScreenState extends State<SplashScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeApp();
|
||||
}
|
||||
|
||||
Future<void> _initializeApp() async {
|
||||
// 初始化应用配置
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
|
||||
if (mounted) {
|
||||
// 导航到登录页面,让用户进行认证
|
||||
Navigator.of(context).pushReplacementNamed(Routes.login);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// TODO: 添加应用Logo
|
||||
Icon(
|
||||
Icons.school,
|
||||
size: 100,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
'AI英语学习',
|
||||
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'智能化英语学习平台',
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 48),
|
||||
const CircularProgressIndicator(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 404页面
|
||||
class NotFoundScreen extends StatelessWidget {
|
||||
const NotFoundScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('页面未找到'),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.error_outline,
|
||||
size: 100,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
'404',
|
||||
style: Theme.of(context).textTheme.headlineLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'抱歉,您访问的页面不存在',
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||
Routes.home,
|
||||
(route) => false,
|
||||
);
|
||||
},
|
||||
child: const Text('返回首页'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 路由导航辅助类
|
||||
class AppNavigator {
|
||||
/// 导航到指定页面
|
||||
static Future<T?> push<T extends Object?>(
|
||||
BuildContext context,
|
||||
String routeName, {
|
||||
Object? arguments,
|
||||
}) {
|
||||
return Navigator.of(context).pushNamed<T>(
|
||||
routeName,
|
||||
arguments: arguments,
|
||||
);
|
||||
}
|
||||
|
||||
/// 替换当前页面
|
||||
static Future<T?> pushReplacement<T extends Object?, TO extends Object?>(
|
||||
BuildContext context,
|
||||
String routeName, {
|
||||
Object? arguments,
|
||||
TO? result,
|
||||
}) {
|
||||
return Navigator.of(context).pushReplacementNamed<T, TO>(
|
||||
routeName,
|
||||
arguments: arguments,
|
||||
result: result,
|
||||
);
|
||||
}
|
||||
|
||||
/// 清空栈并导航到指定页面
|
||||
static Future<T?> pushAndRemoveUntil<T extends Object?>(
|
||||
BuildContext context,
|
||||
String routeName, {
|
||||
Object? arguments,
|
||||
bool Function(Route<dynamic>)? predicate,
|
||||
}) {
|
||||
return Navigator.of(context).pushNamedAndRemoveUntil<T>(
|
||||
routeName,
|
||||
predicate ?? (route) => false,
|
||||
arguments: arguments,
|
||||
);
|
||||
}
|
||||
|
||||
/// 返回上一页
|
||||
static void pop<T extends Object?>(BuildContext context, [T? result]) {
|
||||
Navigator.of(context).pop<T>(result);
|
||||
}
|
||||
|
||||
/// 返回到指定页面
|
||||
static void popUntil(BuildContext context, String routeName) {
|
||||
Navigator.of(context).popUntil(ModalRoute.withName(routeName));
|
||||
}
|
||||
|
||||
/// 检查是否可以返回
|
||||
static bool canPop(BuildContext context) {
|
||||
return Navigator.of(context).canPop();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user