@@ -26,6 +26,12 @@ enum TDUploadFileStatus {
2626enum TDUploadType {
2727 add, // 添加
2828 remove, // 删除
29+ replace, // 替换
30+ }
31+
32+ enum TDUploadBoxType {
33+ roundedSquare, // 圆角方形
34+ circle, // 圆形
2935}
3036
3137class TDUploadFile {
@@ -70,7 +76,11 @@ class TDUpload extends StatefulWidget {
7076 this .onClick,
7177 required this .files,
7278 this .onChange,
73- this .multiple = false })
79+ this .multiple = false ,
80+ this .width = 80.0 ,
81+ this .height = 80.0 ,
82+ this .type = TDUploadBoxType .roundedSquare,
83+ this .enabledReplaceType = false })
7484 : super (key: key);
7585
7686 /// 控制展示的文件列表
@@ -100,9 +110,21 @@ class TDUpload extends StatefulWidget {
100110 /// 监听点击图片位
101111 final TDUploadClickEvent ? onClick;
102112
103- /// 监听添加或删除照片
113+ /// 监听添加, 删除和替换media事件
104114 final TDUploadValueChangedEvent ? onChange;
105115
116+ /// 图片宽度
117+ final double ? width;
118+
119+ /// 图片高度
120+ final double ? height;
121+
122+ /// Box类型
123+ final TDUploadBoxType type;
124+
125+ /// 是否启用replace功能
126+ final bool ? enabledReplaceType;
127+
106128 @override
107129 State <TDUpload > createState () => _TDUploadState ();
108130}
@@ -113,22 +135,28 @@ class _TDUploadState extends State<TDUpload> {
113135 bool get canUpload => widget.multiple ? (widget.max == 0 ? true : fileList.length < widget.max) : fileList.isEmpty;
114136 final ImagePicker _picker = ImagePicker ();
115137
138+ // 类型映射
139+ final Map <TDUploadBoxType , TDImageType > _imageTypeMap = {
140+ TDUploadBoxType .roundedSquare: TDImageType .roundedSquare,
141+ TDUploadBoxType .circle: TDImageType .circle,
142+ };
143+
116144 @override
117145 initState () {
118146 super .initState ();
119147 fileList = widget.files;
120148 }
121149
122150 // 获取相册照片或视频
123- Future <List <XFile >> getMediaFromPicker () async {
124- if (! canUpload || widget.mediaType.isEmpty) {
151+ Future <List <XFile >> getMediaFromPicker (bool isMultiple ) async {
152+ if (widget.mediaType.isEmpty) {
125153 return [];
126154 }
127155
128156 List <XFile > medias;
129157
130158 try {
131- if (widget.multiple ) {
159+ if (isMultiple ) {
132160 if (widget.mediaType.length == 1 && widget.mediaType.contains (TDUploadMediaType .image)) {
133161 medias = await _picker.pickMultiImage ();
134162 } else {
@@ -194,11 +222,39 @@ class _TDUploadState extends State<TDUpload> {
194222 }
195223 }
196224
225+ // 替换资源
226+ void replaceMedia (List <XFile > files, TDUploadFile oldFile) async {
227+ if (files.isEmpty || files.length != 1 ) {
228+ return ;
229+ }
230+
231+ var result = await validateResources (files);
232+
233+ if (result != null ) {
234+ if (widget.onValidate != null ) {
235+ widget.onValidate !(result);
236+ }
237+ return ;
238+ }
239+
240+ var newFile = TDUploadFile (key: oldFile.key, file: File (files[0 ].path), assetPath: files[0 ].path);
241+
242+ if (widget.onChange != null ) {
243+ widget.onChange !([newFile], TDUploadType .replace);
244+ }
245+ }
246+
197247 // 校验资源
198- Future <TDUploadValidatorError ?> validateResources (List <XFile > files) async {
248+ Future <TDUploadValidatorError ?> validateResources (List <XFile > files, [ bool ? multiple] ) async {
199249 TDUploadValidatorError ? error;
200250
201- if (widget.multiple && widget.max > 0 ) {
251+ // 多选逻辑,优选从参数获取
252+ var isMultiple = widget.multiple;
253+ if (multiple != null ) {
254+ isMultiple = multiple;
255+ }
256+
257+ if (isMultiple && widget.max > 0 ) {
202258 var remain = widget.max - fileList.length;
203259
204260 if (files.length > remain) {
@@ -238,7 +294,11 @@ class _TDUploadState extends State<TDUpload> {
238294 children: [
239295 ...fileList.map ((file) => _buildImageBox (context, file)).toList (),
240296 _buildUploadBox (context, shouldDisplay: canUpload, onTap: () async {
241- final files = await getMediaFromPicker ();
297+ if (! canUpload) {
298+ return ;
299+ }
300+
301+ final files = await getMediaFromPicker (widget.multiple);
242302 extractImageList (files);
243303 }),
244304 ],
@@ -252,9 +312,14 @@ class _TDUploadState extends State<TDUpload> {
252312 child: GestureDetector (
253313 onTap: onTap,
254314 child: Container (
255- width: 80 ,
256- height: 80 ,
257- decoration: BoxDecoration (color: TDTheme .of (context).grayColor1, borderRadius: BorderRadius .circular (6 )),
315+ width: widget.width,
316+ height: widget.height,
317+ decoration: widget.type == TDUploadBoxType .circle
318+ ? BoxDecoration (
319+ shape: BoxShape .circle,
320+ color: TDTheme .of (context).grayColor1,
321+ )
322+ : BoxDecoration (color: TDTheme .of (context).grayColor1, borderRadius: BorderRadius .circular (6 )),
258323 child: const Center (
259324 child: Icon (
260325 TDIcons .add,
@@ -266,19 +331,26 @@ class _TDUploadState extends State<TDUpload> {
266331
267332 Widget _buildImageBox (BuildContext context, TDUploadFile file) {
268333 return GestureDetector (
269- onTap: () {
334+ onTap: () async {
270335 if (widget.onClick != null ) {
271336 widget.onClick !(file.key);
272337 }
338+ // 替换资源
339+ if (widget.enabledReplaceType ?? false ) {
340+ final files = await getMediaFromPicker (false );
341+ replaceMedia (files, file);
342+ }
273343 },
274344 child: Stack (
275345 children: [
276346 TDImage (
277- width: 80 ,
278- height: 80 ,
347+ key: Key (file.assetPath ?? '' ),
348+ width: widget.width,
349+ height: widget.height,
279350 imgUrl: file.remotePath,
280351 // assetUrl: file.assetPath,
281352 imageFile: file.file,
353+ type: _imageTypeMap[widget.type] ?? TDImageType .roundedSquare,
282354 ),
283355 Visibility (visible: file.status != TDUploadFileStatus .success, child: _buildShadowBox (file)),
284356 Visibility (
@@ -293,10 +365,15 @@ class _TDUploadState extends State<TDUpload> {
293365 child: Container (
294366 width: 20 ,
295367 height: 20 ,
296- decoration: const BoxDecoration (
297- color: Color .fromRGBO (0 , 0 , 0 , 0.6 ),
298- borderRadius:
299- BorderRadius .only (bottomLeft: Radius .circular (6 ), topRight: Radius .circular (6 ))),
368+ decoration: widget.type == TDUploadBoxType .circle
369+ ? const BoxDecoration (
370+ shape: BoxShape .circle,
371+ color: Color .fromRGBO (0 , 0 , 0 , 0.6 ),
372+ )
373+ : const BoxDecoration (
374+ color: Color .fromRGBO (0 , 0 , 0 , 0.6 ),
375+ borderRadius:
376+ BorderRadius .only (bottomLeft: Radius .circular (6 ), topRight: Radius .circular (6 ))),
300377 child: const Center (
301378 child: Icon (
302379 TDIcons .close,
@@ -326,14 +403,19 @@ class _TDUploadState extends State<TDUpload> {
326403 }
327404
328405 return Container (
329- width: 80 ,
330- height: 80 ,
331- decoration: BoxDecoration (color: const Color .fromRGBO (0 , 0 , 0 , 0.4 ), borderRadius: BorderRadius .circular (6 )),
406+ width: widget.width,
407+ height: widget.height,
408+ decoration: widget.type == TDUploadBoxType .circle
409+ ? const BoxDecoration (
410+ shape: BoxShape .circle,
411+ color: Color .fromRGBO (0 , 0 , 0 , 0.4 ),
412+ )
413+ : BoxDecoration (color: const Color .fromRGBO (0 , 0 , 0 , 0.4 ), borderRadius: BorderRadius .circular (6 )),
332414 child: Padding (
333415 padding: const EdgeInsets .symmetric (vertical: 16 ),
334416 child: Center (
335417 child: Column (
336- mainAxisAlignment: MainAxisAlignment .spaceBetween ,
418+ mainAxisAlignment: MainAxisAlignment .center ,
337419 children: [
338420 Visibility (
339421 visible: file.status == TDUploadFileStatus .loading,
@@ -350,10 +432,13 @@ class _TDUploadState extends State<TDUpload> {
350432 size: 24 ,
351433 color: Colors .white,
352434 )),
353- TDText (
354- displayText,
355- textColor: Colors .white,
356- style: const TextStyle (fontSize: 12 , height: 1.67 ),
435+ Padding (
436+ padding: const EdgeInsets .only (top: 4 ),
437+ child: TDText (
438+ displayText,
439+ textColor: Colors .white,
440+ style: const TextStyle (fontSize: 12 , height: 1.67 ),
441+ ),
357442 ),
358443 ],
359444 ),
0 commit comments