效果图如下,只实现ui效果。

ui展示组件
dart
import 'package:ayidaojia/common/index.dart';
import 'package:ducafe_ui_core/ducafe_ui_core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
/// 图片选择器组件(纯UI组件)
/// 支持多选、删除、自动换行
/// 所有业务逻辑由外部控制
class ImagePickerWidget extends StatelessWidget {
/// 已选择的图片列表
final List<String> images;
/// 最小张数
final int minCount;
/// 最大张数
final int maxCount;
/// 图片尺寸
final double imageSize;
/// 图片圆角
final double borderRadius;
/// 每行显示的图片数量
final int crossAxisCount;
/// 图片间距
final double spacing;
/// 点击添加按钮回调(由外部处理选择和上传逻辑)
final VoidCallback onAddTap;
/// 删除图片回调
final Function(int index) onDelete;
/// 点击图片预览回调
final Function(int index)? onPreview;
/// 是否显示删除确认弹窗
final bool showDeleteConfirm;
const ImagePickerWidget({
super.key,
required this.images,
required this.onAddTap,
required this.onDelete,
this.minCount = 0,
this.maxCount = 9,
this.imageSize = 200.0,
this.borderRadius = 20.0,
this.crossAxisCount = 4,
this.spacing = 20.0,
this.onPreview,
this.showDeleteConfirm = true,
});
// 删除图片
void _handleDelete(int index) {
if (showDeleteConfirm) {
// 显示确认弹窗
showGeneralDialog(
context: Get.context!,
pageBuilder: (BuildContext buildContext, Animation<double> animation, Animation<double> secondaryAnimation) {
return DialogWidget(
title: '删除图片',
description:'确定删除图片吗?',
onConfirm: () async {
onDelete(index);
Get.back();
},
);
});
} else {
onDelete(index);
}
}
// 构建图片项
Widget _buildImageItem(int index) {
return Stack(
children: [
// 图片
ImgWidget(
path: images[index],
width: imageSize.w,
height: imageSize.w,
radius: borderRadius.w,
fit: BoxFit.cover,
).onTap(() {
if (onPreview != null) {
onPreview!(index);
}
}),
// 删除按钮
Positioned(
top: 4.w,
right: 4.w,
child: Container(
width: 40.w,
height: 40.w,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.6),
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
size: 24.w,
color: Colors.white,
),
).onTap(() => _handleDelete(index)),
),
],
);
}
// 构建添加按钮
Widget _buildAddButton(BuildContext context) {
return Container(
width: imageSize.w,
height: imageSize.w,
decoration: BoxDecoration(
color: AppTheme.blockBgColor,
borderRadius: BorderRadius.circular(borderRadius.w),
border: Border.all(
color: AppTheme.dividerColor,
width: 2.w,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.add_photo_alternate_outlined,
size: 60.w,
color: AppTheme.color999,
),
SizedBox(height: 10.w),
TextWidget.body(
'上传图片',
size: 24.sp,
color: AppTheme.color999,
),
if (maxCount > 0)
Padding(
padding: EdgeInsets.only(top: 5.w),
child: TextWidget.body(
'${images.length}/$maxCount',
size: 20.sp,
color: AppTheme.color666,
),
),
],
),
).onTap(onAddTap);
}
@override
Widget build(BuildContext context) {
// 计算需要显示的所有项(图片 + 添加按钮)
List<Widget> items = [];
// 添加已选择的图片
for (int i = 0; i < images.length; i++) {
items.add(_buildImageItem(i));
}
// 如果未达到最大数量,显示添加按钮
if (images.length < maxCount) {
items.add(_buildAddButton(context));
}
return Wrap(
spacing: spacing.w,
runSpacing: spacing.w,
children: items,
);
}
}
/// 简化版:单图片选择器(纯UI组件)
class SingleImagePickerWidget extends StatelessWidget {
/// 图片 URL
final String? imageUrl;
/// 图片尺寸
final double imageSize;
/// 图片圆角
final double borderRadius;
/// 点击添加/更换图片回调(由外部处理选择和上传逻辑)
final VoidCallback onTap;
/// 提示文字
final String hintText;
const SingleImagePickerWidget({
super.key,
this.imageUrl,
required this.onTap,
this.imageSize = 200.0,
this.borderRadius = 20.0,
this.hintText = '上传图片',
});
@override
Widget build(BuildContext context) {
if (imageUrl != null && imageUrl!.isNotEmpty) {
// 显示已选择的图片
return Stack(
children: [
ImgWidget(
path: imageUrl!,
width: imageSize.w,
height: imageSize.w,
radius: borderRadius.w,
fit: BoxFit.cover,
),
// 更换按钮
Positioned(
top: -10.w,
right: -10.w,
child: Container(
width: 40.w,
height: 40.w,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.6),
shape: BoxShape.circle,
),
child: Icon(
Icons.edit,
size: 24.w,
color: Colors.white,
),
).onTap(onTap),
),
],
);
} else {
// 显示添加按钮
return Container(
width: imageSize.w,
height: imageSize.w,
decoration: BoxDecoration(
color: AppTheme.blockBgColor,
borderRadius: BorderRadius.circular(borderRadius.w),
border: Border.all(
color: AppTheme.dividerColor,
width: 2.w,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.add_photo_alternate_outlined,
size: 60.w,
color: AppTheme.color999,
),
SizedBox(height: 10.w),
TextWidget.body(
hintText,
size: 24.sp,
color: AppTheme.color999,
),
],
),
).onTap(onTap);
}
}
}
页面中使用
dart
// 上传图片组件
ImagePickerWidget(
images: controller.images,
minCount: 0,
maxCount: controller.maxImages,
imageSize: 150,
borderRadius: 16,
crossAxisCount: 4,
spacing: 20,
onAddTap: controller.pickImages, // 点击添加按钮触发选择
onDelete: controller.deleteImage,
showDeleteConfirm: true, // 显示删除确认
onPreview: (index) {
Get.to(() => ImagePreview(
imageList: controller.images,
initialIndex: index,
));
},
),
import 'dart:io';
import 'package:ayidaojia/common/index.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class EvaluateController extends GetxController {
EvaluateController();
// 评价图片列表
List<String> images = [];
// 最大图片数量
final int maxImages = 5;
// 删除图片(UI回调)
void deleteImage(int index) {
if (index >= 0 && index < images.length) {
images.removeAt(index);
update(["evaluate"]);
}
}
// 选择图片(需要自行处理上传逻辑)
void pickImages() {
int remainingCount = maxImages - images.length;
if (remainingCount <= 0) {
Loading.toast('最多只能上传$maxImages张图片'.tr);
return;
}
WechatImagePicker.showImagePicker(
context: Get.context!,
maxAssets: 1, // 单张选择
onSingleResult: (File? selectedFile) async {
try {
if (selectedFile == null) return Loading.toast('图片选择失败'.tr);
images.add(selectedFile.path);
update(["evaluate"]);
} catch (e) {
Loading.toast('图片处理失败'.tr);
} finally {
Loading.dismiss();
}
},
);
}
}