feat: 带图评论

This commit is contained in:
guozhigq
2024-10-17 00:40:42 +08:00
parent 853627097d
commit 113a76894a
12 changed files with 319 additions and 7 deletions

View File

@ -1,13 +1,18 @@
import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:pilipala/http/dynamics.dart';
import 'package:pilipala/http/reply.dart';
import 'package:pilipala/http/video.dart';
import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/models/video/reply/emote.dart';
import 'package:pilipala/models/video/reply/item.dart';
import 'package:pilipala/pages/emote/index.dart';
import 'package:pilipala/plugin/pl_gallery/hero_dialog_route.dart';
import 'package:pilipala/plugin/pl_gallery/interactiveviewer_gallery.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'toolbar_icon_button.dart';
@ -44,6 +49,9 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
RxBool isForward = false.obs;
RxBool showForward = false.obs;
RxString message = ''.obs;
final ImagePicker _picker = ImagePicker();
RxList<String> imageList = [''].obs;
List<Map<dynamic, dynamic>> pictures = [];
@override
void initState() {
@ -60,6 +68,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
if (routePath.startsWith('/video')) {
showForward.value = true;
}
imageList.clear();
}
_autoFocus() async {
@ -90,6 +99,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
message: widget.replyItem != null && widget.replyItem!.root != 0
? ' 回复 @${widget.replyItem!.member!.uname!} : ${message.value}'
: message.value,
pictures: pictures,
);
if (result['status']) {
SmartDialog.showToast(result['data']['success_toast']);
@ -125,6 +135,62 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
);
}
void onChooseImage() async {
if (mounted) {
try {
final XFile? pickedFile =
await _picker.pickImage(source: ImageSource.gallery);
print('选择图片成功: ${pickedFile}');
var res = await ReplyHttp.uploadImage(xFile: pickedFile!);
if (res['status']) {
imageList.add(res['data']['img_src']);
pictures.add(res['data']);
print('imageList: $imageList');
print('pictures: $pictures');
}
} catch (e) {
print('选择图片失败: $e');
}
}
}
void onPreviewImg(picList, initIndex, context) {
Navigator.of(context).push(
HeroDialogRoute<void>(
builder: (BuildContext context) => InteractiveviewerGallery(
sources: picList,
initIndex: initIndex,
itemBuilder: (
BuildContext context,
int index,
bool isFocus,
bool enablePageView,
) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
if (enablePageView) {
Navigator.of(context).pop();
}
},
child: Center(
child: Hero(
tag: picList[index],
child: CachedNetworkImage(
fadeInDuration: const Duration(milliseconds: 0),
imageUrl: picList[index],
fit: BoxFit.contain,
),
),
),
);
},
onPageChanged: (int pageIndex) {},
),
),
);
}
@override
void didChangeMetrics() {
super.didChangeMetrics();
@ -175,10 +241,11 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 200,
maxHeight: 250,
minHeight: 120,
),
child: Container(
@ -209,6 +276,64 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
),
),
),
Obx(
() => Padding(
padding: const EdgeInsets.fromLTRB(12, 10, 12, 10),
child: SizedBox(
height: 65, // 固定高度以避免无限扩展
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: imageList.length,
itemBuilder: (context, index) {
final url = imageList[index];
return url != ''
? Container(
width: 65,
height: 65,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.secondaryContainer,
borderRadius:
const BorderRadius.all(Radius.circular(6))),
child: InkWell(
onTap: () =>
onPreviewImg(imageList, index, context),
onLongPress: () {
imageList.removeAt(index);
},
child: CachedNetworkImage(
imageUrl: url,
width: 65,
height: 65,
fit: BoxFit.cover,
),
),
)
: const SizedBox();
},
separatorBuilder: (context, index) =>
const SizedBox(width: 8.0),
),
),
),
),
Obx(
() => Visibility(
visible: imageList.isNotEmpty,
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 10),
child: Text(
'点击预览,长按删除',
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
fontSize: 12,
),
),
),
),
),
Divider(
height: 1,
color: Theme.of(context).dividerColor.withOpacity(0.1),
@ -240,7 +365,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
toolbarType: toolbarType,
selected: toolbarType == 'input',
),
const SizedBox(width: 20),
const SizedBox(width: 10),
ToolbarIconButton(
onPressed: () {
if (toolbarType == 'input') {
@ -254,6 +379,13 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
toolbarType: toolbarType,
selected: toolbarType == 'emote',
),
const SizedBox(width: 10),
ToolbarIconButton(
onPressed: onChooseImage,
icon: const Icon(Icons.photo, size: 22),
toolbarType: toolbarType,
selected: toolbarType == 'picture',
),
const SizedBox(width: 6),
Obx(
() => showForward.value