import 'package:flisp_app/store/todo_dialog_store.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:flisp_app/models/todo.dart'; import 'package:flisp_app/utils/date_utils.dart'; import 'package:flisp_app/widgets/common.dart'; class TodoDialog extends StatefulWidget { final bool isEditing; final TodoItem? initialTodo; const TodoDialog({ super.key, required this.isEditing, this.initialTodo, }); @override State createState() => _TodoDialogState(); } class _TodoDialogState extends State { late TextEditingController _titleController; late TextEditingController _descriptionController; @override void initState() { super.initState(); _titleController = TextEditingController(); _descriptionController = TextEditingController(); // 如果是编辑模式,初始化数据 if (widget.isEditing && widget.initialTodo != null) { WidgetsBinding.instance.addPostFrameCallback((_) { final store = Provider.of(context, listen: false); store.initEditData(widget.initialTodo!); }); } } @override void dispose() { _titleController.dispose(); _descriptionController.dispose(); super.dispose(); } Future _selectDueDate(BuildContext context) async { final DateTime? picked = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime.now(), lastDate: DateTime(2100), ); if (picked != null) { final store = Provider.of(context, listen: false); store.dueDate = picked; } } @override Widget build(BuildContext context) { final store = Provider.of(context); // 同步控制器文本 if (_titleController.text != store.title) { _titleController.text = store.title; _titleController.selection = TextSelection.collapsed(offset: store.title.length); } if (_descriptionController.text != store.description) { _descriptionController.text = store.description; _descriptionController.selection = TextSelection.collapsed(offset: store.description.length); } return Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, children: [ // 标题 Row( children: [ Icon(Icons.add_task, color: Colors.orange, size: 24), SizedBox(width: 8), Text( widget.isEditing ? '编辑待办事项' : '添加待办事项', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.orange, ), ), ], ), SizedBox(height: 20), // 标题输入框 TextField( controller: _titleController, onChanged: (value) => store.title = value, decoration: InputDecoration( labelText: '标题', hintText: '请输入待办事项标题...', counterText: '', suffixText: '${store.title.length}/20', suffixStyle: TextStyle( color: store.title.length > 20 ? Colors.red : Colors.grey, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.orange, width: 1), ), filled: true, fillColor: Colors.white, prefixIcon: Icon(Icons.title, color: Colors.black87), contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14), ), style: TextStyle(fontSize: 16), maxLength: 20, ), SizedBox(height: 12), // 描述输入框 TextField( controller: _descriptionController, onChanged: (value) => store.description = value, decoration: InputDecoration( labelText: '内容', hintText: '请输入待办事项内容...', counterText: '', suffixText: '${store.description.length}/50', suffixStyle: TextStyle( color: store.description.length > 50 ? Colors.red : Colors.grey, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.orange, width: 1), ), filled: true, fillColor: Colors.white, prefixIcon: Icon(Icons.description, color: Colors.black87), contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14), ), style: TextStyle(fontSize: 16), maxLines: 2, maxLength: 50, ), SizedBox(height: 12), // 截止日期选择 Container( decoration: buildBoxDecoration(), child: ListTile( leading: Icon(Icons.calendar_today, color: Colors.blue), title: Text( store.dueDate == null ? '选择截止日期' : '截止: ${formatDate(store.dueDate!)}', style: TextStyle( color: store.dueDate == null ? Colors.grey : Colors.black87, ), ), trailing: store.dueDate != null ? IconButton( icon: Icon(Icons.clear, size: 18), onPressed: () { store.dueDate = null; }, ) : null, onTap: () => _selectDueDate(context), ), ), SizedBox(height: 12), // 优先级选择 Container( decoration: buildBoxDecoration(), child: ListTile( leading: Container( padding: EdgeInsets.all(6), decoration: BoxDecoration( color: store.priority.color.withAlpha(10), shape: BoxShape.circle, ), child: Icon( Icons.flag, color: store.priority.color, size: 18, ), ), title: Text( '优先级', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Colors.grey.shade700, ), ), trailing: DropdownButton( value: store.priority, underline: SizedBox(), icon: Icon(Icons.arrow_drop_down, color: Colors.grey.shade600), iconSize: 20, dropdownColor: Colors.white, borderRadius: BorderRadius.circular(12), onChanged: (value) { if (value != null) { store.priority = value; } }, items: TodoPriority.values.map((priority) { return DropdownMenuItem( value: priority, child: Container( padding: EdgeInsets.symmetric(vertical: 8), child: Row( children: [ Container( padding: EdgeInsets.all(4), decoration: buildBoxDecoration(), child: Icon( priority.icon, color: priority.color, size: 14, ), ), SizedBox(width: 12), Text( priority.label, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), ], ), ), ); }).toList(), ), contentPadding: EdgeInsets.symmetric(horizontal: 16), minLeadingWidth: 0, ), ), ], ), ); } }