flutter 微信聊天输入框
高仿微信聊天输入框,效果图如下(目前都是静态展示,服务还没开始开发):
大家如果观察仔细的话 应该会发现,他输入框下面的高度 刚好就是 软键盘的高度;所以在这里就需要监听软键盘的高度。还要配置
resizeToAvoidBottomInset: false, return Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar((因为CSDN 严格限制 gif动图大小(600kb左右) 无奈只能压缩 所以很糊)(第二张动图 是github上的不知道能放多久)
这里以 单聊为例:
遇到有个问题就是输入框行数的限制:这里这只 maxLines:null,就能自适应高度了。
就能做到 TextField多行输入了
child: TextField( // maxLength: maxLength, focusNode: focusNode, maxLines: null, maxLength: 200, cursorColor: AppColor.color3BAB71, controller: controller, textAlignVertical: TextAlignVertical.center, keyboardType: keyboardType, onEditingComplete: onEditingComplete, onSubmitted: onSubmitted, style: style ?? AppTextStyle.textStyle_28_333333, // inputFormatters: inputFormatters, decoration: InputDecoration( focusedBorder: const OutlineInputBorder( borderSide: BorderSide(width: 0, color: Colors.transparent)), disabledBorder: const OutlineInputBorder( borderSide: BorderSide(width: 0, color: Colors.transparent)), enabledBorder: const OutlineInputBorder( borderSide: BorderSide(width: 0, color: Colors.transparent)), border: OutlineInputBorder( borderSide: BorderSide.none, borderRadius: BorderRadius.circular(7.cale), //borderSide: BorderSide(width: 0, color: Colors.transparent), // borderSide: BorderSide(width: 0, color: Colors.transparent), ), hintText: hintText, prefixIcon: prefixIcon, prefixIconConstraints: prefixIconConstraints, hintStyle: hintStyle ?? AppTextStyle.textStyle_28_AAAAAA, counterText: '', //取消文字计数器 // border: InputBorder.none, isDense: true, errorText: errorText, contentPadding: EdgeInsets.symmetric( horizontal: 16.cale, vertical: 20.cale, ), ), // contentPadding: // EdgeInsets.only(left: 16.cale, right: 16.cale, top: 20.cale), // errorText: "输入错误", ),代码结构如下:
--- chatCommon
------ chat_bottom.dart 聊天底部输入框
------ chat_element_other.dart 聊天时别人信息的显示
------ chat_element_self.dart 聊天时自己信息的显示
------ chat_input_box.dart 聊天文本输入框封装
------ page_chat_group.dart 群聊
------ page_chat_person.dart 单聊
------ provider_chat_content.dart 聊天键盘显示 事件的传递 /键盘高度的处理
chat_bottom.dart import 'package:flutter/material.dart'; import 'package:imflutter/const/app_textStyle.dart'; import 'package:imflutter/pages/chatCommon/provider_chat_content.dart'; import 'package:imflutter/wrap/extension/extension.dart'; import '../../const/app_colors.dart'; import '../../const/app_icon.dart'; import '../../wrap/widget/app_widget.dart'; import 'chat_input_box.dart'; class ChatBottom extends StatefulWidget { final ProviderChatContent providerChatContent; const ChatBottom({Key? key, required this.providerChatContent}) : super(key: key); State<ChatBottom> createState() => _ChatBottomState(); } class _ChatBottomState extends State<ChatBottom> with WidgetsBindingObserver { // 0 语音 1 键盘 2 表情 int _inputType = 0; final TextEditingController _controller = TextEditingController(); final FocusNode _focusNode = FocusNode(); bool get _keyboardShow => widget.providerChatContent.contentShow; final List<Map> _listOption = [ {'title': '相册', 'icon': 'assets/common/chat/ic_details_photo.webp'}, {'title': '拍照', 'icon': 'assets/common/chat/ic_details_camera.webp'}, {'title': '视频通话', 'icon': 'assets/common/chat/ic_details_video.webp'}, {'title': '位置', 'icon': 'assets/common/chat/ic_details_localtion.webp'}, {'title': '红包', 'icon': 'assets/common/chat/ic_details_red.webp'}, {'title': '转账', 'icon': 'assets/common/chat/ic_details_transfer.webp'}, {'title': '语音输入', 'icon': 'assets/common/chat/ic_chat_voice.webp'}, {'title': '我的收藏', 'icon': 'assets/common/chat/ic_details_favorite.webp'}, ]; @override void initState() { // TODO: implement initState super.initState(); WidgetsBinding.instance.addObserver(this); _controller.addListener(() { setState(() {}); }); _focusNode.addListener(() { if (_focusNode.hasFocus) { widget.providerChatContent.updateContentShow(true); } }); } @override Widget build(BuildContext context) { print('ChatBottom------------------------build'); return Container( padding: EdgeInsets.symmetric(vertical: 20.cale), decoration: BoxDecoration( color: AppColor.colorF7F7F7, border: Border( top: BorderSide(width: 1.cale, color: AppColor.colordddddd), ), ), // height: 110.cale, child: Column( children: [ Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ AnimatedSwitcher( duration: const Duration(milliseconds: 20), transitionBuilder: (Widget child, Animation<double> animation) { return FadeTransition( opacity: animation, child: child, ); }, child: _inputType == 0 ? AppWidget.inkWellEffectNone( key: const ValueKey("AppIcon.audio"), onTap: () { print('启动音频'); _inputType = 1; widget.providerChatContent.updateContentShow(false); }, child: Padding( padding: EdgeInsets.only(left: 20.cale, bottom: 15.cale), child: Icon( AppIcon.audio, size: 50.cale, color: Colors.black, ), ), ) : AppWidget.inkWellEffectNone( key: const ValueKey("AppIcon.keyboard"), onTap: () { _inputType = 0; widget.providerChatContent.updateContentShow(true); _focusNode.requestFocus(); }, child: Padding( padding: EdgeInsets.only(left: 20.cale, bottom: 15.cale), child: Icon( AppIcon.keyboard, size: 50.cale, color: Colors.black, ), ), ), ), Expanded( child: _inputType == 0 ? Padding( padding: EdgeInsets.symmetric( horizontal: 20.cale, ), child: ChatInputBox( style: AppTextStyle.textStyle_30_000000, onEditingComplete: () { print("onEditingComplete"); }, onSubmitted: (str) { print("onSubmitted:$str"); }, controller: _controller, focusNode: _focusNode, ), ) : AppWidget.inkWellEffectNone( onTap: () {}, child: Container( margin: EdgeInsets.symmetric(horizontal: 20.cale), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(7.cale), ), height: 80.cale, child: Center( child: Text( '按住 说话', style: AppTextStyle.textStyle_30_000000, ), ), ), ), ), AppWidget.inkWellEffectNone( onTap: () { print('添加表情符号'); }, child: Padding( padding: EdgeInsets.only(bottom: 15.cale), child: Icon( AppIcon.faceHappy, size: 50.cale, color: Colors.black, ), ), ), AnimatedSwitcher( duration: const Duration(milliseconds: 50), transitionBuilder: (Widget child, Animation<double> animation) { return ScaleTransition( scale: animation, alignment: Alignment.centerRight, child: FadeTransition( opacity: animation, child: child, ), ); }, child: _inputType == 0 && _controller.value.text.isNotEmpty ? AppWidget.inkWellEffectNone( key: const ValueKey('发送'), onTap: () { print('发送'); _controller.clear(); }, child: Container( margin: EdgeInsets.only( left: 20.cale, right: 24.cale, bottom: 10.cale), padding: EdgeInsets.symmetric( horizontal: 24.cale, vertical: 10.cale, ), decoration: BoxDecoration( color: AppColor.color05C160, borderRadius: BorderRadius.circular(12.cale), ), child: Center( child: Text( '发送', style: AppTextStyle.textStyle_30_FFFFFF, )), ), ) : AppWidget.inkWellEffectNone( key: const ValueKey('AppIcon.add'), onTap: () { print('添加附件 图片视频'); setState(() { if (_focusNode.hasFocus) { _focusNode.unfocus(); } widget.providerChatContent.updateContentShow(true); // print( // '---------${DataInheritedWidget.of(context)?.dataEnvironment.keyboardHeight}'); //InheritedKeyboard.of(context)?.updateKeyboard(true); }); }, child: Padding( padding: EdgeInsets.only( left: 10.cale, right: 20.cale, bottom: 10.cale), child: Icon( AppIcon.add, size: 58.cale, color: Colors.black, ), ), ), ), ], ), if (_keyboardShow) Container( width: double.infinity, margin: EdgeInsets.only( top: 20.cale, ), // padding: EdgeInsets.only(bottom: 200.cale), decoration: BoxDecoration( border: Border( top: BorderSide(width: 1.cale, color: AppColor.colordddddd), ), ), height: widget.providerChatContent.keyboardHeight, child: Wrap( runAlignment: WrapAlignment.center, alignment: WrapAlignment.center, //crossAxisAlignment: WrapCrossAlignment.center, spacing: 75.cale, runSpacing: 60.cale, children: _listOption .asMap() .map( (key, value) => MapEntry( key, SizedBox( width: 100.cale, height: 150.cale, child: Column( children: [ Container( width: 100.cale, height: 100.cale, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(25.cale), ), child: Image.asset( value['icon'], width: 50.cale, height: 50.cale, ), ), Padding( padding: EdgeInsets.only(top: 16.cale), child: Text( value['title'], style: AppTextStyle.textStyle_20_656565, ), ) ], ), ), ), ) .values .toList(), ), ) ], ), ); } ///应用尺寸改变时回调,例如旋转 键盘 @override void didChangeMetrics() { // TODO: implement didChangeMetrics super.didChangeMetrics(); if (mounted) { // 键盘高度 final double viewInsetsBottom = EdgeInsets.fromWindowPadding( WidgetsBinding.instance.window.viewInsets, WidgetsBinding.instance.window.devicePixelRatio) .bottom; if (viewInsetsBottom > 0) { widget.providerChatContent.updateKeyboardHeight(viewInsetsBottom); } } } @override void dispose() { // TODO: implement dispose _focusNode.dispose(); _controller.dispose(); WidgetsBinding.instance.removeObserver(this); super.dispose(); } } chat_element_other.dart import 'package:flutter/material.dart'; import 'package:imflutter/const/app_colors.dart'; import 'package:imflutter/wrap/extension/extension.dart'; import 'package:imflutter/wrap/widget/app_widget.dart'; class ChatElementOther extends StatefulWidget { /// 用户信息 final Map userInfo; /// 消息 final Map chatMessage; const ChatElementOther( {Key? key, required this.userInfo, required this.chatMessage}) : super(key: key); @override State<ChatElementOther> createState() => _ChatElementOtherState(); } class _ChatElementOtherState extends State<ChatElementOther> { @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.only(top: 24.cale), child: Column( children: [ Padding( padding: EdgeInsets.only(bottom: 40.cale), child: Text('11:25'), ), Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.only(left: 24.cale), child: AppWidget.inkWellEffectNone( onTap: () {}, child: ClipRRect( borderRadius: BorderRadius.circular(7.cale), child: AppWidget.cachedImage(widget.userInfo['icon'], width: 75.cale, height: 75.cale), ), ), ), _chatContent(), ], ) ], ), ); } Widget _chatContent() { /// 1 文本 /// 2 图片 /// 3 语音 /// 4 视频 /// 5 提示消息 /// 6 提示消息 switch (widget.chatMessage['type']) { case 1: return _chatType1(); break; case 2: return _chatType2(); break; case 3: return _chatType3(); break; case 4: return _chatType4(); break; case 5: return _chatType5(); break; case 6: return _chatType6(); break; default: return Container(); break; } } Widget _chatType1() { return Stack( children: [ Container( margin: EdgeInsets.only(left: 25.cale), constraints: BoxConstraints(maxWidth: 500.cale), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12.cale), ), padding: EdgeInsets.symmetric( vertical: 18.cale, horizontal: 20.cale, ), child: Text( widget.chatMessage['content_1'], softWrap: true, ), ), Positioned( top: 25.cale, left: 10.cale, child: CustomPaint( size: Size(20.cale, 30.cale), painter: TrianglePainter(), ), ), ], ); } Widget _chatType2() { return Container( constraints: BoxConstraints( maxWidth: 320.cale, maxHeight: 300.cale, minHeight: 120.cale, ), decoration: BoxDecoration( borderRadius: BorderRadius.circular(7.cale), border: Border.all(width: 1.cale / 2, color: AppColor.color636363), ), margin: EdgeInsets.only(left: 20.cale), child: ClipRRect( borderRadius: BorderRadius.circular(7.cale), child: AppWidget.cachedImage( widget.chatMessage['content_2']['picture_mini']['url'], ), ), ); } Widget _chatType3() { return Container( color: Colors.white, padding: EdgeInsets.all(18.cale), child: Text("这是语音"), ); } Widget _chatType4() { return Container( color: Colors.white, padding: EdgeInsets.all(18.cale), child: Text("这是视频"), ); } Widget _chatType5() { return Container( color: Colors.white, padding: EdgeInsets.all(18.cale), child: Text("这是提示5"), ); } Widget _chatType6() { return Container( color: Colors.white, padding: EdgeInsets.all(18.cale), child: Text("这是提示6"), ); } } class TrianglePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { Paint paint = Paint()..color = Colors.white; Path path = Path(); path.moveTo(0, size.height / 2); path.lineTo(size.width, 0); path.lineTo(size.width, size.height); path.close(); canvas.drawPath(path, paint); return; } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { // TODO: implement shouldRepaint return false; } } chat_element_self.dart import 'package:flutter/material.dart'; import 'package:imflutter/const/app_colors.dart'; import 'package:imflutter/wrap/extension/extension.dart'; import 'package:imflutter/wrap/widget/app_widget.dart'; class ChatElementSelf extends StatefulWidget { /// 用户信息 final Map userInfo; /// 消息 final Map chatMessage; const ChatElementSelf( {Key? key, required this.userInfo, required this.chatMessage}) : super(key: key); @override State<ChatElementSelf> createState() => _ChatElementSelfState(); } class _ChatElementSelfState extends State<ChatElementSelf> { @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.only(top: 24.cale), child: Column( children: [ Padding( padding: EdgeInsets.only(bottom: 40.cale), child: Text('11:25'), ), Row( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.start, children: [ _chatContent(), Padding( padding: EdgeInsets.only(right: 24.cale), child: AppWidget.inkWellEffectNone( onTap: () {}, child: ClipRRect( borderRadius: BorderRadius.circular(7.cale), child: AppWidget.cachedImage(widget.userInfo['icon'], width: 75.cale, height: 75.cale), ), ), ), ], ) ], ), ); } Widget _chatContent() { /// 1 文本 /// 2 图片 /// 3 语音 /// 4 视频 /// 5 提示消息 /// 6 提示消息 switch (widget.chatMessage['type']) { case 1: return _chatType1(); break; case 2: return _chatType2(); break; case 3: return _chatType3(); break; case 4: return _chatType4(); break; case 5: return _chatType5(); break; case 6: return _chatType6(); break; default: return Container(); break; } } Widget _chatType1() { return Stack( children: [ Container( margin: EdgeInsets.only(right: 25.cale), constraints: BoxConstraints(maxWidth: 500.cale), decoration: BoxDecoration( color: AppColor.color94ED6D, borderRadius: BorderRadius.circular(12.cale), ), padding: EdgeInsets.symmetric( vertical: 18.cale, horizontal: 20.cale, ), child: Text( widget.chatMessage['content_1'], softWrap: true, ), ), Positioned( top: 25.cale, right: 10.cale, child: CustomPaint( size: Size(20.cale, 30.cale), painter: TrianglePainter(), ), ), ], ); } Widget _chatType2() { return Container( constraints: BoxConstraints( maxWidth: 320.cale, maxHeight: 300.cale, minHeight: 120.cale, ), decoration: BoxDecoration( borderRadius: BorderRadius.circular(7.cale), border: Border.all(width: 1.cale / 2, color: AppColor.color636363), ), margin: EdgeInsets.only(right: 20.cale), child: ClipRRect( borderRadius: BorderRadius.circular(7.cale), child: AppWidget.cachedImage( widget.chatMessage['content_2']['picture_mini']['url'], ), ), ); } Widget _chatType3() { return Container( color: Colors.white, padding: EdgeInsets.all(18.cale), child: Text("这是语音"), ); } Widget _chatType4() { return Container( color: Colors.white, padding: EdgeInsets.all(18.cale), child: Text("这是视频"), ); } Widget _chatType5() { return Container( color: Colors.white, padding: EdgeInsets.all(18.cale), child: Text("这是提示5"), ); } Widget _chatType6() { return Container( color: Colors.white, padding: EdgeInsets.all(18.cale), child: Text("这是提示6"), ); } } class TrianglePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { Paint paint = Paint()..color = AppColor.color94ED6D; Path path = Path(); // path.moveTo(0, 0); // path.lineTo(0, size.height); // path.lineTo(size.width, size.height); // path.lineTo(size.width, 0); path.moveTo(0, 0); path.lineTo(0, size.height); path.lineTo(size.width, size.height / 2); path.close(); canvas.drawPath(path, paint); return; } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { // TODO: implement shouldRepaint return false; } } chat_input_box.dart import 'package:flutter/material.dart'; import 'package:imflutter/const/app_colors.dart'; import 'package:imflutter/wrap/extension/extension.dart'; import '../../const/app_textStyle.dart'; class ChatInputBox extends StatelessWidget { final String? hintText; final int? maxLength; final VoidCallback? onEditingComplete; final ValueChanged<String>? onSubmitted; final EdgeInsetsGeometry? contentPadding; final TextEditingController? controller; final String? errorText; final Widget? prefixIcon; final TextInputType? keyboardType; final BoxConstraints? prefixIconConstraints; final BoxDecoration? decoration; final TextStyle? style; final TextStyle? hintStyle; final FocusNode? focusNode; const ChatInputBox({ Key? key, this.maxLength = 20, this.controller, this.errorText, this.prefixIcon, this.prefixIconConstraints, this.onEditingComplete, this.onSubmitted, this.contentPadding = EdgeInsets.zero, this.decoration, this.keyboardType, this.style, this.hintStyle, this.focusNode, this.hintText, }) : super(key: key); @override Widget build(BuildContext context) { return Container( // height: 75.cale, // margin: EdgeInsets.all(5.cale), constraints: BoxConstraints( minHeight: 75.cale, maxHeight: 350.cale, ), decoration: BoxDecoration( borderRadius: BorderRadius.circular(7.cale), color: Colors.white, ), child: TextField( // maxLength: maxLength, focusNode: focusNode, maxLines: null, maxLength: 200, cursorColor: AppColor.color3BAB71, controller: controller, textAlignVertical: TextAlignVertical.center, keyboardType: keyboardType, onEditingComplete: onEditingComplete, onSubmitted: onSubmitted, style: style ?? AppTextStyle.textStyle_28_333333, // inputFormatters: inputFormatters, decoration: InputDecoration( focusedBorder: const OutlineInputBorder( borderSide: BorderSide(width: 0, color: Colors.transparent)), disabledBorder: const OutlineInputBorder( borderSide: BorderSide(width: 0, color: Colors.transparent)), enabledBorder: const OutlineInputBorder( borderSide: BorderSide(width: 0, color: Colors.transparent)), border: OutlineInputBorder( borderSide: BorderSide.none, borderRadius: BorderRadius.circular(7.cale), //borderSide: BorderSide(width: 0, color: Colors.transparent), // borderSide: BorderSide(width: 0, color: Colors.transparent), ), hintText: hintText, prefixIcon: prefixIcon, prefixIconConstraints: prefixIconConstraints, hintStyle: hintStyle ?? AppTextStyle.textStyle_28_AAAAAA, counterText: '', //取消文字计数器 // border: InputBorder.none, isDense: true, errorText: errorText, contentPadding: EdgeInsets.symmetric( horizontal: 16.cale, vertical: 20.cale, ), ), // contentPadding: // EdgeInsets.only(left: 16.cale, right: 16.cale, top: 20.cale), // errorText: "输入错误", ), ); } } page_chat_person.dart import 'package:flutter/material.dart'; import 'package:imflutter/wrap/extension/extension.dart'; import 'package:imflutter/wrap/navigator/app_navigator.dart'; import 'package:imflutter/pages/chatCommon/chat_element_other.dart'; import 'package:provider/provider.dart'; import '../../const/app_colors.dart'; import '../../const/app_icon.dart'; import '../../const/app_textStyle.dart'; import '../../wrap/widget/app_widget.dart'; import 'provider_chat_content.dart'; import 'chat_bottom.dart'; import 'chat_element_self.dart'; class PageChatPerson extends StatefulWidget { final Map userInfoOther; const PageChatPerson({Key? key, required this.userInfoOther}) : super(key: key); @override State<PageChatPerson> createState() => _PageChatPersonState(); } class _PageChatPersonState extends State<PageChatPerson> { /// 1 文本 /// 2 图片 /// 3 语音 /// 4 视频 /// 5 提示消息 /// 6 提示消息 final List<Map> _arrayChatMessage = []; @override void initState() { // TODO: implement initState super.initState(); // for (int i = 13; i >= 0; i--) { // //_arrayChatMessage.addAll(_cache); // print("------------------i % 6 + 1:${i % 6 + 1}"); // _arrayChatMessage.add({ // 'id': i, // 'type': i % 6 + 1, // 'content_1': '你吃饭了吗2${i}-${i % 6}', // 'content_2': { // 'picture_mini': { // 'url': // 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', // 'width': 450, // 'height': 200 // }, // 'picture': // 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', // }, // 'content_3': // 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', // 'content_4': '', // 'content_5': '', // 'content_6': '', // 'times': 1000000 + i // }); // } _arrayChatMessage.add({ 'id': 99, 'type': 1, 'content_1': '你吃饭了吗? ', 'content_2': { 'picture_mini': { 'url': 'https://img2.baidu.com/it/u=3202947311,1179654885&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500', 'width': 800, 'height': 500 }, 'picture': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', }, 'content_3': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', 'content_4': '', 'content_5': '', 'content_6': '', 'times': 1000000 + 9 }); _arrayChatMessage.add({ 'id': 100, 'type': 1, 'content_1': '我吃过了你呢? ', 'content_2': { 'picture_mini': { 'url': 'https://img2.baidu.com/it/u=3202947311,1179654885&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500', 'width': 800, 'height': 500 }, 'picture': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', }, 'content_3': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', 'content_4': '', 'content_5': '', 'content_6': '', 'times': 1000000 + 9 }); _arrayChatMessage.add({ 'id': 100, 'type': 2, 'content_1': ' ', 'content_2': { 'picture_mini': { 'url': 'https://img2.baidu.com/it/u=3202947311,1179654885&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500', 'width': 800, 'height': 500 }, 'picture': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', }, 'content_3': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', 'content_4': '', 'content_5': '', 'content_6': '', 'times': 1000000 + 9 }); _arrayChatMessage.add({ 'id': 100, 'type': 2, 'content_1': ' ', 'content_2': { 'picture_mini': { 'url': 'https://lmg.jj20.com/up/allimg/1114/033021091503/210330091503-6-1200.jpg', 'width': 800, 'height': 500 }, 'picture': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', }, 'content_3': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', 'content_4': '', 'content_5': '', 'content_6': '', 'times': 1000000 + 9 }); _arrayChatMessage.add({ 'id': 100, 'type': 2, 'content_1': ' ', 'content_2': { 'picture_mini': { 'url': 'https://lmg.jj20.com/up/allimg/1114/033021091503/210330091503-6-1200.jpg', 'width': 800, 'height': 500 }, 'picture': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', }, 'content_3': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', 'content_4': '', 'content_5': '', 'content_6': '', 'times': 1000000 + 9 }); _arrayChatMessage.add({ 'id': 100, 'type': 2, 'content_1': ' ', 'content_2': { 'picture_mini': { 'url': 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202107%2F16%2F20210716215819_76234.thumb.1000_0.png&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1679722694&t=6ddea52a86e658f1a73f6e0e3865bad6', 'width': 800, 'height': 500 }, 'picture': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', }, 'content_3': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', 'content_4': '', 'content_5': '', 'content_6': '', 'times': 1000000 + 9 }); _arrayChatMessage.add({ 'id': 100, 'type': 2, 'content_1': ' ', 'content_2': { 'picture_mini': { 'url': 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202107%2F16%2F20210716215819_76234.thumb.1000_0.png&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1679722694&t=6ddea52a86e658f1a73f6e0e3865bad6', 'width': 800, 'height': 500 }, 'picture': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', }, 'content_3': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', 'content_4': '', 'content_5': '', 'content_6': '', 'times': 1000000 + 9 }); _arrayChatMessage.add({ 'id': 100, 'type': 2, 'content_1': ' ', 'content_2': { 'picture_mini': { 'url': 'https://photo.tuchong.com/4274381/f/1139873881.jpg', 'width': 800, 'height': 500 }, 'picture': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', }, 'content_3': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', 'content_4': '', 'content_5': '', 'content_6': '', 'times': 1000000 + 9 }); _arrayChatMessage.add({ 'id': 100, 'type': 2, 'content_1': ' ', 'content_2': { 'picture_mini': { 'url': 'https://photo.tuchong.com/4274381/f/11398738812.jpg', 'width': 800, 'height': 500 }, 'picture': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', }, 'content_3': 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg', 'content_4': '', 'content_5': '', 'content_6': '', 'times': 1000000 + 9 }); //print('--datum:${widget.userInfoOther}'); } @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( backgroundColor: AppColor.colorEDEDED, shadowColor: AppColor.colordddddd, elevation: 1.cale, leading: AppWidget.iconBack(() { AppNavigator().navigateBack(); }), centerTitle: true, title: Text( widget.userInfoOther['name'], style: AppTextStyle.textStyle_34_000000, ), actions: [ Padding( padding: EdgeInsets.only(right: 24.cale), child: AppWidget.inkWellEffectNone( onTap: () {}, child: Icon( AppIcon.dot3, size: 46.cale, color: Colors.black, ), ), ) ], ), body: ChangeNotifierProvider<ProviderChatContent>( create: (BuildContext context) => ProviderChatContent(), child: Builder( builder: (BuildContext context) { return Column( children: [ Expanded( child: AppWidget.inkWellEffectNone( onTap: () { FocusScope.of(context).requestFocus( FocusNode(), ); context .read<ProviderChatContent>() .updateContentShow(false); }, child: ListView.builder( padding: EdgeInsets.symmetric(vertical: 30.cale), physics: const BouncingScrollPhysics( parent: AlwaysScrollableScrollPhysics(), ), shrinkWrap: false, reverse: _arrayChatMessage.length > 7, itemCount: _arrayChatMessage.length, // itemExtent: 188.cale, itemBuilder: (BuildContext context, int index) { if (index % 2 != 0) { return ChatElementSelf( userInfo: widget.userInfoOther, chatMessage: _arrayChatMessage[index], ); } else { return ChatElementOther( userInfo: widget.userInfoOther, chatMessage: _arrayChatMessage[index]); } }, ), ), ), Consumer(builder: (BuildContext context, ProviderChatContent providerChatContent, child) { return ChatBottom( providerChatContent: providerChatContent, ); }), ], ); }, ), ), ); } @override void dispose() { super.dispose(); } } provider_chat_content.dart import 'package:flutter/cupertino.dart'; import 'package:imflutter/wrap/extension/extension.dart'; ///用于 软键盘区/发送附件 域显示控制 class ProviderChatContent extends ChangeNotifier { bool _contentShow = false; double _keyboardHeight = 200; /// 是否显示 附件区域 bool get contentShow => _contentShow; /// 键盘高度 double get keyboardHeight => _keyboardHeight - 20.cale; ///更新区域 展示 void updateContentShow(bool isShow) { _contentShow = isShow; notifyListeners(); } void updateKeyboardHeight(double height) { _keyboardHeight = height; notifyListeners(); } }