373 lines
12 KiB
Dart
373 lines
12 KiB
Dart
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<FormBuilderState> formKey;
|
|
final bool isEditing;
|
|
final Todo? initialTodo;
|
|
|
|
const TodoForm({
|
|
super.key,
|
|
required this.formKey,
|
|
required this.isEditing,
|
|
this.initialTodo,
|
|
});
|
|
|
|
@override
|
|
State<TodoForm> createState() => _TodoFormState();
|
|
}
|
|
|
|
class _TodoFormState extends State<TodoForm> {
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final int maxTitleCount = 10;
|
|
final int maxContentCount = 20;
|
|
final provider = Provider.of<TodoProvider>(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<TodoPriority>(
|
|
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<TodoPriority>(
|
|
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()],
|
|
),
|
|
);
|
|
}
|
|
}
|