398 lines
13 KiB
Dart
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);
|
|
}
|