6 コミット 22b310a937 ... a1a0f46f0f

作者 SHA1 メッセージ 日付
  ValiZhang a1a0f46f0f 本commit主要提交下界面相关变更,提交了一些界面相关的 demo 代码 9 ヶ月 前
  ValiZhang 5e8b078191 空null问题解决 11 ヶ月 前
  ValiZhang 5bac445180 初步调通 11 ヶ月 前
  ValiZhang bd265d2450 仍存在问题的一波提交 11 ヶ月 前
  ValiZhang d10eb77137 调整下应用icon和名称 11 ヶ月 前
  ValiZhang 736ddfa257 跑个 demo 11 ヶ月 前
56 ファイル変更1794 行追加144 行削除
  1. 2 1
      android/app/build.gradle.kts
  2. 1 1
      android/app/src/main/AndroidManifest.xml
  3. BIN
      android/app/src/main/res/mipmap-hdpi/ic_launcher.png
  4. BIN
      android/app/src/main/res/mipmap-hdpi/ic_launcher.webp
  5. BIN
      android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
  6. BIN
      android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
  7. BIN
      android/app/src/main/res/mipmap-mdpi/ic_launcher.png
  8. BIN
      android/app/src/main/res/mipmap-mdpi/ic_launcher.webp
  9. BIN
      android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
  10. BIN
      android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
  11. BIN
      android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  12. BIN
      android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
  13. BIN
      android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
  14. BIN
      android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
  15. BIN
      android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  16. BIN
      android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
  17. BIN
      android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
  18. BIN
      android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
  19. BIN
      android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  20. BIN
      android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
  21. BIN
      android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
  22. BIN
      android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
  23. 1 0
      android/gradle.properties
  24. 3 0
      devtools_options.yaml
  25. 0 0
      lib/app/app.dart
  26. 0 0
      lib/app/routes/app_router.dart
  27. 0 0
      lib/app/theme/app_theme.dart
  28. 35 0
      lib/core/constants/options.dart
  29. 99 0
      lib/core/models/challenge_model.dart
  30. 16 0
      lib/core/models/dropdown_item.dart
  31. 23 0
      lib/core/models/option_model.dart
  32. 6 0
      lib/core/proviers/database_providers.dart
  33. 10 0
      lib/core/proviers/repository_providers.dart
  34. 0 0
      lib/core/repositories/card_repository.dart
  35. 14 0
      lib/core/repositories/challenge_repository.dart
  36. 55 0
      lib/core/repositories/mock_challenge_repository.dart
  37. 121 0
      lib/core/repositories/sqlite_challenge_repository.dart
  38. 47 0
      lib/core/routes/app_router.dart
  39. 0 0
      lib/core/services/challenge_service.dart
  40. 50 0
      lib/data/local/database_helper.dart
  41. 65 0
      lib/features/challenge/view_models/challenge_add_vm.dart
  42. 30 0
      lib/features/challenge/view_models/challenge_detail_vm.dart
  43. 54 0
      lib/features/challenge/view_models/challenge_edit_vm.dart
  44. 46 0
      lib/features/challenge/view_models/challenge_list_vm.dart
  45. 198 0
      lib/features/challenge/views/add_challenge_screen.dart
  46. 82 0
      lib/features/challenge/views/challenge_detail_screen.dart
  47. 72 0
      lib/features/challenge/views/challenge_list_screen.dart
  48. 109 0
      lib/features/challenge/views/edit_challenge_screen.dart
  49. 359 0
      lib/features/challenge/views/mock_page.dart
  50. 37 0
      lib/features/challenge/views/widgets/challenge_list_card_widget.dart
  51. 69 0
      lib/features/widgets/app_dropdown.dart
  52. 11 110
      lib/main.dart
  53. 2 0
      macos/Flutter/GeneratedPluginRegistrant.swift
  54. 169 1
      pubspec.lock
  55. 8 1
      pubspec.yaml
  56. 0 30
      test/widget_test.dart

+ 2 - 1
android/app/build.gradle.kts

@@ -8,7 +8,8 @@ plugins {
 android {
     namespace = "com.example.japp_flutter"
     compileSdk = flutter.compileSdkVersion
-    ndkVersion = flutter.ndkVersion
+    // ndkVersion = flutter.ndkVersion
+    ndkVersion = project.properties["org.gradle.ndk.version"] as String
 
     compileOptions {
         sourceCompatibility = JavaVersion.VERSION_11

+ 1 - 1
android/app/src/main/AndroidManifest.xml

@@ -1,6 +1,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
     <application
-        android:label="japp_flutter"
+        android:label="JAPP"
         android:name="${applicationName}"
         android:icon="@mipmap/ic_launcher">
         <activity

BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.webp


BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp


BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp


BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.webp


BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp


BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp


BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp


BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp


BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp


BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp


BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp


BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp


BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp


BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp


BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp


+ 1 - 0
android/gradle.properties

@@ -3,3 +3,4 @@ systemProp.https.proxyHost=mirrors.aliyun.com
 org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
 android.useAndroidX=true
 android.enableJetifier=true
+org.gradle.ndk.version=28.0.12674087

+ 3 - 0
devtools_options.yaml

@@ -0,0 +1,3 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:

+ 0 - 0
lib/app/app.dart


+ 0 - 0
lib/app/routes/app_router.dart


+ 0 - 0
lib/app/theme/app_theme.dart


+ 35 - 0
lib/core/constants/options.dart

@@ -0,0 +1,35 @@
+import 'package:flutter/material.dart';
+import 'package:japp_flutter/core/models/option_model.dart';
+import 'package:japp_flutter/core/models/dropdown_item.dart';
+
+// 通用状态选项
+const List<OptionModel<String>> statusOptions = [
+  OptionModel(value: 'active', label: '激活', icon: Icons.check_circle),
+  OptionModel(value: 'inactive', label: '未激活', icon: Icons.pause_circle),
+  OptionModel(value: 'archived', label: '已归档', icon: Icons.archive),
+];
+
+// 通用优先级选项
+const List<OptionModel<int>> priorityOptions = [
+  OptionModel(value: 1, label: '低'),
+  OptionModel(value: 2, label: '中'),
+  OptionModel(value: 3, label: '高'),
+];
+
+// 通用优先级选项
+const List<OptionModel<int>> difficultOptions = [
+  OptionModel(value: 1, label: '简单'),
+  OptionModel(value: 2, label: '普通'),
+  OptionModel(value: 3, label: '困难'),
+  OptionModel(value: 4, label: '地狱'),
+];
+
+
+
+// 挑战目标难度选项
+const List<DropdownItem<int>> challengeDifficultyOpts = [
+  DropdownItem(value: 1, label: '简单', icon: Icons.sentiment_very_satisfied),
+  DropdownItem(value: 2, label: '普通',icon: Icons.sentiment_satisfied),
+  DropdownItem(value: 3, label: '困难',icon:Icons.sentiment_dissatisfied),
+  DropdownItem(value: 4, label: '地狱',icon: Icons.sentiment_very_dissatisfied),
+];

+ 99 - 0
lib/core/models/challenge_model.dart

@@ -0,0 +1,99 @@
+import 'package:flutter/foundation.dart';
+
+// 待办
+@immutable
+class ChallengeModel {
+  final int? id;
+  final int actionType; // 1-愿景 2-战略目标 3-挑战目标 4-待办目标
+  final String title;
+  final String description;
+  final String cover;
+  final int sort;
+  final int status; // 1-正常 2-进行中 3-已完成 4-失败放弃
+  final int difficulty; // 1-简单 2-正常 3-困难 4-地狱
+  final String remark;
+  final DateTime? startDate;
+  final DateTime? endDate;
+  final DateTime? finishDate;
+  final DateTime? planFinishDate;
+  final int parentId;
+
+  const ChallengeModel({
+    required this.id,
+    required this.actionType,
+    required this.title,
+    required this.description,
+    required this.cover,
+    required this.sort,
+    required this.status,
+    required this.difficulty,
+    required this.remark,
+    required this.startDate,
+    required this.endDate,
+    required this.finishDate,
+    required this.parentId,
+    required this.planFinishDate,
+  });
+
+  factory ChallengeModel.fromJson(Map<String, dynamic> json) {
+    return ChallengeModel(
+      id: json['id'] as int?,
+      title: json['title'] as String,
+      description: json['description'] as String,
+      startDate: DateTime.parse(json['start_date'] as String),
+      endDate: DateTime.parse(json['end_date'] as String),
+      cover: json['cover'] as String,
+      sort: json['sort'] as int,
+      status: json['status'] as int,
+      remark: json['remark'] as String,
+      finishDate: DateTime.parse(json['finish_date'] as String),
+      parentId: json['parent_id'] as int, 
+      actionType: 0, 
+      difficulty: 0, 
+      planFinishDate: DateTime.parse(json['end_date'] as String),
+    );
+  }
+
+  /// 转换为JSON
+  // Map<String, dynamic> toJson() => {
+  //   if (id != null) 'id': id,
+  //   'title': title,
+  //   'description': description,
+  //   'startDate': startDate.toIso8601String(),
+  //   'endDate': endDate.toIso8601String(),
+  // };
+
+  ChallengeModel copyWith({
+    int? id,
+    int? actionType,
+    String? title,
+    String? description,
+    int? status,
+    int? sort,
+    String? cover,
+    String? remark,
+    int? difficulty,
+    DateTime? startDate,
+    DateTime? endDate,
+    DateTime? finishDate,
+    DateTime? planFinishDate,
+    int? parentId,
+  }) {
+    return ChallengeModel(
+      id: id ?? this.id,
+      actionType: actionType ?? this.actionType,
+      title: title ?? this.title,
+      description: description ?? this.description,
+      startDate: startDate ?? this.startDate,
+      endDate: endDate ?? this.endDate, 
+      cover: cover ?? this.cover, 
+      sort: sort ?? this.sort, 
+      status: status ?? this.status, 
+      remark: remark ?? this.remark,
+      finishDate: finishDate ?? this.finishDate,
+      planFinishDate: planFinishDate ?? this.planFinishDate,
+      parentId: parentId ?? this.parentId,
+      difficulty: difficulty ?? this.difficulty,
+    );
+  }
+}

+ 16 - 0
lib/core/models/dropdown_item.dart

@@ -0,0 +1,16 @@
+import 'package:flutter/material.dart';
+
+
+class DropdownItem<T> {
+  final T value;
+  final String label;
+  final IconData? icon;
+  final Color? iconColor;
+
+  const DropdownItem({
+    required this.value,
+    required this.label,
+    this.icon,
+    this.iconColor,
+  });
+}

+ 23 - 0
lib/core/models/option_model.dart

@@ -0,0 +1,23 @@
+import 'package:flutter/material.dart';
+
+class OptionModel<T> {
+  final T value;
+  final String label;
+  final IconData? icon;
+  
+  const OptionModel({
+    required this.value,
+    required this.label,
+    this.icon,
+  });
+  
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      other is OptionModel &&
+          runtimeType == other.runtimeType &&
+          value == other.value;
+  
+  @override
+  int get hashCode => value.hashCode;
+}

+ 6 - 0
lib/core/proviers/database_providers.dart

@@ -0,0 +1,6 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:japp_flutter/data/local/database_helper.dart';
+
+final databaseHelperProvider = Provider<DatabaseHelper>((ref) {
+  return DatabaseHelper(); // 单例模式
+});

+ 10 - 0
lib/core/proviers/repository_providers.dart

@@ -0,0 +1,10 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:japp_flutter/core/proviers/database_providers.dart';
+import 'package:japp_flutter/core/repositories/challenge_repository.dart';
+import 'package:japp_flutter/core/repositories/sqlite_challenge_repository.dart';
+
+// 基础 Repository 提供者(全局可用)
+final challengeRepositoryProvider = Provider<ChallengeRepository>((ref) {
+  // 依赖注入 DatabaseHelper
+  return SqliteChallengeRepository(ref.read(databaseHelperProvider));
+});

+ 0 - 0
lib/core/repositories/card_repository.dart


+ 14 - 0
lib/core/repositories/challenge_repository.dart

@@ -0,0 +1,14 @@
+import 'package:japp_flutter/core/models/challenge_model.dart';
+
+abstract class ChallengeRepository {
+  Future<List<ChallengeModel>> getChallenges({
+    int page=1,
+    int pageSize = 20,
+    int actionType= 1,
+  });
+  Future<ChallengeModel?> getChallengeById(int id);
+  Future<ChallengeModel> addChallenge(ChallengeModel challenge);
+  Future<ChallengeModel> updateChallenge(ChallengeModel challenge);
+  Future<void> deleteChallenge(int id);
+
+}

+ 55 - 0
lib/core/repositories/mock_challenge_repository.dart

@@ -0,0 +1,55 @@
+import 'package:japp_flutter/core/models/challenge_model.dart';
+import 'package:japp_flutter/core/repositories/challenge_repository.dart';
+
+
+class MockChallengeRepository implements ChallengeRepository {
+  final List<ChallengeModel> _mockData = [
+    ChallengeModel(
+      id: 1,
+      title: 'Learn Flutter',
+      startDate: DateTime.now(),
+      endDate: DateTime.now().add(Duration(days: 30)), 
+      description: '', 
+      difficulty: 1, actionType: 1, cover: '', sort: 1, status: 1, remark: '', finishDate: DateTime.now(), parentId: 0, planFinishDate: DateTime.now(),
+      // 其他字段...
+    ),
+  ];
+
+  @override
+  Future<List<ChallengeModel>> getChallenges({
+        int page=1,
+    int pageSize = 20,
+    int actionType= 1,
+  }) async {
+    await Future.delayed(Duration(seconds: 1)); // 模拟网络延迟
+    return _mockData;
+  }
+  
+  @override
+  Future<ChallengeModel> addChallenge(ChallengeModel challenge) {
+    // TODO: implement addChallenge
+    throw UnimplementedError();
+  }
+  
+  @override
+  Future<void> deleteChallenge(int id) {
+    // TODO: implement deleteChallenge
+    throw UnimplementedError();
+  }
+  
+  @override
+  Future<ChallengeModel?> getChallengeById(int id) async{
+    await Future.delayed(Duration(seconds: 1)); // 模拟网络延迟
+    return _mockData[0];
+  }
+  
+  
+  @override
+  Future<ChallengeModel> updateChallenge(ChallengeModel challenge) {
+    // TODO: implement updateChallenge
+    throw UnimplementedError();
+  }
+  
+
+  // 其他方法实现...
+}

+ 121 - 0
lib/core/repositories/sqlite_challenge_repository.dart

@@ -0,0 +1,121 @@
+import 'package:japp_flutter/core/models/challenge_model.dart';
+import 'package:japp_flutter/core/repositories/challenge_repository.dart';
+import 'package:japp_flutter/data/local/database_helper.dart';
+import 'package:sqflite/sqflite.dart';
+
+class SqliteChallengeRepository implements ChallengeRepository {
+  final DatabaseHelper dbHelper;
+
+  SqliteChallengeRepository(this.dbHelper);
+
+  @override
+  Future<List<ChallengeModel>> getChallenges({
+    int page = 1,
+    int pageSize = 20,
+    int actionType = 1,
+  }) async {
+    final db = await dbHelper.database;
+    final maps = await db.query(DatabaseHelper.chllangetTable);
+    return maps.map(_mapDatabaseToModel).toList();
+  }
+
+  @override
+  Future<ChallengeModel> addChallenge(ChallengeModel challenge) async {
+    final db = await dbHelper.database;
+    await db.insert(
+      DatabaseHelper.chllangetTable,
+      _mapModelToDatabase(challenge),
+      conflictAlgorithm: ConflictAlgorithm.replace,
+    );
+    return challenge;
+  }
+
+  // 其他方法实现类似...
+
+  // Helper: 数据库行转 Model
+  ChallengeModel _mapDatabaseToModel(Map<String, dynamic> map) {
+    return ChallengeModel(
+      id: map[DatabaseHelper.columnId],
+      title: map[DatabaseHelper.columnTitle],
+      description: map['description'],
+      actionType: map['action_type'],
+      cover: map['cover'],
+      sort: map['sort'],
+      status: map['status'],
+      remark: map['remark'],
+      parentId: map['parent_id'],
+      difficulty: map['difficulty'],
+      finishDate: map['finish_date']
+          ? DateTime.fromMillisecondsSinceEpoch(map['finish_date'])
+          : null,
+      planFinishDate: map['plan_finish_date']
+          ? DateTime.fromMillisecondsSinceEpoch(map['plan_finish_date'])
+          : null,
+      startDate: map['start_date']
+          ? DateTime.fromMillisecondsSinceEpoch(map['start_date'])
+          : null,
+      endDate: map['end_date']
+          ? DateTime.fromMillisecondsSinceEpoch(map['end_date'])
+          : null,
+    );
+  }
+
+  // Helper: Model 转数据库行
+  Map<String, dynamic> _mapModelToDatabase(ChallengeModel model) {
+    return {
+      DatabaseHelper.columnId: model.id,
+      DatabaseHelper.columnTitle: model.title,
+      'action_type':model.actionType,
+      'cover':model.cover,
+      'sort':model.sort,
+      'status':model.status,
+      'remark':model.remark,
+      'parent_id':model.parentId,
+      'description': model.description,
+      'difficulty': model.difficulty,
+      'start_date': model.startDate?.millisecondsSinceEpoch,
+      'end_date': model.endDate?.millisecondsSinceEpoch,
+      'finish_date':model.finishDate,
+      'plan_finish_date':model.planFinishDate,
+    };
+  }
+
+  @override
+  Future<void> deleteChallenge(int id) async {
+    final db = await dbHelper.database;
+    await db.delete(
+      DatabaseHelper.chllangetTable,
+      where: '${DatabaseHelper.columnId} = ?',
+      whereArgs: [id],
+    );
+  }
+
+  @override
+  Future<ChallengeModel?> getChallengeById(int id) async {
+    final db = await dbHelper.database;
+    final maps = await db.query(
+      DatabaseHelper.chllangetTable,
+      where: '${DatabaseHelper.columnId} = ?',
+      whereArgs: [id],
+      limit: 1,
+    );
+
+    if (maps.isNotEmpty) {
+      return _mapDatabaseToModel(maps.first);
+    }
+    return null;
+  }
+
+  @override
+  Future<ChallengeModel> updateChallenge(ChallengeModel challenge) async {
+    final db = await dbHelper.database;
+    await db.update(
+      DatabaseHelper.chllangetTable,
+      _mapModelToDatabase(challenge),
+      where: '${DatabaseHelper.columnId} = ?',
+      whereArgs: [challenge.id],
+      conflictAlgorithm: ConflictAlgorithm.replace,
+    );
+    return challenge;
+  }
+}

+ 47 - 0
lib/core/routes/app_router.dart

@@ -0,0 +1,47 @@
+import 'package:flutter/material.dart';
+import 'package:japp_flutter/core/models/challenge_model.dart';
+import 'package:japp_flutter/features/challenge/views/add_challenge_screen.dart';
+import 'package:japp_flutter/features/challenge/views/challenge_detail_screen.dart';
+import 'package:japp_flutter/features/challenge/views/challenge_list_screen.dart';
+import 'package:japp_flutter/features/challenge/views/edit_challenge_screen.dart';
+import 'package:japp_flutter/features/challenge/views/mock_page.dart';
+
+class AppRouter {
+  static const String challengeList = '/';
+  static const String challengeDetail = '/challenge/detail';
+  static const String addChallenge = '/challenge/add';
+  static const String editChallenge = '/challenge/edit';
+  static const String mockPage = '/mock/page';
+
+  static Route<dynamic> generateRoute(RouteSettings settings) {
+    switch (settings.name) {
+      case challengeList:
+        return MaterialPageRoute(builder: (_) => const ChallengeListScreen());
+      case challengeDetail:
+        final challengeId = settings.arguments as int;
+        return MaterialPageRoute(
+          builder: (_) => ChallengeDetailScreen(challengeId: challengeId),
+        );
+      case addChallenge:
+        return MaterialPageRoute(builder: (_) => const AddChallengeScreen());
+      case editChallenge:
+        final challenge = settings.arguments as ChallengeModel;
+        return MaterialPageRoute(
+          builder: (_) => ChallengeEditScreen(initialChallenge: challenge),
+        );
+      case mockPage:
+        return MaterialPageRoute(builder: (_)=> const MockPage());
+      default:
+        return MaterialPageRoute(
+          builder: (_) => Scaffold(
+            body: Center(child: Text('No route defined for ${settings.name}')),
+          ),
+        );
+    }
+  }
+
+  // 静态跳转方法(可选)
+  static void navigateTo(BuildContext context, String routeName, {Object? args}) {
+    Navigator.pushNamed(context, routeName, arguments: args);
+  }
+}

+ 0 - 0
lib/core/services/challenge_service.dart


+ 50 - 0
lib/data/local/database_helper.dart

@@ -0,0 +1,50 @@
+import 'package:sqflite/sqflite.dart';
+import 'package:path/path.dart';
+
+class DatabaseHelper {
+  static const _databaseName = 'jay_world_app.db';
+  static const _databaseVersion = 1;
+
+  static const chllangetTable = 'challenges';
+  static const columnId = 'id';
+  static const columnTitle = 'title';
+  // 其他字段...
+
+  static Database? _database;
+
+  Future<Database> get database async {
+    if (_database != null) return _database!;
+    _database = await _initDatabase();
+    return _database!;
+  }
+
+  Future<Database> _initDatabase() async {
+    final path = join(await getDatabasesPath(), _databaseName);
+    return await openDatabase(
+      path,
+      version: _databaseVersion,
+      onCreate: _onCreate,
+    );
+  }
+
+  Future<void> _onCreate(Database db, int version) async {
+    await db.execute('''
+      CREATE TABLE $chllangetTable (
+        $columnId INTEGER PRIMARY KEY AUTOINCREMENT,
+        $columnTitle TEXT NOT NULL,
+        action_type INTEGER NOT NULL,
+        description TEXT NOT NULL,
+        cover TEXT NOT NULL,
+        sort INTEGER NOT NULL,
+        status INTEGER NOT NULL,
+        difficulty INTEGER NOT NULL,
+        remark TEXT NOT NULL,
+        start_date INTEGER,
+        end_date INTEGER,
+        finish_date INTEGER,
+        plan_finish_date INTEGER,
+        parent_id INTEGER NOT NULL
+      );
+    ''');
+  }
+}

+ 65 - 0
lib/features/challenge/view_models/challenge_add_vm.dart

@@ -0,0 +1,65 @@
+// features/challenge/add/add_challenge_viewmodel.dart
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:japp_flutter/core/models/challenge_model.dart';
+import 'package:japp_flutter/core/proviers/repository_providers.dart';
+import 'package:japp_flutter/core/repositories/challenge_repository.dart';
+
+final addChallengeProvider =
+    AsyncNotifierProvider<AddChallengeViewModel, ChallengeModel>(
+      AddChallengeViewModel.new,
+    );
+
+class AddChallengeViewModel extends AsyncNotifier<ChallengeModel> {
+  ChallengeRepository get _repository => ref.read(challengeRepositoryProvider);
+
+  @override
+  Future<ChallengeModel> build() async {
+    // 初始化为空挑战模板
+    return ChallengeModel(
+      id: null,
+      title: '',
+      description: '',
+      startDate: DateTime.now(),
+      endDate: DateTime.now().add(const Duration(days: 30)),
+      actionType: 1,
+      cover: '',
+      sort: 200,
+      status: 1,
+      difficulty: 1,
+      remark: '',
+      finishDate: null,
+      parentId: 1,
+      planFinishDate: null
+    );
+  }
+
+  /// 更新挑战标题
+  void updateTitle(String title) {
+    state = AsyncData(state.value!.copyWith(title: title));
+  }
+
+  /// 更新挑战描述
+  void updateDescription(String description) {
+    state = AsyncData(state.value!.copyWith(description: description));
+  }
+
+  /// 更新难度级别
+  void updateDifficulty(int difficulty) {
+    state = AsyncData(state.value!.copyWith(difficulty: difficulty));
+  }
+
+  /// 更新日期范围
+  void updateDateRange(DateTime start, DateTime end) {
+    state = AsyncData(state.value!.copyWith(startDate: start, endDate: end));
+  }
+
+  /// 提交新挑战
+  Future<void> submitChallenge() async {
+    if (state.value == null) return;
+
+    state = const AsyncLoading();
+    state = await AsyncValue.guard(
+      () => _repository.addChallenge(state.value!),
+    );
+  }
+}

+ 30 - 0
lib/features/challenge/view_models/challenge_detail_vm.dart

@@ -0,0 +1,30 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:japp_flutter/core/models/challenge_model.dart';
+import 'package:japp_flutter/core/proviers/repository_providers.dart';
+import 'package:japp_flutter/core/repositories/challenge_repository.dart';
+
+
+
+final challengeDetailProvider = AsyncNotifierProvider.autoDispose<ChallengeDetailViewModel, ChallengeModel?>(
+  ChallengeDetailViewModel.new,
+);
+
+
+class ChallengeDetailViewModel extends AutoDisposeAsyncNotifier<ChallengeModel?> {
+  ChallengeRepository get _repository => ref.read(challengeRepositoryProvider);
+
+  @override
+  Future<ChallengeModel?> build() async {
+    return null;
+  }
+
+  Future<void> loadChallenge(int challengeId) async {
+    state = const AsyncLoading();
+    state = await AsyncValue.guard(() => _repository.getChallengeById(challengeId));
+  }
+
+  // 手动刷新用
+  Future<void> refreshChallenge(int challengeId) async {
+    await loadChallenge(challengeId);
+  }
+}

+ 54 - 0
lib/features/challenge/view_models/challenge_edit_vm.dart

@@ -0,0 +1,54 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:japp_flutter/core/models/challenge_model.dart';
+import 'package:japp_flutter/core/proviers/repository_providers.dart';
+import 'package:japp_flutter/core/repositories/challenge_repository.dart';
+
+final challengeEditProvider = AsyncNotifierProvider.autoDispose<ChallengeEditViewModel, ChallengeModel?>(
+  ChallengeEditViewModel.new,
+);
+
+class ChallengeEditViewModel extends AutoDisposeAsyncNotifier<ChallengeModel?> {
+  ChallengeRepository get _repository => ref.read(challengeRepositoryProvider);
+
+  @override
+  Future<ChallengeModel?> build() async {
+    // 初始化为 null,由外部传入初始数据
+    return null;
+  }
+
+  /// 初始化编辑数据(从详情页传入)
+  void initialize(ChallengeModel challenge) {
+    state = AsyncData(challenge);
+  }
+
+  /// 更新挑战标题
+  void updateTitle(String title) {
+    state.whenData((challenge) {
+      state = AsyncData(challenge!.copyWith(title: title));
+    });
+  }
+
+  /// 更新挑战描述
+  void updateDescription(String description) {
+    state.whenData((challenge) {
+      state = AsyncData(challenge!.copyWith(description: description));
+    });
+  }
+
+  /// 更新挑战描述
+  void updateDifficulty(int difficulty) {
+    state.whenData((challenge) {
+      state = AsyncData(challenge!.copyWith(difficulty: difficulty));
+    });
+  }
+
+
+  /// 保存挑战
+  Future<void> saveChallenge() async {
+    final challenge = state.value;
+    if (challenge == null) return;
+
+    state = const AsyncLoading();
+    state = await AsyncValue.guard(() => _repository.updateChallenge(challenge));
+  }
+}

+ 46 - 0
lib/features/challenge/view_models/challenge_list_vm.dart

@@ -0,0 +1,46 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:japp_flutter/core/models/challenge_model.dart';
+import 'package:japp_flutter/core/proviers/repository_providers.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:japp_flutter/core/repositories/challenge_repository.dart';
+
+
+final challengeListProvider = AsyncNotifierProvider<ChallengeListViewModel, List<ChallengeModel>>(
+  ChallengeListViewModel.new,
+);
+
+class ChallengeListViewModel extends AsyncNotifier<List<ChallengeModel>> {
+  ChallengeRepository get _repository => ref.read(challengeRepositoryProvider);
+
+  @override
+  Future<List<ChallengeModel>> build() async {
+    // 初始加载数据(可选)
+    return loadChallenges(); // 初始化为空列表,或直接调用 loadChallenges()
+  }
+
+
+  Future<List<ChallengeModel>> loadChallenges() async {
+    state = const AsyncValue.loading();
+    try {
+      final challenges = await _repository.getChallenges();
+      state = AsyncValue.data(challenges);
+      return challenges;
+    } catch (e, stack) {
+      state = AsyncValue.error(e, stack);
+      
+      rethrow;
+    }
+  }
+
+  // /// 加载挑战列表(自动处理加载/错误状态)
+  // Future<void> loadChallenges() async {
+  //   state = const AsyncLoading();
+  //   state = await AsyncValue.guard(() => _repository.getAllChallenges());
+  // }
+
+  /// 刷新数据(别名方法)
+  Future<void> refresh() async {
+      state = const AsyncValue.loading();
+      await loadChallenges(); // 复用加载逻辑
+  }
+}

+ 198 - 0
lib/features/challenge/views/add_challenge_screen.dart

@@ -0,0 +1,198 @@
+// features/challenge/add/add_challenge_screen.dart
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:intl/intl.dart';
+import 'package:japp_flutter/core/constants/options.dart';
+import 'package:japp_flutter/core/models/challenge_model.dart';
+import 'package:japp_flutter/features/challenge/view_models/challenge_add_vm.dart';
+
+class AddChallengeScreen extends ConsumerWidget {
+  const AddChallengeScreen({super.key});
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final challengeState = ref.watch(addChallengeProvider);
+    final theme = Theme.of(context);
+
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('创建新挑战'),
+        actions: [
+          IconButton(
+            icon: const Icon(Icons.check),
+            onPressed: () async {
+              await ref.read(addChallengeProvider.notifier).submitChallenge();
+              if (context.mounted) Navigator.pop(context);
+            },
+            tooltip: '提交',
+          ),
+        ],
+      ),
+      body: challengeState.when(
+        loading: () => const Center(child: CircularProgressIndicator()),
+        error: (error, _) => Center(child: Text('错误: $error')),
+        data: (challenge) => _BuildForm(challenge: challenge),
+      ),
+    );
+  }
+}
+
+class _BuildForm extends ConsumerWidget {
+  final ChallengeModel challenge;
+
+  const _BuildForm({required this.challenge});
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    return SingleChildScrollView(
+      padding: const EdgeInsets.all(16),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          _TitleField(initialValue: challenge.title),
+          const SizedBox(height: 24),
+          _DateRangeField(
+            startDate: challenge.startDate!,
+            endDate: challenge.endDate!,
+          ),
+          const SizedBox(height: 24),
+          _DifficultySelector(currentDifficulty: challenge.difficulty),
+          const SizedBox(height: 24),
+          _DescriptionField(initialValue: challenge.description),
+        ],
+      ),
+    );
+  }
+}
+
+class _TitleField extends ConsumerWidget {
+  final String initialValue;
+
+  const _TitleField({required this.initialValue});
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    return TextFormField(
+      initialValue: initialValue,
+      decoration: const InputDecoration(
+        labelText: '挑战标题*',
+        border: OutlineInputBorder(),
+      ),
+      onChanged: (value) => ref.read(addChallengeProvider.notifier).updateTitle(value),
+    );
+  }
+}
+
+class _DescriptionField extends ConsumerWidget {
+  final String initialValue;
+
+  const _DescriptionField({required this.initialValue});
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    return TextFormField(
+      initialValue: initialValue,
+      decoration: const InputDecoration(
+        labelText: '挑战描述',
+        border: OutlineInputBorder(),
+        alignLabelWithHint: true,
+      ),
+      maxLines: 5,
+      onChanged: (value) => ref.read(addChallengeProvider.notifier).updateDescription(value),
+    );
+  }
+}
+
+class _DifficultySelector extends ConsumerWidget {
+  final int currentDifficulty;
+  // static const difficulties = ['简单', '中等', '困难', '地狱'];
+
+  const _DifficultySelector({required this.currentDifficulty});
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        const Text('难度级别*', style: TextStyle(fontSize: 16)),
+        const SizedBox(height: 8),
+        Wrap(
+          spacing: 8,
+          children: difficultOptions.map((option) {
+            return ChoiceChip(
+              label: Text(option.label),
+              selected: currentDifficulty == option.value,
+              onSelected: (selected) {
+                if (selected) {
+                  ref.read(addChallengeProvider.notifier).updateDifficulty(option.value);
+                }
+              },
+            );
+          }).toList(),
+        ),
+      ],
+    );
+  }
+}
+
+class _DateRangeField extends ConsumerWidget {
+  final DateTime startDate;
+  final DateTime endDate;
+
+  const _DateRangeField({
+    required this.startDate,
+    required this.endDate,
+  });
+
+  Future<void> _selectDate(BuildContext context, bool isStartDate, WidgetRef ref) async {
+    final initialDate = isStartDate ? startDate : endDate;
+    final picked = await showDatePicker(
+      context: context,
+      initialDate: initialDate,
+      firstDate: DateTime.now(),
+      lastDate: DateTime(2100),
+    );
+
+    if (picked != null) {
+      final newStart = isStartDate ? picked : startDate;
+      final newEnd = isStartDate ? endDate : picked;
+      ref.read(addChallengeProvider.notifier).updateDateRange(newStart, newEnd);
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        const Text('挑战周期*', style: TextStyle(fontSize: 16)),
+        const SizedBox(height: 8),
+        Row(
+          children: [
+            Expanded(
+              child: OutlinedButton(
+                onPressed: () => _selectDate(context, true, ref),
+                child: Text(DateFormat('yyyy/MM/dd').format(startDate)),
+              ),
+            ),
+            const Padding(
+              padding: EdgeInsets.symmetric(horizontal: 8),
+              child: Text('至'),
+            ),
+            Expanded(
+              child: OutlinedButton(
+                onPressed: () => _selectDate(context, false, ref),
+                child: Text(DateFormat('yyyy/MM/dd').format(endDate)),
+              ),
+            ),
+          ],
+        ),
+        const SizedBox(height: 4),
+        Text(
+          '总天数: ${endDate.difference(startDate).inDays}天',
+          style: const TextStyle(color: Colors.grey),
+        ),
+      ],
+    );
+  }
+}

+ 82 - 0
lib/features/challenge/views/challenge_detail_screen.dart

@@ -0,0 +1,82 @@
+import 'package:flutter/material.dart';
+import 'package:intl/intl.dart';
+import 'package:japp_flutter/core/models/challenge_model.dart';
+import 'package:japp_flutter/features/challenge/view_models/challenge_detail_vm.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+class ChallengeDetailScreen extends ConsumerWidget {
+  final int challengeId;
+
+  const ChallengeDetailScreen({super.key, required this.challengeId});
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final challengeAsync = ref.watch(challengeDetailProvider);
+
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text("挑战详情"),
+        actions: [
+          IconButton(
+            icon: const Icon(Icons.edit),
+            onPressed: () {
+              final challenge = challengeAsync.value;
+              if (challenge != null) {
+                Navigator.pushNamed(context, '/edit/${challenge.id}');
+              }
+            },
+          ),
+        ],
+      ),
+      body: switch (challengeAsync) {
+        AsyncError(:final error) => Center(child: Text('加载失败: $error')),
+        AsyncData(value: final value) => _buildChallengeContent(value!),
+        _ => const Center(child: CircularProgressIndicator()),
+
+      },
+    );
+  }
+
+  Widget _buildChallengeContent(ChallengeModel challenge) {
+    return SingleChildScrollView(
+      padding: const EdgeInsets.all(16),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Text(
+            challenge.title,
+            style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
+          ),
+          const SizedBox(height: 16),
+          Text(
+            challenge.description,
+            style: const TextStyle(fontSize: 16, height: 1.5),
+          ),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildDateInfo(String label, DateTime date) {
+    return Padding(
+      padding: const EdgeInsets.only(bottom: 8),
+      child: Row(
+        children: [
+          Text('$label: ', style: const TextStyle(fontWeight: FontWeight.bold)),
+          Text(DateFormat('yyyy年MM月dd日').format(date)),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildParticipantInfo(int count) {
+    return Row(
+      children: [
+        const Icon(Icons.people, size: 20),
+        const SizedBox(width: 8),
+        Text('$count 人参与'),
+      ],
+    );
+  }
+
+}

+ 72 - 0
lib/features/challenge/views/challenge_list_screen.dart

@@ -0,0 +1,72 @@
+// challenge_list_screen.dart
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:japp_flutter/core/models/challenge_model.dart';
+import 'package:japp_flutter/features/challenge/view_models/challenge_list_vm.dart';
+import 'package:japp_flutter/features/challenge/views/widgets/challenge_list_card_widget.dart';
+
+class ChallengeListScreen extends ConsumerWidget {
+  const ChallengeListScreen({super.key});
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final challengesState = ref.watch(challengeListProvider);
+
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('挑战列表'),
+        actions: [
+          IconButton(
+            icon: const Icon(Icons.refresh),
+            onPressed: () => ref.read(challengeListProvider.notifier).refresh(),
+          ),
+        ],
+      ),
+      body: _buildBody(challengesState, ref),
+      floatingActionButton: FloatingActionButton(
+        onPressed: () => Navigator.pushNamed(context, '/challenge/add'),
+        child: const Icon(Icons.add),
+      ),
+    );
+  }
+
+  Widget _buildBody(AsyncValue<List<ChallengeModel>> state, WidgetRef ref) {
+    return state.when(
+      loading: () => const Center(child: CircularProgressIndicator()),
+      error: (error, stack) => Center(
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: [
+            const Icon(Icons.error, color: Colors.red, size: 48),
+            const SizedBox(height: 16),
+            Text('加载失败: $error', style: const TextStyle(color: Colors.red)),
+            const SizedBox(height: 16),
+            ElevatedButton(
+              onPressed: () =>
+                  ref.read(challengeListProvider.notifier).refresh(),
+              child: const Text('重试'),
+            ),
+          ],
+        ),
+      ),
+      data: (challenges) => challenges.isEmpty
+          ? const Center(child: Text('暂无挑战', style: TextStyle(fontSize: 18)))
+          : RefreshIndicator(
+              onRefresh: () =>
+                  ref.read(challengeListProvider.notifier).refresh(),
+              child: ListView.builder(
+                padding: const EdgeInsets.all(16),
+                itemCount: challenges.length,
+                itemBuilder: (_, index) => ChallengeListCard(challenge: challenges[index]),
+                
+                // itemBuilder: (context, index) => ListTile(
+                //   title: Text(challenges[index].title),
+                //   // 其他列表项内容
+                // ),
+
+
+              ),
+            ),
+    );
+  }
+}

+ 109 - 0
lib/features/challenge/views/edit_challenge_screen.dart

@@ -0,0 +1,109 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:japp_flutter/core/models/challenge_model.dart';
+import 'package:japp_flutter/features/challenge/view_models/challenge_edit_vm.dart';
+
+class ChallengeEditScreen extends ConsumerWidget {
+  final ChallengeModel? initialChallenge;
+
+  const ChallengeEditScreen({
+    super.key,
+    this.initialChallenge,
+  });
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final challengeState = ref.watch(challengeEditProvider);
+    final theme = Theme.of(context);
+
+    return Scaffold(
+      appBar: AppBar(
+        title: Text(initialChallenge == null ? '创建挑战' : '编辑挑战'),
+        actions: [
+          IconButton(
+            icon: const Icon(Icons.save),
+            onPressed: () async {
+              await ref.read(challengeEditProvider.notifier).saveChallenge();
+              if (context.mounted) Navigator.pop(context);
+            },
+          ),
+        ],
+      ),
+      body: challengeState.when(
+        loading: () => const Center(child: CircularProgressIndicator()),
+        error: (error, _) => Center(child: Text('错误: $error')),
+        data: (challenge) {
+          if (challenge == null) {
+            return const Center(child: Text('未初始化数据'));
+          }
+
+          return SingleChildScrollView(
+            padding: const EdgeInsets.all(16),
+            child: Column(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: [
+                _buildTitleField(challenge, ref),
+                const SizedBox(height: 24),
+                _buildDifficultySelector(challenge, ref),
+                const SizedBox(height: 24),
+                _buildDescriptionField(challenge, ref),
+              ],
+            ),
+          );
+        },
+      ),
+    );
+  }
+
+  Widget _buildTitleField(ChallengeModel challenge, WidgetRef ref) {
+    return TextFormField(
+      initialValue: challenge.title,
+      decoration: const InputDecoration(
+        labelText: '挑战标题',
+        border: OutlineInputBorder(),
+      ),
+      onChanged: (value) => ref.read(challengeEditProvider.notifier).updateTitle(value),
+    );
+  }
+
+  Widget _buildDescriptionField(ChallengeModel challenge, WidgetRef ref) {
+    return TextFormField(
+      initialValue: challenge.description,
+      decoration: const InputDecoration(
+        labelText: '挑战描述',
+        border: OutlineInputBorder(),
+        alignLabelWithHint: true,
+      ),
+      maxLines: 5,
+      onChanged: (value) => ref.read(challengeEditProvider.notifier).updateDescription(value),
+    );
+  }
+
+  Widget _buildDifficultySelector(ChallengeModel challenge, WidgetRef ref) {
+    // const difficulties = ['简单', '中等', '困难'];
+
+    const difficulties = [1,2,3,4];
+    
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        const Text('难度级别', style: TextStyle(fontSize: 16)),
+        const SizedBox(height: 8),
+        Wrap(
+          spacing: 8,
+          children: difficulties.map((level) {
+            return ChoiceChip(
+              label: Text(level.toString()),
+              selected: challenge.difficulty == level,
+              onSelected: (selected) {
+                if (selected) {
+                  ref.read(challengeEditProvider.notifier).updateDifficulty(level);
+                }
+              },
+            );
+          }).toList(),
+        ),
+      ],
+    );
+  }
+}

+ 359 - 0
lib/features/challenge/views/mock_page.dart

@@ -0,0 +1,359 @@
+import 'package:flutter/material.dart';
+import 'package:intl/intl.dart';
+import 'package:japp_flutter/core/constants/options.dart';
+import 'package:japp_flutter/features/widgets/app_dropdown.dart';
+
+
+class MockPage extends StatelessWidget {
+  const MockPage({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return MaterialApp(
+      title: 'Flutter 表单示例',
+      theme: ThemeData(
+        primarySwatch: Colors.blue,
+      ),
+      home: const MyFormPage(),
+    );
+  }
+}
+
+// 表单数据模型
+class FormData {
+  String name;
+  String email;
+  String phone;
+  String password;
+  bool agreeTerms;
+  DateTime? startDate;
+  DateTime? endDate;
+  int? difficult;
+  FormData({
+    this.name = '',
+    this.email = 'fin@qq.com',
+    this.phone = '13770656091',
+    this.password = '',
+    this.agreeTerms = false,
+    this.startDate,
+    this.endDate,
+    this.difficult = 2,
+  });
+
+  // 转换为Map方便打印
+  Map<String, dynamic> toMap() {
+    return {
+      'name': name,
+      'email': email,
+      'phone': phone,
+      'password': password.replaceAll(RegExp(r'.'), '*'), // 密码用*号代替
+      'agreeTerms': agreeTerms,
+      'startDate': startDate != null ? DateFormat('yyyy-MM-dd').format(startDate!) : null,
+      'endDate': endDate != null ? DateFormat('yyyy-MM-dd').format(endDate!) : null,
+      'difficult':difficult
+    };
+  }
+}
+
+
+
+// 难度等级模型
+class DifficultyLevel {
+  final int value;
+  final String label;
+  final IconData? icon;
+
+  const DifficultyLevel({
+    required this.value,
+    required this.label,
+    this.icon,
+  });
+}
+
+// 预定义的难度等级
+const List<DifficultyLevel> difficultyLevels = [
+  DifficultyLevel(value: 1, label: '简单', icon: Icons.sentiment_very_satisfied),
+  DifficultyLevel(value: 2, label: '中等', icon: Icons.sentiment_satisfied),
+  DifficultyLevel(value: 3, label: '困难', icon: Icons.sentiment_dissatisfied),
+  DifficultyLevel(value: 4, label: '专家', icon: Icons.sentiment_very_dissatisfied),
+];
+
+
+class MyFormPage extends StatefulWidget {
+  const MyFormPage({super.key});
+
+  @override
+  State<MyFormPage> createState() => _MyFormPageState();
+}
+
+class _MyFormPageState extends State<MyFormPage> {
+  final _formKey = GlobalKey<FormState>();
+  final FormData _formData = FormData(
+      startDate: DateTime.now(), // 默认开始日期为今天
+      endDate: DateTime.now().add(const Duration(days: 7)), // 默认结束日期为7天后
+  );
+
+  // 选择开始日期
+  Future<void> _selectStartDate() async {
+    final DateTime? picked = await showDatePicker(
+      context: context,
+      initialDate: _formData.startDate ?? DateTime.now(),
+      firstDate: DateTime(2000),
+      lastDate: DateTime(2100),
+    );
+    if (picked != null && picked != _formData.startDate) {
+      setState(() {
+        _formData.startDate = picked;
+        // 如果结束日期早于开始日期,重置结束日期
+        if (_formData.endDate != null && _formData.endDate!.isBefore(picked)) {
+          _formData.endDate = null;
+        }
+      });
+    }
+  }
+
+  // 选择结束日期
+  Future<void> _selectEndDate() async {
+    final DateTime? picked = await showDatePicker(
+      context: context,
+      initialDate: _formData.endDate ?? (_formData.startDate ?? DateTime.now()),
+      firstDate: _formData.startDate ?? DateTime.now(),
+      lastDate: DateTime(2100),
+    );
+    if (picked != null && picked != _formData.endDate) {
+      setState(() {
+        _formData.endDate = picked;
+      });
+    }
+  }
+
+
+  // 提交表单
+  void _submitForm() {
+    if (_formKey.currentState!.validate()) {
+      _formKey.currentState!.save();
+      
+      // 获取完整的难度信息(不仅仅是value)
+      final selectedDifficulty = difficultyLevels.firstWhere(
+        (level) => level.value == _formData.difficult,
+        orElse: () => const DifficultyLevel(value: 0, label: '未知'),
+      );
+
+      print('表单提交数据: ${_formData.toMap()}');
+      
+      // 显示提交成功的提示
+      ScaffoldMessenger.of(context).showSnackBar(
+        const SnackBar(content: Text('表单提交成功! 数据已打印到控制台')),
+      );
+    }
+  }
+
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('Flutter 表单示例'),
+      ),
+      body: Padding(
+        padding: const EdgeInsets.all(16.0),
+        child: Form(
+          key: _formKey,
+          child: ListView(
+            children: [
+              TextFormField(
+                decoration: const InputDecoration(
+                  labelText: '姓名',
+                  hintText: '请输入您的姓名',
+                  border: OutlineInputBorder(),
+                ),
+                validator: (value) {
+                  if (value == null || value.isEmpty) {
+                    return '请输入姓名';
+                  }
+                  return null;
+                },
+                onSaved: (value) => _formData.name = value!,
+              ),
+              const SizedBox(height: 16),
+              TextFormField(
+                initialValue: _formData.email,
+                decoration: const InputDecoration(
+                  labelText: '电子邮件',
+                  hintText: '请输入您的电子邮件',
+                  border: OutlineInputBorder(),
+                ),
+                keyboardType: TextInputType.emailAddress,
+                validator: (value) {
+                  if (value == null || value.isEmpty) {
+                    return '请输入电子邮件';
+                  }
+                  if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
+                    return '请输入有效的电子邮件地址';
+                  }
+                  return null;
+                },
+                onSaved: (value) => _formData.email = value!,
+              ),
+              const SizedBox(height: 16),
+              TextFormField(
+                initialValue: _formData.phone,
+                decoration: const InputDecoration(
+                  labelText: '手机号码',
+                  hintText: '请输入您的手机号码',
+                  border: OutlineInputBorder(),
+                ),
+                keyboardType: TextInputType.phone,
+                validator: (value) {
+                  if (value == null || value.isEmpty) {
+                    return '请输入手机号码';
+                  }
+                  if (!RegExp(r'^[0-9]{11}$').hasMatch(value)) {
+                    return '请输入有效的11位手机号码';
+                  }
+                  return null;
+                },
+                onSaved: (value) => _formData.phone = value!,
+              ),
+              const SizedBox(height: 16),
+              TextFormField(
+                decoration: const InputDecoration(
+                  labelText: '密码',
+                  hintText: '请输入您的密码',
+                  border: OutlineInputBorder(),
+                ),
+                obscureText: true,
+                validator: (value) {
+                  if (value == null || value.isEmpty) {
+                    return '请输入密码';
+                  }
+                  if (value.length < 6) {
+                    return '密码长度不能少于6位';
+                  }
+                  return null;
+                },
+                onSaved: (value) => _formData.password = value!,
+              ),
+              const SizedBox(height: 16),
+              // 开始日期选择
+              InkWell(
+                onTap: _selectStartDate,
+                child: InputDecorator(
+                  decoration: const InputDecoration(
+                    labelText: '开始日期',
+                    border: OutlineInputBorder(),
+                  ),
+                  child: Row(
+                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                    children: [
+                      Text(
+                        _formData.startDate != null
+                            ? DateFormat('yyyy-MM-dd').format(_formData.startDate!)
+                            : '请选择开始日期',
+                      ),
+                      const Icon(Icons.calendar_today, size: 20),
+                    ],
+                  ),
+                ),
+              ),
+              const SizedBox(height: 16),
+              // 结束日期选择
+              InkWell(
+                onTap: _formData.startDate == null
+                    ? null
+                    : _selectEndDate,
+                child: InputDecorator(
+                  decoration: const InputDecoration(
+                    labelText: '结束日期',
+                    border: OutlineInputBorder(),
+                  ),
+                  child: Row(
+                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                    children: [
+                      Text(
+                        _formData.endDate != null
+                            ? DateFormat('yyyy-MM-dd').format(_formData.endDate!)
+                            : '请选择结束日期',
+                      ),
+                      const Icon(Icons.calendar_today, size: 20),
+                    ],
+                  ),
+                ),
+              ),
+              const SizedBox(height: 16),
+              // 难度选择下拉框
+              // DropdownButtonFormField<int>(
+              //   decoration: const InputDecoration(
+              //     labelText: '挑战难度',
+              //     border: OutlineInputBorder(),
+              //   ),
+              //   value: _formData.difficult,
+              //   items: difficultyLevels.map((level) {
+              //     return DropdownMenuItem<int>(
+              //       value: level.value,
+              //       child: Row(
+              //         children: [
+              //           if (level.icon != null) 
+              //             Icon(level.icon, size: 20),
+              //           const SizedBox(width: 8),
+              //           Text(level.label),
+              //         ],
+              //       ),
+              //     );
+              //   }).toList(),
+              //   onChanged: (value) {
+              //     setState(() {
+              //       _formData.difficult = value;
+              //     });
+              //   },
+              //   validator: (value) {
+              //     if (value == null) {
+              //       return '请选择难度等级';
+              //     }
+              //     return null;
+              //   },
+              //   onSaved: (value) => _formData.difficult = value,
+              // ),
+
+              AppDropdown(
+                label: '挑战难度', 
+                value: _formData.difficult, 
+                items: challengeDifficultyOpts, 
+                onChanged: (value) {
+                  setState(() {
+                    _formData.difficult = value;
+                  });
+                },
+              ),
+
+
+              const SizedBox(height: 16),
+
+              Row(
+                children: [
+                  Checkbox(
+                    value: _formData.agreeTerms,
+                    onChanged: (value) {
+                      setState(() {
+                        _formData.agreeTerms = value!;
+                      });
+                    },
+                  ),
+                  const Text('我同意用户协议'),
+                ],
+              ),
+              const SizedBox(height: 24),
+              ElevatedButton(
+                onPressed: _submitForm,
+                style: ElevatedButton.styleFrom(
+                  padding: const EdgeInsets.symmetric(vertical: 16),
+                ),
+                child: const Text('提交表单'),
+              ),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+}

+ 37 - 0
lib/features/challenge/views/widgets/challenge_list_card_widget.dart

@@ -0,0 +1,37 @@
+import 'package:flutter/material.dart';
+import 'package:japp_flutter/core/models/challenge_model.dart';
+
+class ChallengeListCard extends StatelessWidget {
+  final ChallengeModel challenge;
+
+  const ChallengeListCard({super.key, required this.challenge});
+  @override
+  Widget build(BuildContext context) {
+    final theme = Theme.of(context);
+
+    return Card(
+      elevation: 2,
+      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
+      margin: const EdgeInsets.only(bottom: 16),
+      child: Padding(
+        padding: const EdgeInsets.all(16),
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            Text(
+              challenge.title,
+              style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
+            ),
+            const SizedBox(height: 16),
+
+            Text(
+              challenge.description,
+              style: TextStyle(color: Colors.grey[700], fontSize: 14),
+            ),
+            const SizedBox(height: 16),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 69 - 0
lib/features/widgets/app_dropdown.dart

@@ -0,0 +1,69 @@
+import 'package:flutter/material.dart';
+import 'package:japp_flutter/core/models/dropdown_item.dart';
+
+class AppDropdown<T> extends StatelessWidget {
+  final String label;
+  final T? value;
+  final List<DropdownItem<T>> items; // 改为接收通用列表
+  final ValueChanged<T?> onChanged;
+  final FormFieldValidator<T>? validator;
+  final String? hintText;
+  final bool isExpanded;
+  final Widget? prefixIcon;
+  final InputBorder? border;
+  final TextStyle? textStyle;
+  final double? iconSize;
+  final Color? iconColor;
+
+  const AppDropdown({
+    super.key,
+    required this.label,
+    required this.value,
+    required this.items,
+    required this.onChanged,
+    this.validator,
+    this.hintText,
+    this.isExpanded = true,
+    this.prefixIcon,
+    this.border,
+    this.textStyle,
+    this.iconSize = 20.0,
+    this.iconColor,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return DropdownButtonFormField<T>(
+      decoration: InputDecoration(
+        labelText: label,
+        hintText: hintText,
+        border: border ?? const OutlineInputBorder(),
+        prefixIcon: prefixIcon,
+      ),
+      value: value,
+      items: items.map((item) {
+        return DropdownMenuItem<T>(
+          value: item.value,
+          child: Row(
+            children: [
+              if (item.icon != null)
+                Icon(
+                  item.icon,
+                  size: iconSize,
+                  color: item.iconColor ?? iconColor,
+                ),
+              if (item.icon != null) const SizedBox(width: 8),
+              Text(
+                item.label,
+                style: textStyle ?? Theme.of(context).textTheme.bodyMedium,
+              ),
+            ],
+          ),
+        );
+      }).toList(),
+      onChanged: onChanged,
+      validator: validator,
+      isExpanded: isExpanded,
+    );
+  }
+}

+ 11 - 110
lib/main.dart

@@ -1,122 +1,23 @@
 import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:japp_flutter/core/routes/app_router.dart';
 
-void main() {
-  runApp(const MyApp());
-}
+void main() => runApp(ProviderScope(child: const ChallengeApp()));
 
-class MyApp extends StatelessWidget {
-  const MyApp({super.key});
+class ChallengeApp extends StatelessWidget {
+  const ChallengeApp({super.key});
 
-  // This widget is the root of your application.
   @override
   Widget build(BuildContext context) {
     return MaterialApp(
-      title: 'Flutter Demo',
+      title: '挑战列表',
+      debugShowCheckedModeBanner: false,
       theme: ThemeData(
-        // This is the theme of your application.
-        //
-        // TRY THIS: Try running your application with "flutter run". You'll see
-        // the application has a purple toolbar. Then, without quitting the app,
-        // try changing the seedColor in the colorScheme below to Colors.green
-        // and then invoke "hot reload" (save your changes or press the "hot
-        // reload" button in a Flutter-supported IDE, or press "r" if you used
-        // the command line to start the app).
-        //
-        // Notice that the counter didn't reset back to zero; the application
-        // state is not lost during the reload. To reset the state, use hot
-        // restart instead.
-        //
-        // This works for code too, not just values: Most code changes can be
-        // tested with just a hot reload.
-        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
-      ),
-      home: const MyHomePage(title: 'Flutter Demo Home Page'),
-    );
-  }
-}
-
-class MyHomePage extends StatefulWidget {
-  const MyHomePage({super.key, required this.title});
-
-  // This widget is the home page of your application. It is stateful, meaning
-  // that it has a State object (defined below) that contains fields that affect
-  // how it looks.
-
-  // This class is the configuration for the state. It holds the values (in this
-  // case the title) provided by the parent (in this case the App widget) and
-  // used by the build method of the State. Fields in a Widget subclass are
-  // always marked "final".
-
-  final String title;
-
-  @override
-  State<MyHomePage> createState() => _MyHomePageState();
-}
-
-class _MyHomePageState extends State<MyHomePage> {
-  int _counter = 0;
-
-  void _incrementCounter() {
-    setState(() {
-      // This call to setState tells the Flutter framework that something has
-      // changed in this State, which causes it to rerun the build method below
-      // so that the display can reflect the updated values. If we changed
-      // _counter without calling setState(), then the build method would not be
-      // called again, and so nothing would appear to happen.
-      _counter++;
-    });
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    // This method is rerun every time setState is called, for instance as done
-    // by the _incrementCounter method above.
-    //
-    // The Flutter framework has been optimized to make rerunning build methods
-    // fast, so that you can just rebuild anything that needs updating rather
-    // than having to individually change instances of widgets.
-    return Scaffold(
-      appBar: AppBar(
-        // TRY THIS: Try changing the color here to a specific color (to
-        // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
-        // change color while the other colors stay the same.
-        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
-        // Here we take the value from the MyHomePage object that was created by
-        // the App.build method, and use it to set our appbar title.
-        title: Text(widget.title),
-      ),
-      body: Center(
-        // Center is a layout widget. It takes a single child and positions it
-        // in the middle of the parent.
-        child: Column(
-          // Column is also a layout widget. It takes a list of children and
-          // arranges them vertically. By default, it sizes itself to fit its
-          // children horizontally, and tries to be as tall as its parent.
-          //
-          // Column has various properties to control how it sizes itself and
-          // how it positions its children. Here we use mainAxisAlignment to
-          // center the children vertically; the main axis here is the vertical
-          // axis because Columns are vertical (the cross axis would be
-          // horizontal).
-          //
-          // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
-          // action in the IDE, or press "p" in the console), to see the
-          // wireframe for each widget.
-          mainAxisAlignment: MainAxisAlignment.center,
-          children: <Widget>[
-            const Text('You have pushed the button this many times:'),
-            Text(
-              '$_counter',
-              style: Theme.of(context).textTheme.headlineMedium,
-            ),
-          ],
-        ),
+        primarySwatch: Colors.blue,
+        appBarTheme: const AppBarTheme(elevation: 1),
       ),
-      floatingActionButton: FloatingActionButton(
-        onPressed: _incrementCounter,
-        tooltip: 'Increment',
-        child: const Icon(Icons.add),
-      ), // This trailing comma makes auto-formatting nicer for build methods.
+      initialRoute: AppRouter.mockPage,
+      onGenerateRoute: AppRouter.generateRoute,
     );
   }
 }

+ 2 - 0
macos/Flutter/GeneratedPluginRegistrant.swift

@@ -5,6 +5,8 @@
 import FlutterMacOS
 import Foundation
 
+import sqflite_darwin
 
 func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
+  SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
 }

+ 169 - 1
pubspec.lock

@@ -49,6 +49,22 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.8"
+  dio:
+    dependency: "direct main"
+    description:
+      name: dio
+      sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "5.8.0+1"
+  dio_web_adapter:
+    dependency: transitive
+    description:
+      name: dio_web_adapter
+      sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.1.1"
   fake_async:
     dependency: transitive
     description:
@@ -62,6 +78,14 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_hooks:
+    dependency: "direct main"
+    description:
+      name: flutter_hooks
+      sha256: b772e710d16d7a20c0740c4f855095026b31c7eb5ba3ab67d2bd52021cd9461d
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "0.21.2"
   flutter_lints:
     dependency: "direct dev"
     description:
@@ -70,11 +94,35 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "5.0.0"
+  flutter_riverpod:
+    dependency: "direct main"
+    description:
+      name: flutter_riverpod
+      sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.6.1"
   flutter_test:
     dependency: "direct dev"
     description: flutter
     source: sdk
     version: "0.0.0"
+  http_parser:
+    dependency: transitive
+    description:
+      name: http_parser
+      sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "4.1.2"
+  intl:
+    dependency: "direct main"
+    description:
+      name: intl
+      sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "0.20.2"
   leak_tracker:
     dependency: transitive
     description:
@@ -131,6 +179,14 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.16.0"
+  nested:
+    dependency: transitive
+    description:
+      name: nested
+      sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.0.0"
   path:
     dependency: transitive
     description:
@@ -139,6 +195,46 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.9.1"
+  platform:
+    dependency: transitive
+    description:
+      name: platform
+      sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "3.1.6"
+  plugin_platform_interface:
+    dependency: transitive
+    description:
+      name: plugin_platform_interface
+      sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.1.8"
+  provider:
+    dependency: "direct main"
+    description:
+      name: provider
+      sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "6.1.5"
+  riverpod:
+    dependency: transitive
+    description:
+      name: riverpod
+      sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.6.1"
+  riverpod_annotation:
+    dependency: "direct main"
+    description:
+      name: riverpod_annotation
+      sha256: e14b0bf45b71326654e2705d462f21b958f987087be850afd60578fcd502d1b8
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.6.1"
   sky_engine:
     dependency: transitive
     description: flutter
@@ -152,6 +248,46 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.10.1"
+  sqflite:
+    dependency: "direct main"
+    description:
+      name: sqflite
+      sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.4.2"
+  sqflite_android:
+    dependency: transitive
+    description:
+      name: sqflite_android
+      sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.4.1"
+  sqflite_common:
+    dependency: transitive
+    description:
+      name: sqflite_common
+      sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.5.5"
+  sqflite_darwin:
+    dependency: transitive
+    description:
+      name: sqflite_darwin
+      sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.4.2"
+  sqflite_platform_interface:
+    dependency: transitive
+    description:
+      name: sqflite_platform_interface
+      sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.4.0"
   stack_trace:
     dependency: transitive
     description:
@@ -160,6 +296,14 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.12.1"
+  state_notifier:
+    dependency: transitive
+    description:
+      name: state_notifier
+      sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.0.0"
   stream_channel:
     dependency: transitive
     description:
@@ -176,6 +320,14 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.4.1"
+  synchronized:
+    dependency: transitive
+    description:
+      name: synchronized
+      sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "3.4.0"
   term_glyph:
     dependency: transitive
     description:
@@ -192,6 +344,14 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.7.4"
+  typed_data:
+    dependency: transitive
+    description:
+      name: typed_data
+      sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.4.0"
   vector_math:
     dependency: transitive
     description:
@@ -208,6 +368,14 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "15.0.0"
+  web:
+    dependency: transitive
+    description:
+      name: web
+      sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.1.1"
 sdks:
   dart: ">=3.8.1 <4.0.0"
-  flutter: ">=3.18.0-18.0.pre.54"
+  flutter: ">=3.24.0"

+ 8 - 1
pubspec.yaml

@@ -34,7 +34,14 @@ dependencies:
   # The following adds the Cupertino Icons font to your application.
   # Use with the CupertinoIcons class for iOS style icons.
   cupertino_icons: ^1.0.8
-
+  intl: ^0.20.2
+  provider: ^6.0.0 # 核心状态管理
+  dio: ^5.0.0       # 网络请求
+  sqflite: ^2.4.2
+  flutter_hooks: ^0.21.2
+  flutter_riverpod: ^2.6.1
+  riverpod_annotation: ^2.6.1
+  
 dev_dependencies:
   flutter_test:
     sdk: flutter

+ 0 - 30
test/widget_test.dart

@@ -1,30 +0,0 @@
-// This is a basic Flutter widget test.
-//
-// To perform an interaction with a widget in your test, use the WidgetTester
-// utility in the flutter_test package. For example, you can send tap and scroll
-// gestures. You can also use WidgetTester to find child widgets in the widget
-// tree, read text, and verify that the values of widget properties are correct.
-
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import 'package:japp_flutter/main.dart';
-
-void main() {
-  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
-    // Build our app and trigger a frame.
-    await tester.pumpWidget(const MyApp());
-
-    // Verify that our counter starts at 0.
-    expect(find.text('0'), findsOneWidget);
-    expect(find.text('1'), findsNothing);
-
-    // Tap the '+' icon and trigger a frame.
-    await tester.tap(find.byIcon(Icons.add));
-    await tester.pump();
-
-    // Verify that our counter has incremented.
-    expect(find.text('0'), findsNothing);
-    expect(find.text('1'), findsOneWidget);
-  });
-}