import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:image_picker/image_picker.dart'; import 'package:news_app/constant/color_res.dart'; import 'package:news_app/constant/size_res.dart'; import 'package:news_app/http/http_util.dart'; import 'package:news_app/widget/my_txt.dart'; import '../../widget/auth_gesture_detector.dart'; /// @author: bo.zeng /// @email: cnhbwds@gmail.com /// @date: 2025 2025/4/17 12:22 /// @description: /// 图片上传状态 enum UploadStatus { uploading, // 上传中 success, // 上传成功 failed, // 上传失败 } /// 图片数据模型 class ImageItem { final String localPath; // 本地路径 String? remoteUrl; // 远程URL UploadStatus status; // 上传状态 ImageItem({ required this.localPath, this.remoteUrl, this.status = UploadStatus.uploading, }); ImageItem copyWith({ String? localPath, String? remoteUrl, UploadStatus? status, }) { return ImageItem( localPath: localPath ?? this.localPath, remoteUrl: remoteUrl ?? this.remoteUrl, status: status ?? this.status, ); } } class CommentInputBarWidget extends ConsumerStatefulWidget { final FocusNode focusNode; final String id; final Function(String, List)? onSend; // 改为传递URL列表 const CommentInputBarWidget( this.focusNode, this.onSend, this.id, { super.key, }); @override ConsumerState createState() => _CommentInputBarState(); } class _CommentInputBarState extends ConsumerState { final TextEditingController _controller = TextEditingController(); final List _imageItems = []; static const int maxImageCount = 9; Future _pickImage() async { final List pickedFiles = await ImagePicker().pickMultiImage( imageQuality: 80, ); if (pickedFiles.isNotEmpty) { // 计算还能添加多少张图片 final remainingSlots = maxImageCount - _imageItems.length; if (remainingSlots <= 0) { // 已达到最大数量,不添加 return; } // 添加新图片,最多添加到9张 final imagesToAdd = pickedFiles.take(remainingSlots).toList(); setState(() { for (var file in imagesToAdd) { _imageItems.add(ImageItem(localPath: file.path)); } }); // 立即上传图片 _uploadImages(imagesToAdd.map((e) => e.path).toList()); } } /// 上传图片 Future _uploadImages(List filePaths) async { // 批量上传接口 final urls = await HttpUtil().uploadCommentImages(filePaths); setState(() { // 更新上传状态 int urlIndex = 0; for (int i = 0; i < _imageItems.length; i++) { if (_imageItems[i].status == UploadStatus.uploading && filePaths.contains(_imageItems[i].localPath)) { if (urlIndex < urls.length) { _imageItems[i] = _imageItems[i].copyWith( remoteUrl: urls[urlIndex], status: UploadStatus.success, ); urlIndex++; } else { _imageItems[i] = _imageItems[i].copyWith( status: UploadStatus.failed, ); } } } }); } /// 重试上传失败的图片 Future _retryUpload(int index) async { final item = _imageItems[index]; setState(() { _imageItems[index] = item.copyWith(status: UploadStatus.uploading); }); final url = await HttpUtil().uploadCommentImage(item.localPath); setState(() { if (url != null) { _imageItems[index] = item.copyWith( remoteUrl: url, status: UploadStatus.success, ); } else { _imageItems[index] = item.copyWith(status: UploadStatus.failed); } }); } void _removeImage(int index) { setState(() { _imageItems.removeAt(index); }); } void _clearImages() { setState(() { _imageItems.clear(); }); } @override Widget build(BuildContext context) { double size = MediaQuery.of(context).padding.bottom; return Container( decoration: BoxDecoration( color: Colors.white, border: Border(top: BorderSide(color: colorE5E5E5, width: 0.5)), ), padding: EdgeInsets.only( top: horizontalPadding, bottom: size > 0 ? size : horizontalPadding, ), child: Column( crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: [ // 图片预览区域 - 支持多张图片 if (_imageItems.isNotEmpty) Container( height: 60.h, margin: EdgeInsets.only(bottom: 10.w), child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: List.generate(_imageItems.length, (index) { final item = _imageItems[index]; return Container( width: 60.w, height: 60.h, margin: EdgeInsets.only(right: 8.w), child: Stack( alignment: Alignment.topRight, children: [ // 显示图片(本地或远程) ClipRRect( borderRadius: BorderRadius.circular(4.r), child: item.status == UploadStatus.success && item.remoteUrl != null ? CachedNetworkImage( imageUrl: item.remoteUrl!, width: 60.w, height: 60.h, fit: BoxFit.cover, placeholder: (context, url) => Image.file( File(item.localPath), width: 60.w, height: 60.h, fit: BoxFit.cover, ), errorWidget: (context, url, error) => Image.file( File(item.localPath), width: 60.w, height: 60.h, fit: BoxFit.cover, ), ) : Image.file( File(item.localPath), width: 60.w, height: 60.h, fit: BoxFit.cover, ), ), // 上传中遮罩 if (item.status == UploadStatus.uploading) Container( width: 60.w, height: 60.h, decoration: BoxDecoration( color: Colors.black26, borderRadius: BorderRadius.circular(4.r), ), child: Center( child: SizedBox( width: 20.w, height: 20.w, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ), ), ), // 上传失败标记 if (item.status == UploadStatus.failed) Container( width: 60.w, height: 60.h, decoration: BoxDecoration( color: Colors.black26, borderRadius: BorderRadius.circular(4.r), ), child: Center( child: GestureDetector( onTap: () => _retryUpload(index), child: Icon( Icons.refresh, color: Colors.white, size: 20.w, ), ), ), ), // 删除按钮 GestureDetector( onTap: () => _removeImage(index), child: Container( margin: EdgeInsets.all(2.w), width: 16.w, height: 16.w, decoration: BoxDecoration( color: Colors.black54, shape: BoxShape.circle, ), child: Icon( Icons.close, color: Colors.white, size: 10.w, ), ), ), ], ), ); }), ), ), ), Row( children: [ Expanded( child: Container( height: 40.h, margin: EdgeInsets.only(right: 10.w), child: TextField( decoration: InputDecoration( fillColor: colorF5F7FA, filled: true, border: OutlineInputBorder( borderRadius: BorderRadius.circular(5.r), borderSide: BorderSide.none, ), contentPadding: EdgeInsets.symmetric( horizontal: 10.w, vertical: 20.h, ), hintText: '快来写下你的评论吧~', hintStyle: TextStyle(color: color7788A0, fontSize: 12.sp), ), focusNode: widget.focusNode, controller: _controller, ), ), ), // 图片上传按钮 GestureDetector( onTap: _pickImage, child: Container( width: 40.w, height: 40.h, margin: EdgeInsets.only(right: 8.w), alignment: Alignment.center, child: Icon( Icons.image, color: _imageItems.isNotEmpty ? color188FFF : color7788A0, size: 24.w, ), ), ), AuthGestureDetector( onTap: () { // 获取所有上传成功的图片URL final successUrls = _imageItems .where((item) => item.status == UploadStatus.success && item.remoteUrl != null) .map((item) => item.remoteUrl!) .toList(); widget.onSend?.call(_controller.text, successUrls); _controller.clear(); _clearImages(); }, child: Container( width: 50.w, padding: EdgeInsets.symmetric(vertical: 5.h), alignment: Alignment.center, decoration: BoxDecoration( color: color188FFF, borderRadius: BorderRadius.circular(5.r), ), child: myTxt(text: "发送", color: Colors.white, fontSize: 12.sp), ), ), ], ), ], ), ); } }