import 'package:flisp_app/provider/todo_provider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:flisp_app/models/todo.dart'; class TodoForm extends StatefulWidget { final GlobalKey formKey; final bool isEditing; final Todo? initialTodo; const TodoForm({ super.key, required this.formKey, required this.isEditing, this.initialTodo, }); @override State createState() => _TodoFormState(); } class _TodoFormState extends State { @override void initState() { super.initState(); } @override Widget build(BuildContext context) { final int maxTitleCount = 10; final int maxContentCount = 20; final provider = Provider.of(context); Widget buildTitle() { return Row( children: [ Icon( widget.isEditing ? Icons.edit_note : Icons.add_task, color: Colors.orange, size: 24, ), SizedBox(width: 8), Text( widget.isEditing ? '编辑待办事项' : '添加待办事项', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.orange, ), ), ], ); } FormBuilderTextField buildTitleField() { return FormBuilderTextField( name: 'title', initialValue: provider.formItem.title, onChanged: (value) { setState(() { provider.formItem.title = value ?? ''; }); }, decoration: InputDecoration( label: RichText( text: TextSpan( text: '标题', style: TextStyle( color: Colors.grey.shade700, fontSize: 16, ), children: const [ TextSpan( text: '*', style: TextStyle( color: Colors.red, fontSize: 18, fontWeight: FontWeight.bold, ), ), ], ), ), hintText: '请输入待办事项标题...', counterText: '', suffixText: '${provider.formItem.title.length}/$maxTitleCount', 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), ), filled: true, fillColor: Colors.white, prefixIcon: Icon(Icons.title, color: Colors.blue), contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14), ), maxLength: maxTitleCount, validator: (value) { if (value == null || value.isEmpty) { return '请输入标题'; } if (value.length > maxTitleCount) { return '标题不能超过$maxTitleCount个字符'; } return null; }, ); } FormBuilderTextField buildContentField() { return FormBuilderTextField( name: 'content', initialValue: provider.formItem.content, onChanged: (value) { setState(() { provider.formItem.content = value ?? ''; }); }, decoration: InputDecoration( labelText: '内容', hintText: '请输入待办事项内容...', counterText: '', suffixText: '${provider.formItem.content.length}/$maxContentCount', 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), ), filled: true, fillColor: Colors.white, prefixIcon: Icon(Icons.description, color: Colors.green), contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14), ), maxLines: 2, maxLength: maxContentCount, validator: (value) { if (value != null && value.length > maxContentCount) { return '内容不能超过$maxContentCount个字符'; } return null; }, ); } IconButton buildClearDueDateSuffixIcon() { return IconButton( icon: Icon(Icons.clear, size: 18), onPressed: () { setState(() { provider.formItem.dueDate = null; }); widget.formKey.currentState?.fields['dueDate']?.didChange(null); }, ); } FormBuilderDateTimePicker buildDueDateField() { return FormBuilderDateTimePicker( name: 'dueDate', format: DateFormat('yyyy-MM-dd'), initialValue: provider.formItem.dueDate, inputType: InputType.date, onChanged: (value) { setState(() { provider.formItem.dueDate = value; }); }, decoration: InputDecoration( labelText: provider.formItem.dueDate == null ? '选择截止日期' : '截止: ${DateFormat('yyyy-MM-dd').format(provider.formItem.dueDate!)}', labelStyle: TextStyle( color: provider.formItem.dueDate == null ? Colors.grey : Colors.black87, ), prefixIcon: Icon(Icons.calendar_month, color: Colors.purple), 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), ), filled: true, fillColor: Colors.white, contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14), suffixIcon: provider.formItem.dueDate != null ? buildClearDueDateSuffixIcon() : null, ), validator: (value) { if (value != null && value.isBefore(DateTime.now().subtract(Duration(days: 1)))) { return '不能选择过去的日期'; } return null; }, ); } IconButton buildClearScheduledTimeSuffixIcon() { return IconButton( icon: Icon(Icons.clear, size: 18), onPressed: () { setState(() { provider.formItem.scheduledTime = null; }); widget.formKey.currentState?.fields['scheduledTime']?.didChange(null); }, ); } FormBuilderDateTimePicker buildScheduledTimeField() { return FormBuilderDateTimePicker( name: 'scheduledTime', format: DateFormat('yyyy-MM-dd HH:mm:ss'), initialValue: provider.formItem.scheduledTime, inputType: InputType.both, onChanged: (value) { setState(() { provider.formItem.scheduledTime = value; }); }, decoration: InputDecoration( labelText: provider.formItem.scheduledTime == null ? '选择提醒日期' : '提醒: ${DateFormat('yyyy-MM-dd HH:mm:ss').format(provider.formItem.scheduledTime!)}', labelStyle: TextStyle( color: provider.formItem.scheduledTime == null ? Colors.grey : Colors.black87, ), prefixIcon: Icon(Icons.calendar_month, color: Colors.purple), 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), ), filled: true, fillColor: Colors.white, contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14), suffixIcon: provider.formItem.scheduledTime != null ? buildClearScheduledTimeSuffixIcon() : null, ), validator: (value) { if (value != null && value.isBefore(DateTime.now())) { return '不能选择过去的日期'; } return null; }, ); } FormBuilderRadioGroup builderRadioGroup() { return FormBuilderRadioGroup( name: 'priority', initialValue: provider.formItem.priority, decoration: InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.zero, ), orientation: OptionsOrientation.horizontal, wrapSpacing: 6, options: TodoPriority.values.map((priority) { return FormBuilderFieldOption( value: priority, child: Row( mainAxisSize: MainAxisSize.min, children: [Text(priority.label)], ), ); }).toList(), onChanged: (value) { if (value != null) { setState(() { provider.formItem.priority = value; }); } }, ); } Container buildPriorityField() { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey.shade300, width: 1), ), padding: EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.flag, color: Colors.orange), SizedBox(width: 6), Text('优先级'), ], ), builderRadioGroup(), ], ), ); } FormBuilder buildForm() { return FormBuilder( key: widget.formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ buildTitleField(), SizedBox(height: 12), buildContentField(), SizedBox(height: 12), buildDueDateField(), SizedBox(height: 12), buildScheduledTimeField(), SizedBox(height: 12), buildPriorityField(), ], ), ); } return Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, children: [buildTitle(), SizedBox(height: 20), buildForm()], ), ); } }