主要用的知识点:Draggable,DragTarget,GridView
只需要传一个图片数组就可以使用,代码如下:
import 'package:flutter/material.dart';
import 'package:yp_erp/Page/Procurement_page/model/procurement_model.dart';
import 'package:image_picker/image_picker.dart';
typedef DeleteCallBack = void Function(GoodsImageModel model);
typedef TakePhotoCallBack = void Function(Map imageInfo);
class DragGridViewBuild extends StatefulWidget {
List<GoodsImageModel> images; // 图片数组
GoodsImageModel movingValue; // 记录正在移动的数据
DeleteCallBack deleteAction; // 删除
TakePhotoCallBack takePhotoAction; // 拍照
DragGridViewBuild(
{this.images,
this.movingValue,
this.deleteAction,
this.takePhotoAction,
Key key})
: super(key: key);
@override
_DragGridViewBuildState createState() => _DragGridViewBuildState();
}
class _DragGridViewBuildState extends State<DragGridViewBuild> {
@override
Widget build(BuildContext context) {
return _buildGridView();
}
// 创建GridView
Widget _buildGridView() {
return GridView(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 1,
),
children: _buildItems(),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
);
}
// 生成GridView的items
List<Widget> _buildItems() {
List<Widget> items = [];
for (GoodsImageModel value in widget.images) {
items.add(_buildDragAbleItem(value));
}
// 最多是5张图片,不显示拍照按钮(这里可根据自己需求,设置最大上传图片的数量)
if (widget.images.length < 5) {
items.add(_buildTakePhotoBtn());
}
return items;
}
// 生成可拖动的item
Widget _buildDragAbleItem(GoodsImageModel value) {
return Draggable(
data: value,
child: DragTarget(
// candidateData为onWillAccept回调为true时可接收的数据列表,rejectedData为onWillAccept回调为false时拒绝时的数据列表
builder: (context, candidateData, rejectedData) {
return _buildBaseItem(value);
},
// 当拖拽锚点进入DragTarget范围时回调,true时会将Data数据添加到candidateData列表中;false时会将Data数据添加到rejectedData列表中
onWillAccept: (moveData) {
var accept = moveData != null;
if (accept) {
_exchangeItemAction(
{'moveData': moveData, 'toData': value, 'onAccept': false});
}
return accept;
},
// 接收Data数据,只有onWillAccept返回true且拖拽结束时拖拽锚点位于DragTarget内才会回调
onAccept: (moveData) {
_exchangeItemAction(
{'moveData': moveData, 'toData': value, 'onAccept': true});
},
),
// feedback: 跟随手指移动的视图
feedback: SizedBox(
width: 100,
height: 100,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(4)),
child: Image.network(
value.url,
fit: BoxFit.cover,
),
),
),
onDragStarted: () {
// 开始拖拽
_onDragStartedAction(value);
},
onDraggableCanceled: (Velocity velocity, Offset offset) {
//清空标记,进行重新绘制
_onDraggableCanceledAction();
},
onDragCompleted: () {
//清空标记,进行重新绘制
_onDraggableCanceledAction();
},
);
}
Widget _buildBaseItem(GoodsImageModel value) {
if (value.url == widget.movingValue.url) {
// 如果当前移动的和之前的是同一个值,显示空白
return Container();
}
return Stack(
children: [
SizedBox(
width: 88,
height: 88,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(4)),
child: Image.network(
value.url,
fit: BoxFit.cover,
),
),
),
Positioned(
child: GestureDetector(
onTap: () {
if (widget.deleteAction != null) {
widget.deleteAction(value);
}
},
behavior: HitTestBehavior.translucent,
child: Image.asset('images/drag_delete.png', height: 16, width: 16),
),
top: 4,
right: 8,
)
],
);
}
// 拍照的item
Widget _buildTakePhotoBtn() {
return GestureDetector(
onTap: getImage,
child: Image.asset(
'images/add_goods_take_photo_gray.png',
),
);
}
//选择相册照片
Future getImage() async {
XFile image = await ImagePicker()
.pickImage(source: ImageSource.camera, imageQuality: 60);
if (widget.takePhotoAction != null) {
widget.takePhotoAction({'imagePath': image});
}
}
// 开始拖拽 记录开始拖拽的数据
void _onDragStartedAction(GoodsImageModel model) {
setState(() {
widget.movingValue = model;
});
}
// 拖拽取消
void _onDraggableCanceledAction() {
setState(() {
widget.movingValue = GoodsImageModel();
});
}
// 数据交换
void _exchangeItemAction(Map info) {
setState(() {
var toIndex = widget.images.indexOf(info['toData']);
var moveData = info['moveData'];
var onAccept = info['onAccept'];
widget.images.remove(moveData);
widget.images.insert(toIndex, moveData);
if (onAccept) {
widget.movingValue = GoodsImageModel();
}
});
}
}
说明:GoodsImageModel是自己的图片模型,读者可使用自己的模型!可以根据前面的文章,把Image.network换成自定义的widget,点击图片有预览效果
图片选择用了一个三方插件image_picker
注:cv可直接用!!!! (帮忙点个赞呗)
初始状态 只有一个拍照按钮,没有其他虚线框!