Flutter开发模仿百度云盘创建文件夹功能Draggable和DragTarget的混合使用

使用LongPressDraggableDragTarget写了个类似于百度云盘管理文件和文件夹的功能(为了避免和列表的滑动手势冲突,所以采用LongPressDraggable而不是Draggable):

1、拖拽文件到文件夹中
2、拖拽两个文件可以合并成一个新的文件夹

效果如下:

实现效果

1、文件夹可以拖拽到另外一个文件夹中去
2、文件夹不可以拖拽到设备中去
3、设备可以拖拽到文件夹中去
4、两个设备可以合并成一个新的文件夹

使用到的三方 get: ^4.6.6

gif.gif

代码展示(代码注释都写的比较清楚,如果有不懂的可以在下方留言)

import 'package:flutter/material.dart';
import 'package:get/get.dart';class DraggableListView extends StatefulWidget {const DraggableListView({super.key});@overrideState<DraggableListView> createState() => _DraggableListViewState();
}class _DraggableListViewState extends State<DraggableListView> {final ScrollController _scrollController = ScrollController();final TextEditingController _nameController = TextEditingController();final List<Map<String, dynamic>> _gatherList = [{'label': '顺义区'},{'label': '朝阳区'},{'label': '通州区'},{'label': '密云区'},{'label': '海淀区'},];final List<Map<String, dynamic>> _deviceList = [{'label': '设备---1'},{'label': '设备---2'},{'label': '设备---3'},{'label': '设备---4'},{'label': '设备---5'},{'label': '设备---6'},{'label': '设备---7'},{'label': '设备---8'},{'label': '设备---9'},{'label': '设备---10'},{'label': '设备---11'},];///当前拖拽的cell的indexint dragIndex = 0;///判断拖拽的是文件夹还是设备bool isDragFile = false;@overridevoid initState() {super.initState();}@overridevoid dispose() {super.dispose();_scrollController.dispose();_nameController.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('DraggableListView'),),body: _buildBody(),);}Widget _buildBody() {Color bgColor = Colors.black38;return Column(children: [Expanded(child: ListView.builder(controller: _scrollController,itemCount: _deviceList.length + _gatherList.length,itemExtent: cellHeight,itemBuilder: (context, index) {///文件夹列表if (index < _gatherList.length) {return Container(padding: const EdgeInsets.symmetric(horizontal: 10.0,vertical: 5.0,),child: LongPressDraggable(data: index,//拖拽的文件夹内容展示feedback: _buildFeedbackContainer(index: index,isFile: true,),onDragStarted: () {dragIndex = index;isDragFile = true;},//被拖拽的文件夹cell在列表中的展示childWhenDragging: _buildContainerWhenDragging(),onDragUpdate: (details) {// 拖拽让列表上下滚动_scrollListView(details);},child: DragTarget<int>(onAccept: (int data) {if (!isDragFile) {///Get.snackbar("提示","${_deviceList[data]}被移动到${_gatherList[index]}中去了");///如果拖拽的是设备放到文件夹上,就移除设备_deviceList.removeAt(data);} else {///如果拖拽的是文件夹,当拖拽的文件夹和被拖拽的文件夹不是一个的时候,合并文件夹if (dragIndex != index) {///Get.snackbar("提示","${_gatherList[data]}被移动到${_gatherList[index]}中去了");///如果拖拽的是文件夹放到文件夹上,就移除文件夹_gatherList.removeAt(data);}}setState(() {});},onWillAccept: (data) {if (isDragFile) {///当拖拽的是某个文件夹的时候,如果拖拽的文件夹放到被拖拽的文件夹上面的时候,不改变原来文件夹的状态(背景色)if (dragIndex != index) {bgColor = Colors.red;}} else {bgColor = Colors.red;}return data != null;},onLeave: (data) {bgColor = bgColor;setState(() {});},builder: (context, candidateData, rejectedData) {///文件夹的cell展示return Container(alignment: Alignment.center,decoration: BoxDecoration(color: bgColor,borderRadius: const BorderRadius.all(Radius.circular(18.0),),),child: _buildGatherCell(index),);},),),);}///设备列表return Container(margin: const EdgeInsets.symmetric(horizontal: 10.0,vertical: 5.0,),child: LongPressDraggable(data: index - _gatherList.length,//拖拽的设备内容展示feedback: _buildFeedbackContainer(index: index,isFile: false,),//被拖拽的设备cell在列表中的展示childWhenDragging: _buildContainerWhenDragging(),onDragStarted: () {isDragFile = false;dragIndex = index - _gatherList.length;},onDragUpdate: (details) {// 拖拽让列表上下滚动_scrollListView(details);},child: DragTarget<int>(onAccept: (int data) {///拖拽设备放到设备上进行合并+创建新的文件夹///如果是把文件夹拖拽到设备上不做任何操作if (!isDragFile) {_mergeDevice(data: data, index: index);}},onWillAccept: (data) {if (!isDragFile) {if (dragIndex != (index - _gatherList.length)) {bgColor = Colors.red;}}return data != null;},onLeave: (data) {bgColor = bgColor;setState(() {});},builder: (context, candidateData, rejectedData) {return Container(alignment: Alignment.center,color: bgColor,child: _buildDeviceCell(index),);},),),);},),),],);}///创建文件夹的cellWidget _buildGatherCell(int index) {return Row(children: [const SizedBox(width: 50.0),Expanded(child: Align(alignment: Alignment.centerLeft,child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Text("${_gatherList[index]["label"]}",style: const TextStyle(color: Colors.white,fontSize: 16.0,),),],),),),const Icon(Icons.arrow_forward_ios,color: Colors.white,),const SizedBox(width: 10.0),],);}///创建设备的cellWidget _buildDeviceCell(int index) {return Row(children: [const SizedBox(width: 50.0),Expanded(child: Align(alignment: Alignment.centerLeft,child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Text("${_deviceList[index - _gatherList.length]["label"]}",style: const TextStyle(color: Colors.white,fontSize: 16.0,fontWeight: FontWeight.w500,),),],),),),],);}///合并两个设备-创建新的文件夹_mergeDevice({required int data,required int index,}) {Get.defaultDialog(title: "新建集合",titlePadding: const EdgeInsets.symmetric(vertical: 16.0),titleStyle: const TextStyle(color: Colors.white,fontWeight: FontWeight.w400,fontSize: 16.0,),backgroundColor: const Color.fromRGBO(25, 29, 39, 1),barrierDismissible: false,content: Column(children: [Container(height: 44.0,padding: const EdgeInsets.symmetric(horizontal: 10.0),margin: const EdgeInsets.symmetric(horizontal: 16.0),decoration: BoxDecoration(border: Border.all(color: const Color.fromRGBO(43, 82, 255, 1),),borderRadius: BorderRadius.circular(8.0),),alignment: Alignment.center,child: TextField(controller: _nameController,style: const TextStyle(color: Colors.white),decoration: const InputDecoration(border: InputBorder.none,enabledBorder: InputBorder.none,hintText: "新建集合",hintStyle: TextStyle(color: Color.fromRGBO(255, 255, 255, 0.45),fontSize: 16.0,),isCollapsed: true,// 输入框内容上下居中contentPadding: EdgeInsets.only(top: 0, bottom: 0),),),),const SizedBox(height: 20.0),Container(padding: const EdgeInsets.symmetric(horizontal: 16.0),child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,children: [Container(width: 105.0,height: 44.0,decoration: BoxDecoration(color: const Color.fromRGBO(2, 3, 6, 1),borderRadius: BorderRadius.circular(8.0),),child: TextButton(onPressed: () {_nameController.clear();setState(() {});Get.back();},child: const Text("取消",style: TextStyle(color: Colors.white,fontSize: 16.0,fontWeight: FontWeight.w400,),),),),Container(width: 105.0,height: 44.0,decoration: BoxDecoration(color: const Color.fromRGBO(43, 82, 255, 1),borderRadius: BorderRadius.circular(8.0),),child: TextButton(onPressed: () {if (_nameController.text.isEmpty) {Get.snackbar("提示", "请输入名称!");return;}for (var item in _gatherList) {if (item["label"] == _nameController.text) {Get.snackbar("提示", "该名称已存在,请重新输入!");return;}}var array = [_deviceList[data],_deviceList[index - _gatherList.length]];_deviceList.removeWhere((element) => array.contains(element));///删除设备之后再创建文件夹_gatherList.add({'label': _nameController.text},);var fileName = _nameController.text;_nameController.clear();setState(() {});Get.back();Get.snackbar("提示", "${array[0]}和${array[1]}已合并到文件夹${fileName}中");},child: const Text("确定",style: TextStyle(color: Colors.white,fontSize: 16.0,fontWeight: FontWeight.w400,),),),),],),)],),);}///拖拽的时候上下滚动列表_scrollListView(DragUpdateDetails details) {if (details.globalPosition.dy < 200) {if (_scrollController.offset <= 0.0) return;// 执行向下滚动操作_scrollController.jumpTo(_scrollController.offset - 5);}if (details.globalPosition.dy > 700) {if (_scrollController.offset >=_scrollController.position.maxScrollExtent) return;// 执行向上滚动操作_scrollController.jumpTo(_scrollController.offset + 5);}}///创建拖拽过程中的内容展示Widget _buildFeedbackContainer({required int index,required bool isFile, //是否是文件夹}) {return Container(alignment: Alignment.center,width: Get.width,height: cellHeight,decoration: BoxDecoration(borderRadius: const BorderRadius.all(Radius.circular(10.0),),color: Colors.yellow.withOpacity(0.6),),child: Text(isFile? "拖拽的内容:${_gatherList[index]["label"]}": "拖拽的设备:${_deviceList[index - _gatherList.length]["label"]}",style: const TextStyle(decoration: TextDecoration.none,fontSize: 20.0,color: Colors.red,),),);}///创建占位容器Widget _buildContainerWhenDragging() {return Container(alignment: Alignment.center,color: Colors.red,child: const Text("我是个占位容器,真实的我被拽走了",style: TextStyle(color: Colors.black,),),);}
}const cellHeight = 88.0;

简书地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/452610.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

肯尼斯·里科《C和指针》第11章 动态内存分配(1)动态内存分配的基础知识

数组的元素存储于内存中连续的位置上。当一个数组被声明时&#xff0c;它所需要的内存在编译时就被分配。但是&#xff0c;也可以使用动态内存分配在运行时为它分配内存。在本章中&#xff0c;我们将研究这两种技巧的区别&#xff0c;看看什么时候应该使用动态内存分配以及怎样…

2011-2022省级金融科技指数(基于百度搜索指数)

本文手工整理了2011-2022年金融科技相关关键词在各个省份的百度搜索指数&#xff0c;并汇总成金融科技指数。具体步骤如下。首先&#xff0c;基于商业银行小微企业信贷业务实践&#xff0c;参考沈悦和郭品&#xff08;2015&#xff09;&#xff0c;以及盛天翔和范从来&#xff…

【华为】GRE Over IPsec 实验配置

【思科】GRE Over IPsec 实验配置 前言报文格式 实验需求配置拓扑GRE配置步骤IPsec 配置步骤R1基础配置GRE 配置IPsec 配置 ISP_R2基础配置 R3基础配置GRE 配置IPsec 配置 PCPC1PC2 抓包检查OSPF建立GRE隧道建立IPsec 隧道建立Ping 配置文档 前言 GRE over IPSec可利用GRE和IP…

java入门、环境配置及其特点介绍

目录 一、java语言的重要特点 二、java开发工具包&#xff08;JDK&#xff09;及其环境配置 三、java入门代码 四、Java运行机制 五、java学习方法 一、java语言的重要特点 java是面向对象的Java是健壮性的。Java具有强类型机制、异常处理、垃圾的自动收集等特点java语言是跨…

八、并发工具(下)

九、ReentrantReadWriteLock 1&#xff09;使用 可以有多个读同时发生&#xff0c;读写不能同时发生&#xff0c;写写不能同时发生 Slf4j(topic "c.pool") public class Test7 {public static void main(String[] args) {DataContainer dataContainer new DataC…

JS(react)图片压缩+图片上传

上传dome var fileNodeTakeStock: any createRef();<inputref{fileNodeTakeStock}onChange{showPictureTakeStock}style{{ display: "none" }}id"fileInpBtn"type"file"accept"image/*" //限制上传格式multiple{false}capture&qu…

ShardingSphere 5.x 系列【1】专栏导读

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Spring Boot 版本 3.1.0 本系列ShardingSphere 版本 5.4.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-sharding-sphere-demo 文章目录 1. 背景2. 简介3. 适用人群4. 环境…

代码随想录算法训练营|day24

第七章 回溯算法 77.组合代码随想录文章详解总结 77.组合 以n5,k3为例 (1)for循环遍历&#xff0c;递归选择符合要求的值加入path&#xff0c;len(path)k时&#xff0c;返回 statrtIndex保证每次递归取到的值不重复 剪枝&#xff1a;i<n-(k-len(path))1 后续需要k-len(pat…

n-皇后问题(DFS)

题目 n−皇后问题是指将n个皇后放在nn的国际象棋棋盘上&#xff0c;使得皇后不能相互攻击到&#xff0c;即任意两个皇后都不能处于同一行、同一列或同一斜线上。 现在给定整数n&#xff0c;请你输出所有的满足条件的棋子摆法。 输入格式&#xff1a; 共一行&#xff0c;包含…

算法设计与分析实验:回溯

目录 一、组合总和 1.1 具体思路 1.2 思路展示 1.3 代码实现 1.4 复杂度分析 1.5 运行结果 二、全排列 2.1 具体思路 2.2 思路展示 2.3 代码实现 2.4 复杂度分析 2.5 运行结果 三、N皇后问题 3.1 具体思路 3.2 思路展示 3.3 代码实现 3.4 复杂度分析 3.5 运行…

WorkPlus助力企业构建高效沟通与协作的即时通讯平台

在信息高度交流的时代中&#xff0c;即时沟通和高效协作成为企业取得成功的关键因素。而即时通讯平台作为实现即时沟通和快速协作的基础工具&#xff0c;WorkPlus以其创新的设计和优质的服务&#xff0c;助力企业提升沟通效率和协作能力&#xff0c;促进创新与发展。 为何选择W…

【Chrono Engine学习总结】1-安装配置与程序运行

本文仅用于个人安装记录。 官方安装教程 https://api.projectchrono.org/8.0.0/tutorial_install_chrono.html Windows下安装 windows下安装就按照教程好了。采用cmake-gui进行配置&#xff0c;建议首次安装只安装核心模块。然后依此configure下irrlicht&#xff0c;sensor…