欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

一、本日目标

  1. 实现作业管理页面
  2. 创建作业数据模型
  3. 实现作业卡片组件
  4. 完成作业增删改查功能
  5. 添加筛选和统计功能

二、文件变更

lib/
├── models/
│   └── homework.dart                  # 作业数据模型(新增)
├── widgets/
│   ├── homework_card.dart             # 作业卡片组件(新增)
│   └── add_homework_dialog.dart       # 添加/编辑作业弹窗(新增)
└── pages/
    └── homework_page.dart             # 作业主页(重构)

三、核心代码实现

3.1 作业数据模型 (lib/models/homework.dart)
class Homework {
  final String id;
  final String title;       // 作业标题
  final String courseName;  // 所属课程
  final String description; // 作业描述
  final DateTime deadline;  // 截止日期
  final bool isCompleted;   // 是否完成
  final int priority;       // 优先级 1-高 2-中 3-低

  Homework({
    required this.id,
    required this.title,
    required this.courseName,
    required this.description,
    required this.deadline,
    this.isCompleted = false,
    this.priority = 2,
  });

  // 从 JSON 解析
  factory Homework.fromJson(Map<String, dynamic> json) {
    return Homework(
      id: json['id'],
      title: json['title'],
      courseName: json['courseName'],
      description: json['description'],
      deadline: DateTime.parse(json['deadline']),
      isCompleted: json['isCompleted'] ?? false,
      priority: json['priority'] ?? 2,
    );
  }

  // 转换为 JSON
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'title': title,
      'courseName': courseName,
      'description': description,
      'deadline': deadline.toIso8601String(),
      'isCompleted': isCompleted,
      'priority': priority,
    };
  }

  // 复制并修改
  Homework copyWith({
    String? id,
    String? title,
    String? courseName,
    String? description,
    DateTime? deadline,
    bool? isCompleted,
    int? priority,
  }) {
    return Homework(
      id: id ?? this.id,
      title: title ?? this.title,
      courseName: courseName ?? this.courseName,
      description: description ?? this.description,
      deadline: deadline ?? this.deadline,
      isCompleted: isCompleted ?? this.isCompleted,
      priority: priority ?? this.priority,
    );
  }
}
3.2 作业卡片组件 (lib/widgets/homework_card.dart)

支持完成状态切换、编辑和删除操作:

class HomeworkCard extends StatelessWidget {
  final Homework homework;
  final VoidCallback onToggleComplete;
  final VoidCallback onEdit;
  final VoidCallback onDelete;

  const HomeworkCard({
    super.key,
    required this.homework,
    required this.onToggleComplete,
    required this.onEdit,
    required this.onDelete,
  });

  
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.only(bottom: 12),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.1), blurRadius: 8)],
      ),
      child: Material(
        color: Colors.transparent,
        borderRadius: BorderRadius.circular(12),
        child: InkWell(
          onTap: onEdit,
          child: Container(
            padding: const EdgeInsets.all(16),
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Checkbox(value: homework.isCompleted, onChanged: (_) => onToggleComplete(), activeColor: Colors.green),
                Expanded(child: _buildContent()),
                PopupMenuButton<String>(
                  onSelected: (value) => value == 'edit' ? onEdit() : onDelete(),
                  itemBuilder: (context) => const [
                    PopupMenuItem(value: 'edit', child: Row(children: [Icon(Icons.edit, size: 18), SizedBox(width: 8), Text('编辑')])),
                    PopupMenuItem(value: 'delete', child: Row(children: [Icon(Icons.delete, size: 18, color: Colors.red), SizedBox(width: 8), Text('删除', style: TextStyle(color: Colors.red))])),
                  ],
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildContent() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(children: [
          Expanded(child: Text(homework.title, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, decoration: homework.isCompleted ? TextDecoration.lineThrough : null))),
          _buildPriorityChip(),
        ]),
        const SizedBox(height: 4),
        Row(children: [Icon(Icons.book, size: 14, color: Colors.grey[600]), const SizedBox(width: 4), Text(homework.courseName, style: TextStyle(fontSize: 13))]),
        if (homework.description.isNotEmpty) ...[
          const SizedBox(height: 4),
          Text(homework.description, style: TextStyle(fontSize: 13, color: Colors.grey[600]), maxLines: 2, overflow: TextOverflow.ellipsis),
        ],
        const SizedBox(height: 4),
        Row(children: [Icon(Icons.event, size: 14, color: _getDeadlineColor()), const SizedBox(width: 4), Text('截止:${_formatDate(homework.deadline)}', style: TextStyle(fontSize: 12, color: _getDeadlineColor()))]),
      ],
    );
  }

  Widget _buildPriorityChip() {
    final colors = {1: Colors.red, 2: Colors.orange, 3: Colors.green};
    final labels = {1: '高', 2: '中', 3: '低'};
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
      decoration: BoxDecoration(color: colors[homework.priority]?.withOpacity(0.1), borderRadius: BorderRadius.circular(12)),
      child: Text(labels[homework.priority]!, style: TextStyle(fontSize: 11, color: colors[homework.priority])),
    );
  }

  Color _getDeadlineColor() {
    if (homework.isCompleted) return Colors.grey;
    if (homework.deadline.isBefore(DateTime.now())) return Colors.red;
    if (homework.deadline.difference(DateTime.now()).inDays <= 2) return Colors.orange;
    return Colors.grey;
  }

  String _formatDate(DateTime date) => '${date.month}/${date.day} ${date.hour.toString().padLeft(2, '0')}:${date.minute.toString().padLeft(2, '0')}';
}
3.3 添加/编辑作业弹窗 (lib/widgets/add_homework_dialog.dart)

支持作业标题、所属课程、描述、截止日期、优先级:

class AddHomeworkDialog extends StatefulWidget {
  final Homework? homework;
  const AddHomeworkDialog({super.key, this.homework});

  
  State<AddHomeworkDialog> createState() => _AddHomeworkDialogState();
}

class _AddHomeworkDialogState extends State<AddHomeworkDialog> {
  final _formKey = GlobalKey<FormState>();
  late String _title, _courseName, _description;
  late DateTime _deadline;
  late int _priority;

  
  void initState() {
    super.initState();
    if (widget.homework != null) {
      _title = widget.homework!.title;
      _courseName = widget.homework!.courseName;
      _description = widget.homework!.description;
      _deadline = widget.homework!.deadline;
      _priority = widget.homework!.priority;
    } else {
      _title = _courseName = _description = '';
      _deadline = DateTime.now().add(const Duration(days: 7));
      _priority = 2;
    }
  }

  
  Widget build(BuildContext context) {
    return Dialog(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: Container(
        padding: const EdgeInsets.all(20),
        child: Form(
          key: _formKey,
          child: SingleChildScrollView(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Text(widget.homework == null ? '添加作业' : '编辑作业', style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
                const SizedBox(height: 20),
                TextFormField(decoration: const InputDecoration(labelText: '作业标题'), validator: (v) => v?.isEmpty == true ? '请输入标题' : null, onSaved: (v) => _title = v!),
                const SizedBox(height: 16),
                DropdownButtonFormField<String>(
                  decoration: const InputDecoration(labelText: '所属课程'),
                  items: ['高等数学', '大学英语', '程序设计', '数据结构', '体育'].map((c) => DropdownMenuItem(value: c, child: Text(c))).toList(),
                  onChanged: (v) => setState(() => _courseName = v!),
                  validator: (v) => v == null ? '请选择课程' : null,
                ),
                const SizedBox(height: 16),
                TextFormField(decoration: const InputDecoration(labelText: '作业描述'), maxLines: 3, onSaved: (v) => _description = v!),
                const SizedBox(height: 16),
                ListTile(
                  tileColor: Colors.grey.shade50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
                  title: const Text('截止日期'),
                  trailing: Text('${_deadline.month}/${_deadline.day} ${_deadline.hour}:${_deadline.minute}'),
                  onTap: () async {
                    final date = await showDatePicker(context: context, initialDate: _deadline, firstDate: DateTime.now(), lastDate: DateTime.now().add(const Duration(days: 365)));
                    if (date != null) {
                      final time = await showTimePicker(context: context, initialTime: TimeOfDay.fromDateTime(_deadline));
                      if (time != null) setState(() => _deadline = DateTime(date.year, date.month, date.day, time.hour, time.minute));
                    }
                  },
                ),
                const SizedBox(height: 16),
                SegmentedButton<int>(
                  segments: const [ButtonSegment(value: 1, label: Text('高')), ButtonSegment(value: 2, label: Text('中')), ButtonSegment(value: 3, label: Text('低'))],
                  selected: {_priority},
                  onSelectionChanged: (set) => setState(() => _priority = set.first),
                ),
                const SizedBox(height: 24),
                Row(mainAxisAlignment: MainAxisAlignment.end, children: [
                  TextButton(onPressed: () => Navigator.pop(context), child: const Text('取消')),
                  ElevatedButton(onPressed: _submit, child: Text(widget.homework == null ? '添加' : '保存')),
                ]),
              ],
            ),
          ),
        ),
      ),
    );
  }

  void _submit() {
    if (_formKey.currentState!.validate()) {
      _formKey.currentState!.save();
      Navigator.pop(context, Homework(
        id: widget.homework?.id ?? DateTime.now().millisecondsSinceEpoch.toString(),
        title: _title, courseName: _courseName, description: _description, deadline: _deadline,
        isCompleted: widget.homework?.isCompleted ?? false, priority: _priority,
      ));
    }
  }
}
3.4 作业主页 (lib/pages/homework_page.dart)
class HomeworkPage extends StatefulWidget {
  const HomeworkPage({super.key});

  
  State<HomeworkPage> createState() => _HomeworkPageState();
}

class _HomeworkPageState extends State<HomeworkPage> {
  List<Homework> _homeworks = [];
  int _selectedFilter = 0;  // 0-全部 1-未完成 2-已完成

  final List<Homework> _initialHomeworks = [
    Homework(id: '1', title: '高等数学作业', courseName: '高等数学', description: '完成课本第45-50页习题', deadline: DateTime.now().add(const Duration(days: 5)), priority: 1),
    Homework(id: '2', title: '程序设计实验报告', courseName: '程序设计', description: '完成学生管理系统界面设计', deadline: DateTime.now().add(const Duration(days: 3)), priority: 2),
    Homework(id: '3', title: '英语作文', courseName: '大学英语', description: '写一篇关于环保的作文', deadline: DateTime.now().subtract(const Duration(days: 1)), priority: 1),
  ];

  
  void initState() {
    super.initState();
    _homeworks = List.from(_initialHomeworks);
  }

  
  Widget build(BuildContext context) {
    final filteredHomeworks = _getFilteredHomeworks();
    return Column(
      children: [
        _buildFilterChips(),
        _buildStatsBar(),
        Expanded(
          child: filteredHomeworks.isEmpty
              ? const Center(child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [Icon(Icons.assignment_turned_in, size: 64, color: Colors.grey), Text('暂无作业')]))
              : ListView.builder(
                  padding: const EdgeInsets.all(16),
                  itemCount: filteredHomeworks.length,
                  itemBuilder: (context, index) => HomeworkCard(
                    homework: filteredHomeworks[index],
                    onToggleComplete: () => _toggleComplete(filteredHomeworks[index].id),
                    onEdit: () => _editHomework(filteredHomeworks[index]),
                    onDelete: () => _deleteHomework(filteredHomeworks[index].id),
                  ),
                ),
        ),
      ],
    );
  }

  Widget _buildFilterChips() {
    return Container(padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row(children: [
      _buildFilterChip('全部', 0), const SizedBox(width: 8),
      _buildFilterChip('未完成', 1), const SizedBox(width: 8),
      _buildFilterChip('已完成', 2),
    ]));
  }

  Widget _buildFilterChip(String label, int index) => FilterChip(
    label: Text(label), selected: _selectedFilter == index,
    onSelected: (_) => setState(() => _selectedFilter = index),
    backgroundColor: Colors.grey.shade200, selectedColor: Colors.blue.shade100,
  );

  Widget _buildStatsBar() {
    final total = _homeworks.length, completed = _homeworks.where((h) => h.isCompleted).length;
    return Container(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      decoration: BoxDecoration(color: Colors.blue.shade50, borderRadius: BorderRadius.circular(12)),
      child: Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
        _buildStatItem('总作业', total, Colors.blue), _buildStatItem('已完成', completed, Colors.green), _buildStatItem('未完成', total - completed, Colors.orange),
      ]),
    );
  }

  Widget _buildStatItem(String label, int count, Color color) => Column(children: [
    Text('$count', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: color)),
    Text(label, style: TextStyle(fontSize: 12, color: Colors.grey[600])),
  ]);

  List<Homework> _getFilteredHomeworks() {
    switch (_selectedFilter) {
      case 1: return _homeworks.where((h) => !h.isCompleted).toList();
      case 2: return _homeworks.where((h) => h.isCompleted).toList();
      default: return _homeworks;
    }
  }

  void _addHomework() async {
    final result = await showDialog<Homework>(context: context, builder: (context) => const AddHomeworkDialog());
    if (result != null) setState(() => _homeworks.add(result));
  }

  void _editHomework(Homework homework) async {
    final result = await showDialog<Homework>(context: context, builder: (context) => AddHomeworkDialog(homework: homework));
    if (result != null) setState(() => _homeworks[_homeworks.indexWhere((h) => h.id == homework.id)] = result);
  }

  void _deleteHomework(String id) {
    showDialog(context: context, builder: (context) => AlertDialog(
      title: const Text('删除作业'), content: const Text('确定要删除这个作业吗?'),
      actions: [
        TextButton(onPressed: () => Navigator.pop(context), child: const Text('取消')),
        TextButton(onPressed: () => setState(() => _homeworks.removeWhere((h) => h.id == id)) || Navigator.pop(context), child: const Text('删除', style: TextStyle(color: Colors.red))),
      ],
    ));
  }

  void _toggleComplete(String id) => setState(() {
    final index = _homeworks.indexWhere((h) => h.id == id);
    if (index != -1) _homeworks[index] = _homeworks[index].copyWith(isCompleted: !_homeworks[index].isCompleted);
  });
}

四、本日成果

成果说明
✅ 作业数据模型定义 Homework 类,包含标题、课程、描述、截止日期、优先级、完成状态
✅ 作业卡片组件展示作业详情,支持复选框、优先级标签、截止日期颜色提示
✅ 添加作业底部浮动按钮,弹窗表单填写
✅ 编辑作业点击卡片或菜单编辑,修改后实时更新
✅ 删除作业菜单删除,带确认对话框
✅ 筛选功能全部/未完成/已完成 三种筛选
✅ 统计信息显示总作业数、已完成数、未完成数
✅ 完成状态复选框切换,已完成作业划线显示
✅ 截止日期智能提示过期显示红色,2天内显示橙色

五、交互流程图

┌─────────────────────────────────────────────────────────────────────┐
│                           作业页交互流程                              │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  底部➕按钮 ──→ 添加作业弹窗 ──→ 填写表单 ──→ 保存 ──→ 作业列表新增     │
│                                                                      │
│  点击作业卡片 ──→ 编辑作业弹窗 ──→ 修改表单 ──→ 保存 ──→ 列表更新      │
│                                                                      │
│  三点菜单 ──→ 编辑/删除                                               │
│              │                                                       │
│              └── 删除 ──→ 确认对话框 ──→ 确认 ──→ 列表移除            │
│                                                                      │
│  复选框 ──→ 切换完成状态 ──→ 卡片样式更新(划线/颜色变化)              │
│                                                                      │
│  筛选标签 ──→ 全部/未完成/已完成 ──→ 作业列表筛选显示                   │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

六、运行验证

flutter run
预期效果状态
作业页显示作业列表
顶部显示筛选标签和统计信息
底部显示浮动添加按钮
点击➕按钮可添加作业
点击复选框可切换完成状态
点击卡片可编辑作业
点击三点菜单可编辑/删除
删除时弹出确认对话框
筛选标签可切换显示不同状态作业
截止日期临近/过期有颜色提示

在这里插入图片描述

七、下一步计划

任务优先级
数据持久化(保存到本地)
个人页面完善
作业提醒通知
课表导入(拍照识别OCR)

本日完成:作业管理页面完整实现,支持增删改查、筛选统计、完成状态管理。项目核心功能已基本具备,下一步将实现数据持久化。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐