import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:aslan/chatvibe_ui/components/at_compontent.dart'; import 'package:aslan/chatvibe_ui/theme/chatvibe_theme.dart'; import 'package:aslan/app_localizations.dart'; import 'package:aslan/config/pickImage.dart'; import 'package:aslan/chatvibe_ui/components/dialog/dialog_base.dart'; import 'package:aslan/chatvibe_ui/components/text/at_text.dart'; import 'package:aslan/chatvibe_core/utilities/at_pick_utils.dart'; import 'package:aslan/chatvibe_data/sources/local/user_manager.dart'; import 'package:aslan/chatvibe_domain/models/res/login_res.dart'; class DraggableImageGrid extends StatefulWidget { Function(List images) callback; DraggableImageGrid(this.callback); @override _DraggableImageGridState createState() => _DraggableImageGridState(); } class _DraggableImageGridState extends State { // 图片列表数据 List images = [ ImageItem('', 0, 0), ImageItem('', 1, 0), ImageItem('', 2, 0), ImageItem('', 3, 0), ImageItem('', 4, 0), ]; // 当前拖动的图片索引 int? draggedIndex; // 目标位置的索引 int? targetIndex; @override void initState() { super.initState(); List photoBackgoundImages = (AccountStorage().getCurrentUser()?.userProfile?.backgroundPhotos ?? []) .where((t) => t.status != 2) .toList(); for (int i = 0; i < photoBackgoundImages.length; i++) { var element = photoBackgoundImages[i]; images[i] = ImageItem(element.url ?? "", i, element.status ?? 0); } } @override Widget build(BuildContext context) { return Row( children: [ // 左边大图 _buildImageItem(images[0], 0, isLarge: true), SizedBox(width: 5.w), // 右边4张小图 Column( children: [ Row( children: [ _buildImageItem(images[1], 1), SizedBox(width: 5.w), _buildImageItem(images[2], 2), ], ), SizedBox(height: 5.w), Row( children: [ _buildImageItem(images[3], 3), SizedBox(width: 5.w), _buildImageItem(images[4], 4), ], ), ], ), ], ); } Widget _buildImageItem(ImageItem item, int index, {bool isLarge = false}) { final isDragging = draggedIndex == index; final isTarget = targetIndex == index && draggedIndex != null; return GestureDetector( child: DragTarget( onWillAccept: (data) { setState(() { targetIndex = index; }); return data != null; }, onAccept: (data) { _swapImages(data.id, item.id); widget.callback(images); setState(() { draggedIndex = null; targetIndex = null; }); }, onLeave: (data) { setState(() { targetIndex = null; }); }, builder: (context, candidateData, rejectedData) { return LongPressDraggable( data: item, onDragStarted: () { setState(() { draggedIndex = index; }); }, onDragEnd: (details) { setState(() { draggedIndex = null; targetIndex = null; }); }, feedback: Opacity( opacity: 0.8, child: Material( color: Colors.transparent, child: Container( width: isLarge ? ScreenUtil().screenWidth * 0.45 : ScreenUtil().screenWidth * 0.215, height: isLarge ? ScreenUtil().screenWidth * 0.45 : ScreenUtil().screenWidth * 0.215, child: item.path.isEmpty ? Image.asset( "atu_images/general/at_icon_edit_user_info_add_pic.png", fit: BoxFit.cover, ) : netImage( url: item.path, borderRadius: BorderRadius.circular(8.w), ), ), ), ), childWhenDragging: Container( width: isLarge ? ScreenUtil().screenWidth * 0.45 : ScreenUtil().screenWidth * 0.215, height: isLarge ? ScreenUtil().screenWidth * 0.45 : ScreenUtil().screenWidth * 0.215, decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(8), ), ), child: AnimatedContainer( duration: Duration(milliseconds: 350), width: isLarge ? ScreenUtil().screenWidth * 0.45 : ScreenUtil().screenWidth * 0.215, height: isLarge ? ScreenUtil().screenWidth * 0.45 : ScreenUtil().screenWidth * 0.215, decoration: BoxDecoration( border: isTarget ? Border.all(color: ChatVibeTheme.primaryColor, width: 2) : null, borderRadius: BorderRadius.circular(8), boxShadow: isDragging ? [ BoxShadow( color: Colors.black12, blurRadius: 10, spreadRadius: 2, ), ] : null, ), child: Stack( alignment: AlignmentDirectional.bottomCenter, children: [ // 图片 item.path.isEmpty ? Image.asset( "atu_images/general/at_icon_edit_user_info_add_pic.png", width: double.infinity, height: double.infinity, fit: BoxFit.cover, ) : netImage( url: item.path, width: double.infinity, height: double.infinity, borderRadius: BorderRadius.circular(8.w), ), item.path.isNotEmpty && item.status == 0 ? Container( alignment: AlignmentDirectional.center, margin: EdgeInsets.symmetric( horizontal: isLarge ? 1.w : 0, vertical: isLarge ? 1.w : 0, ), height: isLarge ? 22.w : 18.w, decoration: BoxDecoration( color: Colors.black38, borderRadius: BorderRadius.only( bottomLeft: Radius.circular(isLarge ? 12 : 8), bottomRight: Radius.circular(isLarge ? 12 : 8), ), ), child: text( ATAppLocalizations.of(context)!.underReview, textColor: Colors.white, fontSize: isLarge ? 10.sp : 9.sp, ), ) : Container(), ], ), ), ); }, ), onTap: () { if (images[index].path.isEmpty) { _showAddPicDialog(index); } else { _showDeletePicDialog(index); } }, ); } // 交换图片位置 void _swapImages(int sourceId, int targetId) { setState(() { final sourceIndex = images.indexWhere((item) => item.id == sourceId); final targetIndex = images.indexWhere((item) => item.id == targetId); if (sourceIndex != -1 && targetIndex != -1) { final temp = images[sourceIndex]; images[sourceIndex] = images[targetIndex]; images[targetIndex] = temp; } }); } void _showAddPicDialog(int index) { SmartDialog.show( tag: "showAddPicDialog", alignment: Alignment.bottomCenter, debounce: true, animationType: SmartAnimationType.fade, builder: (_) { return SafeArea(top:false,child: Container( padding: EdgeInsets.only(bottom: 10.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only( topLeft: Radius.circular(12.w), topRight: Radius.circular(12.w), ), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ GestureDetector( behavior: HitTestBehavior.opaque, child: Container( alignment: AlignmentDirectional.center, margin: EdgeInsets.only(top: 10.w), child: text( ATAppLocalizations.of(context)!.chooseFromAblum, textColor: Colors.black, fontWeight: FontWeight.w600, fontSize: 15.sp, ), ), onTap: () async { SmartDialog.dismiss(tag: "showAddPicDialog"); final File? result = await ImagePick.pickSingleFromGallery( context, ); if (result == null) return; // 用户取消选择 ATPickUtils.cropImage(result, context, 1, true, ( bool success, String url, ) { if (success) { _addBackgroundImage(url, index); } }); }, ), GestureDetector( behavior: HitTestBehavior.opaque, child: Container( alignment: AlignmentDirectional.center, margin: EdgeInsets.only(top: 10.w), child: text( ATAppLocalizations.of(context)!.camera, textColor: Colors.black, fontWeight: FontWeight.w600, fontSize: 15.sp, ), ), onTap: () async { SmartDialog.dismiss(tag: "showAddPicDialog"); var result = await ImagePick.pickFromCamera(context); if (result != null) { ATPickUtils.cropImage(result, context, 1, true, ( bool success, String url, ) { if (success) { _addBackgroundImage(url, index); } }); } }, ), GestureDetector( behavior: HitTestBehavior.opaque, child: Container( alignment: AlignmentDirectional.center, margin: EdgeInsets.only(top: 10.w), child: text( ATAppLocalizations.of(context)!.cancel, textColor: Colors.black, fontWeight: FontWeight.w600, fontSize: 15.sp, ), ), onTap: () { SmartDialog.dismiss(tag: "showAddPicDialog"); }, ), ], ), )); }, ); } void _addBackgroundImage(String url, int index) { setState(() { images[index] = ImageItem(url, index, 0); }); widget.callback(images); } void _showDeletePicDialog(int index) { SmartDialog.show( tag: "showConfirmDialog", alignment: Alignment.center, debounce: true, animationType: SmartAnimationType.fade, builder: (_) { return MsgDialog( title: ATAppLocalizations.of(context)!.tips, msg: ATAppLocalizations.of(context)!.doYouWantToDeleteIt, btnText: ATAppLocalizations.of(context)!.confirm, onEnsure: () { _removeBackgroundImage(index); }, ); }, ); } void _removeBackgroundImage(int index) { setState(() { images[index] = ImageItem("", index, 0); }); widget.callback(images); } } // 图片数据模型 class ImageItem { final String path; final int id; final num status; ImageItem(this.path, this.id, this.status); }