feat:更新待办实现对话框内容
This commit is contained in:
51
lib/models/common.dart
Normal file
51
lib/models/common.dart
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import 'package:awesome_dialog/awesome_dialog.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
BoxDecoration buildBoxDecoration() {
|
||||||
|
return BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: Colors.grey[200]!, width: 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Container buildBody({Widget? child}) {
|
||||||
|
return Container(
|
||||||
|
color: Colors.grey[50],
|
||||||
|
padding: EdgeInsets.all(5),
|
||||||
|
child: SingleChildScrollView(child: child),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Container buildCard({Widget? child}) {
|
||||||
|
return Container(
|
||||||
|
decoration: buildBoxDecoration(),
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showAwesomeDialog({
|
||||||
|
required BuildContext context,
|
||||||
|
required Widget body,
|
||||||
|
required VoidCallback onOk,
|
||||||
|
required VoidCallback onCancel,
|
||||||
|
}) {
|
||||||
|
AwesomeDialog(
|
||||||
|
context: context,
|
||||||
|
dialogType: DialogType.noHeader,
|
||||||
|
animType: AnimType.scale,
|
||||||
|
body: body,
|
||||||
|
dialogBackgroundColor: Colors.white,
|
||||||
|
btnOkText: "确认",
|
||||||
|
btnCancelText: "取消",
|
||||||
|
btnOkColor: Colors.orange,
|
||||||
|
btnCancelColor: Colors.grey,
|
||||||
|
buttonsBorderRadius: BorderRadius.circular(10),
|
||||||
|
headerAnimationLoop: false,
|
||||||
|
dismissOnTouchOutside: false,
|
||||||
|
dismissOnBackKeyPress: true,
|
||||||
|
btnOkOnPress: onOk,
|
||||||
|
btnCancelOnPress: onCancel,
|
||||||
|
).show();
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'package:awesome_dialog/awesome_dialog.dart';
|
||||||
|
import 'package:flisp_app/models/common.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart'; // 用于复制功能
|
import 'package:flutter/services.dart'; // 用于复制功能
|
||||||
|
|
||||||
@@ -72,6 +74,7 @@ enum TodoFilter {
|
|||||||
today('今天');
|
today('今天');
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
|
|
||||||
const TodoFilter(this.label);
|
const TodoFilter(this.label);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,16 +110,24 @@ class _TodoPageState extends State<TodoPage> {
|
|||||||
|
|
||||||
// 统计数据
|
// 统计数据
|
||||||
int get _totalCount => _todos.length;
|
int get _totalCount => _todos.length;
|
||||||
int get _activeCount => _todos.where((todo) => !todo.isCompleted).length;
|
|
||||||
int get _completedCount => _todos.where((todo) => todo.isCompleted).length;
|
int get _activeCount =>
|
||||||
|
_todos
|
||||||
|
.where((todo) => !todo.isCompleted)
|
||||||
|
.length;
|
||||||
|
|
||||||
|
int get _completedCount =>
|
||||||
|
_todos
|
||||||
|
.where((todo) => todo.isCompleted)
|
||||||
|
.length;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('待办事项'),
|
title: const Text('待办事项'),
|
||||||
backgroundColor: Colors.orange,
|
backgroundColor: Colors.orange,
|
||||||
foregroundColor: Colors.white
|
foregroundColor: Colors.white
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -128,7 +139,9 @@ class _TodoPageState extends State<TodoPage> {
|
|||||||
|
|
||||||
// 待办事项列表
|
// 待办事项列表
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _filteredTodos.isEmpty ? _buildEmptyState() : _buildTodoList(),
|
child: _filteredTodos.isEmpty
|
||||||
|
? _buildEmptyState()
|
||||||
|
: _buildTodoList(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -287,7 +300,8 @@ class _TodoPageState extends State<TodoPage> {
|
|||||||
|
|
||||||
// 构建待办事项副标题
|
// 构建待办事项副标题
|
||||||
Widget? _buildTodoSubtitle(TodoItem todo, bool isOverdue) {
|
Widget? _buildTodoSubtitle(TodoItem todo, bool isOverdue) {
|
||||||
final hasContent = todo.description?.isNotEmpty == true || todo.dueDate != null;
|
final hasContent = todo.description?.isNotEmpty == true ||
|
||||||
|
todo.dueDate != null;
|
||||||
|
|
||||||
if (!hasContent) return null;
|
if (!hasContent) return null;
|
||||||
|
|
||||||
@@ -335,10 +349,11 @@ class _TodoPageState extends State<TodoPage> {
|
|||||||
// 显示添加待办事项对话框
|
// 显示添加待办事项对话框
|
||||||
void _showAddTodoDialog() {
|
void _showAddTodoDialog() {
|
||||||
_resetForm();
|
_resetForm();
|
||||||
showDialog(
|
|
||||||
context: context,
|
showAwesomeDialog(context: context,
|
||||||
builder: (context) => _buildTodoDialog(isEditing: false),
|
body: _buildTodoDialogContent(isEditing: true),
|
||||||
);
|
onOk: () => _saveTodo(false, null),
|
||||||
|
onCancel: () => _resetForm());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示编辑待办事项对话框
|
// 显示编辑待办事项对话框
|
||||||
@@ -349,92 +364,208 @@ class _TodoPageState extends State<TodoPage> {
|
|||||||
_selectedPriority = todo.priority;
|
_selectedPriority = todo.priority;
|
||||||
_selectedCategory = todo.category;
|
_selectedCategory = todo.category;
|
||||||
|
|
||||||
showDialog(
|
showAwesomeDialog(context: context,
|
||||||
context: context,
|
body: _buildTodoDialogContent(isEditing: true),
|
||||||
builder: (context) => _buildTodoDialog(isEditing: true, todo: todo),
|
onOk: () => _saveTodo(false, null),
|
||||||
);
|
onCancel: () => _resetForm());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建待办事项对话框
|
// 构建待办事项对话框
|
||||||
Widget _buildTodoDialog({bool isEditing = false, TodoItem? todo}) {
|
Widget _buildTodoDialogContent({bool isEditing = false}) {
|
||||||
return AlertDialog(
|
var title = isEditing ? '编辑待办事项' : '添加待办事项';
|
||||||
title: Text(isEditing ? '编辑待办事项' : '添加待办事项'),
|
return Padding(
|
||||||
content: SingleChildScrollView(
|
padding: const EdgeInsets.all(16),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
TextField(
|
// 标题
|
||||||
controller: _titleController,
|
Row(
|
||||||
decoration: const InputDecoration(
|
children: [
|
||||||
labelText: '标题*',
|
Icon(Icons.add_task, color: Colors.orange, size: 24),
|
||||||
border: OutlineInputBorder(),
|
SizedBox(width: 8),
|
||||||
|
Text(title, style: TextStyle(fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.orange)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
|
||||||
|
// 标题输入框
|
||||||
|
TextField(
|
||||||
|
controller: _titleController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: '标题',
|
||||||
|
hintText: '请输入待办事项标题...',
|
||||||
|
counterText: '',
|
||||||
|
suffixText: '${_titleController.text.length}/20',
|
||||||
|
suffixStyle: TextStyle(
|
||||||
|
color: _titleController.text.length > 20 ? Colors.red : Colors.grey,
|
||||||
),
|
),
|
||||||
maxLength: 100,
|
border: OutlineInputBorder(
|
||||||
),
|
borderRadius: BorderRadius.circular(12),
|
||||||
const SizedBox(height: 12),
|
borderSide: BorderSide(color: Colors.grey.shade200),
|
||||||
TextField(
|
|
||||||
controller: _descriptionController,
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: '描述',
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
),
|
||||||
maxLines: 2,
|
enabledBorder: OutlineInputBorder(
|
||||||
maxLength: 200,
|
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),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
style: TextStyle(fontSize: 16),
|
||||||
// 截止日期选择
|
maxLength: 20,
|
||||||
ListTile(
|
onChanged: (value) {
|
||||||
leading: const Icon(Icons.calendar_today),
|
if (context.mounted) setState(() {});
|
||||||
title: Text(_selectedDueDate == null
|
},
|
||||||
? '选择截止日期'
|
),
|
||||||
: '截止: ${_formatDate(_selectedDueDate!)}'),
|
|
||||||
|
SizedBox(height: 12),
|
||||||
|
|
||||||
|
// 描述输入框
|
||||||
|
TextField(
|
||||||
|
controller: _descriptionController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: '内容',
|
||||||
|
hintText: '请输入待办事项内容...',
|
||||||
|
counterText: '',
|
||||||
|
suffixText: '${_titleController.text.length}/50',
|
||||||
|
suffixStyle: TextStyle(
|
||||||
|
color: _titleController.text.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.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(
|
||||||
|
_selectedDueDate == null
|
||||||
|
? '选择截止日期'
|
||||||
|
: '截止: ${_formatDate(_selectedDueDate!)}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: _selectedDueDate == null ? Colors.grey : Colors
|
||||||
|
.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
trailing: _selectedDueDate != null
|
trailing: _selectedDueDate != null
|
||||||
? IconButton(
|
? IconButton(
|
||||||
icon: const Icon(Icons.clear),
|
icon: Icon(Icons.clear, size: 18),
|
||||||
onPressed: () => setState(() => _selectedDueDate = null),
|
onPressed: () {
|
||||||
|
setState(() => _selectedDueDate = null);
|
||||||
|
// 重新显示对话框以更新状态
|
||||||
|
_showAddTodoDialog();
|
||||||
|
},
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
onTap: () => _selectDueDate(),
|
onTap: () => _selectDueDateInDialog(),
|
||||||
),
|
),
|
||||||
// 优先级选择
|
),
|
||||||
ListTile(
|
SizedBox(height: 12),
|
||||||
leading: const Icon(Icons.flag),
|
|
||||||
title: const Text('优先级'),
|
// 优先级选择
|
||||||
|
Container(
|
||||||
|
decoration: buildBoxDecoration(),
|
||||||
|
child: ListTile(
|
||||||
|
leading: Container(
|
||||||
|
padding: EdgeInsets.all(6),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _selectedPriority.color.withAlpha(10),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.flag,
|
||||||
|
color: _selectedPriority.color,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'优先级',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.grey.shade700,
|
||||||
|
),
|
||||||
|
),
|
||||||
trailing: DropdownButton<Priority>(
|
trailing: DropdownButton<Priority>(
|
||||||
value: _selectedPriority,
|
value: _selectedPriority,
|
||||||
onChanged: (value) => setState(() => _selectedPriority = value!),
|
underline: SizedBox(),
|
||||||
|
icon: Icon(Icons.arrow_drop_down, color: Colors.grey.shade600),
|
||||||
|
iconSize: 20,
|
||||||
|
dropdownColor: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => _selectedPriority = value!);
|
||||||
|
},
|
||||||
items: Priority.values.map((priority) {
|
items: Priority.values.map((priority) {
|
||||||
return DropdownMenuItem(
|
return DropdownMenuItem(
|
||||||
value: priority,
|
value: priority,
|
||||||
child: Row(
|
child: Container(
|
||||||
children: [
|
padding: EdgeInsets.symmetric(vertical: 8),
|
||||||
Icon(priority.icon, color: priority.color, size: 16),
|
child: Row(
|
||||||
const SizedBox(width: 8),
|
children: [
|
||||||
Text(priority.label),
|
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(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
minLeadingWidth: 0,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
child: const Text('取消'),
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: _validateForm() ? () => _saveTodo(isEditing, todo) : null,
|
|
||||||
child: Text(isEditing ? '保存' : '添加'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选择截止日期
|
// 在对话框中选择日期
|
||||||
Future<void> _selectDueDate() async {
|
Future<void> _selectDueDateInDialog() async {
|
||||||
final DateTime? picked = await showDatePicker(
|
final DateTime? picked = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: DateTime.now(),
|
initialDate: DateTime.now(),
|
||||||
@@ -446,12 +577,16 @@ class _TodoPageState extends State<TodoPage> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_selectedDueDate = picked;
|
_selectedDueDate = picked;
|
||||||
});
|
});
|
||||||
|
// 重新显示对话框以更新日期显示
|
||||||
|
_showAddTodoDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证表单
|
// 验证表单
|
||||||
bool _validateForm() {
|
bool _validateForm() {
|
||||||
return _titleController.text.trim().isNotEmpty;
|
return _titleController.text
|
||||||
|
.trim()
|
||||||
|
.isNotEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置表单
|
// 重置表单
|
||||||
@@ -468,9 +603,14 @@ class _TodoPageState extends State<TodoPage> {
|
|||||||
if (!_validateForm()) return;
|
if (!_validateForm()) return;
|
||||||
|
|
||||||
final newTodo = TodoItem(
|
final newTodo = TodoItem(
|
||||||
id: isEditing ? todo!.id : DateTime.now().millisecondsSinceEpoch.toString(),
|
id: isEditing ? todo!.id : DateTime
|
||||||
|
.now()
|
||||||
|
.millisecondsSinceEpoch
|
||||||
|
.toString(),
|
||||||
title: _titleController.text.trim(),
|
title: _titleController.text.trim(),
|
||||||
description: _descriptionController.text.trim().isEmpty ?
|
description: _descriptionController.text
|
||||||
|
.trim()
|
||||||
|
.isEmpty ?
|
||||||
null : _descriptionController.text.trim(),
|
null : _descriptionController.text.trim(),
|
||||||
dueDate: _selectedDueDate,
|
dueDate: _selectedDueDate,
|
||||||
priority: _selectedPriority,
|
priority: _selectedPriority,
|
||||||
@@ -481,15 +621,32 @@ class _TodoPageState extends State<TodoPage> {
|
|||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
final index = _todos.indexWhere((t) => t.id == todo!.id);
|
final index = _todos.indexWhere((t) => t.id == todo!.id);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
_todos[index] = newTodo.copyWith(isCompleted: _todos[index].isCompleted);
|
_todos[index] =
|
||||||
|
newTodo.copyWith(isCompleted: _todos[index].isCompleted);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_todos.insert(0, newTodo);
|
_todos.insert(0, newTodo);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Navigator.pop(context);
|
|
||||||
_resetForm();
|
_resetForm();
|
||||||
|
|
||||||
|
// 显示成功提示
|
||||||
|
_showSuccessDialog(isEditing ? '更新成功' : '添加成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示成功提示
|
||||||
|
void _showSuccessDialog(String message) {
|
||||||
|
AwesomeDialog(
|
||||||
|
context: context,
|
||||||
|
dialogType: DialogType.success,
|
||||||
|
animType: AnimType.scale,
|
||||||
|
title: message,
|
||||||
|
btnOkText: "好的",
|
||||||
|
btnOkColor: Colors.green,
|
||||||
|
btnOkOnPress: () {},
|
||||||
|
autoHide: Duration(seconds: 2), // 2秒后自动关闭
|
||||||
|
).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换待办事项完成状态
|
// 切换待办事项完成状态
|
||||||
@@ -508,20 +665,21 @@ class _TodoPageState extends State<TodoPage> {
|
|||||||
Future<bool> _showDeleteConfirmation(TodoItem todo) async {
|
Future<bool> _showDeleteConfirmation(TodoItem todo) async {
|
||||||
return await showDialog<bool>(
|
return await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) =>
|
||||||
title: const Text('确认删除'),
|
AlertDialog(
|
||||||
content: Text('确定要删除"${todo.title}"吗?此操作不可撤销。'),
|
title: const Text('确认删除'),
|
||||||
actions: [
|
content: Text('确定要删除"${todo.title}"吗?此操作不可撤销。'),
|
||||||
TextButton(
|
actions: [
|
||||||
onPressed: () => Navigator.pop(context, false),
|
TextButton(
|
||||||
child: const Text('取消'),
|
onPressed: () => Navigator.pop(context, false),
|
||||||
|
child: const Text('取消'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context, true),
|
||||||
|
child: const Text('删除', style: TextStyle(color: Colors.red)),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context, true),
|
|
||||||
child: const Text('删除', style: TextStyle(color: Colors.red)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
) ?? false;
|
) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,36 +698,37 @@ class _TodoPageState extends State<TodoPage> {
|
|||||||
void _showTodoOptions(TodoItem todo) {
|
void _showTodoOptions(TodoItem todo) {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => Column(
|
builder: (context) =>
|
||||||
mainAxisSize: MainAxisSize.min,
|
Column(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
ListTile(
|
children: [
|
||||||
leading: const Icon(Icons.content_copy),
|
ListTile(
|
||||||
title: const Text('复制内容'),
|
leading: const Icon(Icons.content_copy),
|
||||||
onTap: () {
|
title: const Text('复制内容'),
|
||||||
Navigator.pop(context);
|
onTap: () {
|
||||||
_copyTodoContent(todo);
|
Navigator.pop(context);
|
||||||
},
|
_copyTodoContent(todo);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(todo.isCompleted ? Icons.undo : Icons.done_all),
|
||||||
|
title: Text(todo.isCompleted ? '标记为未完成' : '标记为已完成'),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
_toggleTodo(todo.id);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.delete, color: Colors.red),
|
||||||
|
title: const Text('删除', style: TextStyle(color: Colors.red)),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
_deleteTodo(todo.id);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
ListTile(
|
|
||||||
leading: Icon(todo.isCompleted ? Icons.undo : Icons.done_all),
|
|
||||||
title: Text(todo.isCompleted ? '标记为未完成' : '标记为已完成'),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
_toggleTodo(todo.id);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const Divider(),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.delete, color: Colors.red),
|
|
||||||
title: const Text('删除', style: TextStyle(color: Colors.red)),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
_deleteTodo(todo.id);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -609,7 +768,8 @@ class _TodoPageState extends State<TodoPage> {
|
|||||||
// 导出待办事项
|
// 导出待办事项
|
||||||
void _exportTodos() {
|
void _exportTodos() {
|
||||||
final exportText = _todos.map((todo) {
|
final exportText = _todos.map((todo) {
|
||||||
return '${todo.isCompleted ? '[✓]' : '[ ]'} ${todo.title}${todo.dueDate != null ? ' (截止: ${_formatDate(todo.dueDate!)})' : ''}';
|
return '${todo.isCompleted ? '[✓]' : '[ ]'} ${todo.title}${todo.dueDate !=
|
||||||
|
null ? ' (截止: ${_formatDate(todo.dueDate!)})' : ''}';
|
||||||
}).join('\n');
|
}).join('\n');
|
||||||
|
|
||||||
Clipboard.setData(ClipboardData(text: exportText));
|
Clipboard.setData(ClipboardData(text: exportText));
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <rive_native/rive_native_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
|
g_autoptr(FlPluginRegistrar) rive_native_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "RiveNativePlugin");
|
||||||
|
rive_native_plugin_register_with_registrar(rive_native_registrar);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
rive_native
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
@@ -5,8 +5,10 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import rive_native
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
RiveNativePlugin.register(with: registry.registrar(forPlugin: "RiveNativePlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
64
pubspec.lock
64
pubspec.lock
@@ -1,6 +1,14 @@
|
|||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.7.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -9,6 +17,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.12.0"
|
version: "2.12.0"
|
||||||
|
awesome_dialog:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: awesome_dialog
|
||||||
|
sha256: "4c5821a0a637ceee022084e78c1b8237dd4b8bfca4dd24ac2484662a56707338"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.3.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -96,6 +112,30 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
graphs:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: graphs
|
||||||
|
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
http:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.0"
|
||||||
|
http_parser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_parser
|
||||||
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.2"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -216,6 +256,22 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.5+1"
|
version: "6.1.5+1"
|
||||||
|
rive:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: rive
|
||||||
|
sha256: "20d91e17b1bae5f4030d252336227f990782c942cb44e41685a13ae5f885e641"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.14.0-dev.13"
|
||||||
|
rive_native:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: rive_native
|
||||||
|
sha256: "1b65b473939b008feb30c1f43785996063cefaf4ceddfa1a2ffe6051201e8d3a"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.16"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -325,6 +381,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.4"
|
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:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ dependencies:
|
|||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
provider: ^6.1.1 # 状态管理
|
provider: ^6.1.1 # 状态管理
|
||||||
shared_preferences: ^2.2.2 # 本地存储
|
shared_preferences: ^2.2.2 # 本地存储
|
||||||
|
awesome_dialog: ^3.3.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <rive_native/rive_native_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
RiveNativePluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("RiveNativePlugin"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
rive_native
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
Reference in New Issue
Block a user