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:news_app/constant/color_res.dart'; import 'package:news_app/extension/base.dart'; import '../../model/new_comment_model.dart'; import '../../widget/auth_gesture_detector.dart'; /// @author: bo.zeng /// @email: cnhbwds@gmail.com /// @date: 2025 2025/4/17 12:22 /// @description: /// 评论图片网格组件 - 参考微信朋友圈布局 class CommentImagesGrid extends StatelessWidget { final List images; const CommentImagesGrid({super.key, required this.images}); @override Widget build(BuildContext context) { if (images.isEmpty) return const SizedBox.shrink(); final count = images.length; double itemWidth = 0; double itemHeight = 0; // 根据图片数量计算尺寸(微信朋友圈风格) switch (count) { case 1: itemWidth = 200.w; itemHeight = 200.w; break; case 2: itemWidth = 150.w; itemHeight = 150.w; break; case 3: case 4: itemWidth = 130.w; itemHeight = 130.w; break; default: // 5-9张图片 itemWidth = 100.w; itemHeight = 100.w; break; } // 计算列数 int crossAxisCount = 1; if (count == 2 || count == 4) { crossAxisCount = 2; } else if (count >= 3) { crossAxisCount = count <= 4 ? 2 : 3; } return GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: crossAxisCount, mainAxisSpacing: 5.w, crossAxisSpacing: 5.w, childAspectRatio: 1, ), itemCount: count, itemBuilder: (context, index) { final imgUrl = images[index]; return GestureDetector( onTap: () => _previewImage(context, images, index), child: ClipRRect( borderRadius: BorderRadius.circular(4.r), child: CachedNetworkImage( imageUrl: imgUrl.toString(), width: itemWidth, height: itemHeight, fit: BoxFit.cover, placeholder: (context, url) => Container( width: itemWidth, height: itemHeight, color: colorF5F7FA, child: Icon(Icons.image, color: colorCCCCCC, size: 30.w), ), errorWidget: (context, url, error) => Container( width: itemWidth, height: itemHeight, color: colorF5F7FA, child: Icon(Icons.broken_image, color: colorCCCCCC, size: 30.w), ), ), ), ); }, ); } /// 预览图片 void _previewImage(BuildContext context, List images, int index) { Navigator.of(context).push( PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => ImageViewerPage( images: images.map((e) => e.toString()).toList(), initialIndex: index, ), transitionsBuilder: (context, animation, secondaryAnimation, child) { return FadeTransition( opacity: animation, child: child, ); }, ), ); } } /// 图片预览页面 class ImageViewerPage extends StatefulWidget { final List images; final int initialIndex; const ImageViewerPage({ super.key, required this.images, required this.initialIndex, }); @override State createState() => _ImageViewerPageState(); } class _ImageViewerPageState extends State { late PageController _pageController; late int _currentIndex; @override void initState() { super.initState(); _currentIndex = widget.initialIndex; _pageController = PageController(initialPage: _currentIndex); } @override void dispose() { _pageController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black, body: Stack( children: [ // 图片预览 PageView.builder( controller: _pageController, itemCount: widget.images.length, onPageChanged: (index) { setState(() { _currentIndex = index; }); }, itemBuilder: (context, index) { return Center( child: CachedNetworkImage( imageUrl: widget.images[index], fit: BoxFit.contain, placeholder: (context, url) => Center( child: CircularProgressIndicator(color: Colors.white), ), errorWidget: (context, url, error) => Center( child: Icon(Icons.broken_image, color: Colors.white, size: 50), ), ), ); }, ), // 关闭按钮 Positioned( top: MediaQuery.of(context).padding.top + 10, right: 10, child: GestureDetector( onTap: () => Navigator.pop(context), child: Container( padding: EdgeInsets.all(8.w), decoration: BoxDecoration( color: Colors.black54, shape: BoxShape.circle, ), child: Icon(Icons.close, color: Colors.white, size: 24.w), ), ), ), // 图片指示器 if (widget.images.length > 1) Positioned( bottom: MediaQuery.of(context).padding.bottom + 20, left: 0, right: 0, child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate( widget.images.length, (index) => AnimatedContainer( duration: const Duration(milliseconds: 200), margin: EdgeInsets.symmetric(horizontal: 3.w), width: _currentIndex == index ? 8.w : 6.w, height: 6.w, decoration: BoxDecoration( color: _currentIndex == index ? Colors.white : colorF5F7FA, borderRadius: BorderRadius.circular(3.r), ), ), ), ), ), ), ], ), ); } } class CommentItemWidget extends ConsumerStatefulWidget { //加一个回调函数 final Function(int, String)? onTap; final Function(int)? onLongTap; final Comment comment; final int commentIndex; const CommentItemWidget( this.onTap, this.onLongTap, { super.key, required this.comment, required this.commentIndex, }); @override ConsumerState createState() => _CommentItemWidgetState(); } class _CommentItemWidgetState extends ConsumerState { @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ CircleAvatar( radius: 13.w, backgroundImage: NetworkImage( widget.comment.fromUser?.avatar ?? "", ), ), SizedBox(width: 5.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, spacing: 4.h, children: [ Text( widget.comment.fromUser?.nickname ?? "", style: TextStyle( fontWeight: FontWeight.bold, color: color333333, fontSize: 14.sp, ), ), Text( widget.comment.content ?? "", style: TextStyle(color: color333333, fontSize: 14.sp), ), // 图片展示 if (widget.comment.resourceUrls != null && widget.comment.resourceUrls!.isNotEmpty) Padding( padding: EdgeInsets.only(top: 8.h), child: CommentImagesGrid( images: widget.comment.resourceUrls!, ), ), Row( spacing: 10.w, children: [ Text( widget.comment.createTime ?? "", style: TextStyle(color: color7788A0, fontSize: 12.sp), ), AuthGestureDetector( onTap: () { widget.onTap?.call( widget.commentIndex, widget.comment.fromUser?.nickname ?? "", ); }, child: Text( '回复', style: TextStyle(color: color333333, fontSize: 12.sp), ), ), SizedBox( width: 5.w, ), AuthGestureDetector( onTap: () { widget.onLongTap?.call( widget.commentIndex, ); }, child: Text( '举报', style: TextStyle(color: color333333, fontSize: 12.sp), ), ), ], ), ], ), ), ], ), if (widget.comment.subComment.isSafeData) Padding( padding: EdgeInsets.only(left: 30.w, top: 10.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: widget.comment.subComment!.map((sub) { return Padding( padding: EdgeInsets.only(bottom: 8.h), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ CircleAvatar( radius: 13.w, backgroundImage: NetworkImage( widget.comment.fromUser?.avatar ?? "", ), ), SizedBox(width: 5.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, spacing: 4.h, children: [ Text( sub.fromUser?.nickname ?? "", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 14.sp, ), ), Text( sub.content ?? "", style: TextStyle(fontSize: 14.sp), ), Row( spacing: 10.w, children: [ Text( sub.createTime ?? "", style: TextStyle( color: color7788A0, fontSize: 12.sp, ), ), ], ), ], ), ), ], ), ); }).toList(), ), ), ], ); } }