aslan-flutter/lib/chatvibe_ui/widgets/person/draggable_image_grid.dart
2026-07-01 18:25:58 +08:00

398 lines
13 KiB
Dart

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<ImageItem> images) callback;
DraggableImageGrid(this.callback);
@override
_DraggableImageGridState createState() => _DraggableImageGridState();
}
class _DraggableImageGridState extends State<DraggableImageGrid> {
// 图片列表数据
List<ImageItem> 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<PersonPhoto> 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<ImageItem>(
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<ImageItem>(
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);
}