如何使用Flutter创建看板应用并实现拖放功能?
Flutter 看板拖拽实现方案
下面是实现包含"To Do"、"In Progress"、"Done"三列且支持任务跨列拖拽的完整代码示例,基于Flutter原生组件实现,无需额外依赖:
1. 定义任务数据模型
class Task { final String id; final String title; final String status; // 对应 "todo"、"in_progress"、"done" Task({required this.id, required this.title, required this.status}); // 复制方法,用于更新任务状态 Task copyWith({String? status}) { return Task( id: id, title: title, status: status ?? this.status, ); } }
2. 主看板页面实现
import 'package:flutter/material.dart'; class KanbanBoard extends StatefulWidget { const KanbanBoard({super.key}); @override State<KanbanBoard> createState() => _KanbanBoardState(); } class _KanbanBoardState extends State<KanbanBoard> { // 初始化各列任务数据 List<Task> _todoTasks = [ Task(id: '1', title: '完成Flutter拖拽功能', status: 'todo'), Task(id: '2', title: '设计任务卡片样式', status: 'todo'), ]; List<Task> _inProgressTasks = [ Task(id: '3', title: '编写数据模型', status: 'in_progress'), ]; List<Task> _doneTasks = [ Task(id: '4', title: '创建Flutter项目', status: 'done'), ]; // 处理任务转移:从原列表移除,添加到目标列表 void _moveTask(Task task, String newStatus) { setState(() { // 从原状态列表移除 switch (task.status) { case 'todo': _todoTasks.remove(task); break; case 'in_progress': _inProgressTasks.remove(task); break; case 'done': _doneTasks.remove(task); break; } // 添加到新状态列表 final updatedTask = task.copyWith(status: newStatus); switch (newStatus) { case 'todo': _todoTasks.add(updatedTask); break; case 'in_progress': _inProgressTasks.add(updatedTask); break; case 'done': _doneTasks.add(updatedTask); break; } }); } // 构建任务卡片(可拖拽) Widget _buildTaskCard(Task task) { return Draggable<Task>( data: task, feedback: Material( elevation: 4, child: Container( width: 280, padding: const EdgeInsets.all(16), margin: const EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 4)], ), child: Text(task.title, style: const TextStyle(fontSize: 16)), ), ), childWhenDragging: Container( height: 60, margin: const EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(8), ), ), child: Container( padding: const EdgeInsets.all(16), margin: const EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 4)], ), child: Text(task.title, style: const TextStyle(fontSize: 16)), ), ); } // 构建看板列(可接收拖拽) Widget _buildKanbanColumn(String title, String status, List<Task> tasks) { return Expanded( child: Container( margin: const EdgeInsets.symmetric(horizontal: 8), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), const SizedBox(height: 16), Expanded( child: DragTarget<Task>( onAccept: (task) { _moveTask(task, status); }, builder: (context, candidateData, rejectedData) { return ListView.builder( itemCount: tasks.length, itemBuilder: (context, index) { return _buildTaskCard(tasks[index]); }, ); }, ), ), ], ), ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('项目任务看板')), body: Row( children: [ _buildKanbanColumn('To Do', 'todo', _todoTasks), _buildKanbanColumn('In Progress', 'in_progress', _inProgressTasks), _buildKanbanColumn('Done', 'done', _doneTasks), ], ), ); } }
3. 关键功能说明
- 拖拽组件:使用
Draggable包裹任务卡片,data字段传递任务对象,feedback定义拖拽时的预览样式,childWhenDragging定义拖拽时原位置的占位样式。 - 接收组件:使用
DragTarget包裹列内的任务列表,onAccept方法处理拖拽完成后的任务转移逻辑,通过setState更新列表刷新UI。 - 状态管理:这里用
StatefulWidget的本地状态管理任务列表,实际项目中可根据需求替换为Provider、Riverpod或Bloc等状态管理方案。
4. 扩展功能建议
- 如果需要支持同列内任务排序,可以将
ListView.builder替换为ReorderableListView,结合onReorder方法调整列表顺序。 - 可以添加任务添加/删除功能,通过弹窗或底部输入框实现。
- 支持任务详情展示,点击任务卡片跳转详情页面。
内容的提问来源于stack exchange,提问作者DarkSistem
相关产品推荐
相关产品推荐

