import 'dart:async'; import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:together_mobile/common/constants.dart'; import 'package:together_mobile/database/box_type.dart'; import 'package:together_mobile/models/contact_model.dart'; import 'package:together_mobile/models/init_get_it.dart'; import 'package:together_mobile/models/user_model.dart'; import 'package:together_mobile/request/server.dart'; import 'package:together_mobile/screens/message/image_view_screen/image_view_screen.dart'; class FriendMessageBubble extends StatefulWidget { const FriendMessageBubble({ super.key, required this.index, required this.length, required this.contactId, required this.senderId, required this.type, required this.dateTime, required this.isShowTime, required this.text, required this.attachments, required this.attachmentItems, }); final int index, length; final String contactId, senderId, type, dateTime, text; final bool isShowTime; final List attachments; final List attachmentItems; @override State createState() => _FriendMessageBubbleState(); } class _FriendMessageBubbleState extends State { // add late here so you can access widget instance List _isImagesLoaded = []; final List _timerList = []; late bool _isHideMsg; @override void initState() { super.initState(); _isImagesLoaded = List.filled(widget.attachments.length, false); for (var i = 0; i < widget.attachments.length; i++) { String imagePath = '${getIt.get().baseImageDir}/${widget.attachments[i]}'; File file = File(imagePath); if (file.existsSync()) { _isImagesLoaded[i] = true; } else { _isImagesLoaded[i] = false; _timerList.add( Timer.periodic( const Duration(milliseconds: 200), (timer) { if ((file.existsSync())) { setState(() { _isImagesLoaded[i] = true; }); timer.cancel(); } }, ), ); } } final chatSettingBox = Hive.box('chat_setting'); late final chatSetting = chatSettingBox.get(widget.contactId); _isHideMsg = chatSetting!.isHideMsg; if (_isHideMsg) { if (widget.index + 1 == widget.length) { _isHideMsg = false; Timer( const Duration(milliseconds: 3500), () { setState(() { _isHideMsg = true; }); }, ); } } } @override void dispose() { for (var element in _timerList) { element.cancel(); } super.dispose(); } @override Widget build(BuildContext context) { bool isFriend = widget.senderId == widget.contactId; return Container( padding: const EdgeInsets.symmetric( vertical: kDefaultPadding / 2, horizontal: kDefaultPadding, ), child: Column( children: [ // message date time if (widget.isShowTime) SizedBox( height: 35.0, child: Text( widget.dateTime, style: const TextStyle( color: kUnActivatedColor, ), ), ), Row( textDirection: isFriend ? TextDirection.ltr : TextDirection.rtl, crossAxisAlignment: CrossAxisAlignment.start, children: [ isFriend ? getIt .get() .friends[widget.contactId]! .avatar .isEmpty ? CircleAvatar( child: Image.asset('assets/images/user_2.png'), ) : CircleAvatar( backgroundImage: CachedNetworkImageProvider( '$userAvatarsUrl/${getIt.get().friends[widget.contactId]!.avatar}', ), ) : CircleAvatar( backgroundImage: CachedNetworkImageProvider( '$userAvatarsUrl/${getIt.get().avatar}', ), ), const SizedBox( width: 10, ), Expanded( child: Column( crossAxisAlignment: isFriend ? CrossAxisAlignment.start : CrossAxisAlignment.end, children: [ if (widget.type == 'text/multipart') // message box Container( padding: const EdgeInsets.fromLTRB(10, 10, 10, 0), decoration: BoxDecoration( color: isFriend ? const Color.fromARGB(255, 241, 241, 241) : kPrimaryColor, borderRadius: BorderRadius.circular(10.0), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (_isHideMsg) const Text('消息已隐藏'), // text message content if (widget.text.isNotEmpty && !_isHideMsg) Text( widget.text, textWidthBasis: TextWidthBasis.longestLine, ), const SizedBox( height: 10, ), // image content if have if (widget.attachments.isNotEmpty && !_isHideMsg) ...List.generate( widget.attachments.length, (int index) { int attachmentItemIndex = widget.attachmentItems.indexWhere( (element) => element.resource == widget.attachments[index], ); int heroTagId = widget .attachmentItems[attachmentItemIndex].id; return Container( margin: const EdgeInsets.only( bottom: 15, ), constraints: const BoxConstraints( maxHeight: 120, ), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10.0), ), child: _isImagesLoaded[index] ? GestureDetector( onTap: () { _onTapImage( context, widget.attachments[index], ); }, child: Hero( tag: heroTagId, child: Image.file( File( '${getIt.get().baseImageDir}/${widget.attachments[index]}', ), ), ), ) : Container( width: 20, height: 40, padding: const EdgeInsets.symmetric( vertical: 10, ), child: const CircularProgressIndicator( color: kSecondaryColor, strokeWidth: 3.0, ), ), ); }, ), ], ), ), ], ), ), ], ), if (_isHideMsg) TextButton( onPressed: () { setState(() { _isHideMsg = false; }); }, style: TextButton.styleFrom( padding: EdgeInsets.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), child: const Text('显示消息'), ), ], ), ); } void _onTapImage(BuildContext context, String attachment) { int attachmentIndex = widget.attachmentItems.indexWhere( (element) => element.resource == attachment, ); context.pushNamed( 'ImageView', extra: { 'attachmentItems': widget.attachmentItems, 'index': attachmentIndex, }, ); } }