0


flutter 微信聊天输入框

高仿微信聊天输入框,效果图如下(目前都是静态展示,服务还没开始开发):

大家如果观察仔细的话 应该会发现,他输入框下面的高度 刚好就是 软键盘的高度;所以在这里就需要监听软键盘的高度。还要配置

  1. resizeToAvoidBottomInset: false,
  1. return Scaffold(
  2. resizeToAvoidBottomInset: false,
  3. appBar: AppBar(

因为CSDN 严格限制 gif动图大小(600kb左右) 无奈只能压缩 所以很糊)(第二张动图 是github上的不知道能放多久)


** 这里以 单聊为例:**

  1. 遇到有个问题就是**输入框行数的限制**:这里这只 maxLinesnull,就能自适应高度了。

就能做到 TextField多行输入了

  1. child: TextField(
  2. // maxLength: maxLength,
  3. focusNode: focusNode,
  4. maxLines: null,
  5. maxLength: 200,
  6. cursorColor: AppColor.color3BAB71,
  7. controller: controller,
  8. textAlignVertical: TextAlignVertical.center,
  9. keyboardType: keyboardType,
  10. onEditingComplete: onEditingComplete,
  11. onSubmitted: onSubmitted,
  12. style: style ?? AppTextStyle.textStyle_28_333333,
  13. // inputFormatters: inputFormatters,
  14. decoration: InputDecoration(
  15. focusedBorder: const OutlineInputBorder(
  16. borderSide: BorderSide(width: 0, color: Colors.transparent)),
  17. disabledBorder: const OutlineInputBorder(
  18. borderSide: BorderSide(width: 0, color: Colors.transparent)),
  19. enabledBorder: const OutlineInputBorder(
  20. borderSide: BorderSide(width: 0, color: Colors.transparent)),
  21. border: OutlineInputBorder(
  22. borderSide: BorderSide.none,
  23. borderRadius: BorderRadius.circular(7.cale),
  24. //borderSide: BorderSide(width: 0, color: Colors.transparent),
  25. // borderSide: BorderSide(width: 0, color: Colors.transparent),
  26. ),
  27. hintText: hintText,
  28. prefixIcon: prefixIcon,
  29. prefixIconConstraints: prefixIconConstraints,
  30. hintStyle: hintStyle ?? AppTextStyle.textStyle_28_AAAAAA,
  31. counterText: '', //取消文字计数器
  32. // border: InputBorder.none,
  33. isDense: true,
  34. errorText: errorText,
  35. contentPadding: EdgeInsets.symmetric(
  36. horizontal: 16.cale,
  37. vertical: 20.cale,
  38. ),
  39. ),
  40. // contentPadding:
  41. // EdgeInsets.only(left: 16.cale, right: 16.cale, top: 20.cale),
  42. // errorText: "输入错误",
  43. ),

代码结构如下:

--- chatCommon

  1. ------ chat_bottom.dart 聊天底部输入框
  2. ------ chat_element_other.dart 聊天时别人信息的显示
  3. ------ chat_element_self.dart 聊天时自己信息的显示
  4. ------ chat_input_box.dart 聊天文本输入框封装
  5. ------ page_chat_group.dart 群聊
  6. ------ page_chat_person.dart 单聊
  7. ------ provider_chat_content.dart 聊天键盘显示 事件的传递 /键盘高度的处理

chat_bottom.dart

  1. import 'package:flutter/material.dart';
  2. import 'package:imflutter/const/app_textStyle.dart';
  3. import 'package:imflutter/pages/chatCommon/provider_chat_content.dart';
  4. import 'package:imflutter/wrap/extension/extension.dart';
  5. import '../../const/app_colors.dart';
  6. import '../../const/app_icon.dart';
  7. import '../../wrap/widget/app_widget.dart';
  8. import 'chat_input_box.dart';
  9. class ChatBottom extends StatefulWidget {
  10. final ProviderChatContent providerChatContent;
  11. const ChatBottom({Key? key, required this.providerChatContent})
  12. : super(key: key);
  13. State<ChatBottom> createState() => _ChatBottomState();
  14. }
  15. class _ChatBottomState extends State<ChatBottom> with WidgetsBindingObserver {
  16. // 0 语音 1 键盘 2 表情
  17. int _inputType = 0;
  18. final TextEditingController _controller = TextEditingController();
  19. final FocusNode _focusNode = FocusNode();
  20. bool get _keyboardShow => widget.providerChatContent.contentShow;
  21. final List<Map> _listOption = [
  22. {'title': '相册', 'icon': 'assets/common/chat/ic_details_photo.webp'},
  23. {'title': '拍照', 'icon': 'assets/common/chat/ic_details_camera.webp'},
  24. {'title': '视频通话', 'icon': 'assets/common/chat/ic_details_video.webp'},
  25. {'title': '位置', 'icon': 'assets/common/chat/ic_details_localtion.webp'},
  26. {'title': '红包', 'icon': 'assets/common/chat/ic_details_red.webp'},
  27. {'title': '转账', 'icon': 'assets/common/chat/ic_details_transfer.webp'},
  28. {'title': '语音输入', 'icon': 'assets/common/chat/ic_chat_voice.webp'},
  29. {'title': '我的收藏', 'icon': 'assets/common/chat/ic_details_favorite.webp'},
  30. ];
  31. @override
  32. void initState() {
  33. // TODO: implement initState
  34. super.initState();
  35. WidgetsBinding.instance.addObserver(this);
  36. _controller.addListener(() {
  37. setState(() {});
  38. });
  39. _focusNode.addListener(() {
  40. if (_focusNode.hasFocus) {
  41. widget.providerChatContent.updateContentShow(true);
  42. }
  43. });
  44. }
  45. @override
  46. Widget build(BuildContext context) {
  47. print('ChatBottom------------------------build');
  48. return Container(
  49. padding: EdgeInsets.symmetric(vertical: 20.cale),
  50. decoration: BoxDecoration(
  51. color: AppColor.colorF7F7F7,
  52. border: Border(
  53. top: BorderSide(width: 1.cale, color: AppColor.colordddddd),
  54. ),
  55. ),
  56. // height: 110.cale,
  57. child: Column(
  58. children: [
  59. Row(
  60. crossAxisAlignment: CrossAxisAlignment.end,
  61. children: [
  62. AnimatedSwitcher(
  63. duration: const Duration(milliseconds: 20),
  64. transitionBuilder: (Widget child, Animation<double> animation) {
  65. return FadeTransition(
  66. opacity: animation,
  67. child: child,
  68. );
  69. },
  70. child: _inputType == 0
  71. ? AppWidget.inkWellEffectNone(
  72. key: const ValueKey("AppIcon.audio"),
  73. onTap: () {
  74. print('启动音频');
  75. _inputType = 1;
  76. widget.providerChatContent.updateContentShow(false);
  77. },
  78. child: Padding(
  79. padding:
  80. EdgeInsets.only(left: 20.cale, bottom: 15.cale),
  81. child: Icon(
  82. AppIcon.audio,
  83. size: 50.cale,
  84. color: Colors.black,
  85. ),
  86. ),
  87. )
  88. : AppWidget.inkWellEffectNone(
  89. key: const ValueKey("AppIcon.keyboard"),
  90. onTap: () {
  91. _inputType = 0;
  92. widget.providerChatContent.updateContentShow(true);
  93. _focusNode.requestFocus();
  94. },
  95. child: Padding(
  96. padding:
  97. EdgeInsets.only(left: 20.cale, bottom: 15.cale),
  98. child: Icon(
  99. AppIcon.keyboard,
  100. size: 50.cale,
  101. color: Colors.black,
  102. ),
  103. ),
  104. ),
  105. ),
  106. Expanded(
  107. child: _inputType == 0
  108. ? Padding(
  109. padding: EdgeInsets.symmetric(
  110. horizontal: 20.cale,
  111. ),
  112. child: ChatInputBox(
  113. style: AppTextStyle.textStyle_30_000000,
  114. onEditingComplete: () {
  115. print("onEditingComplete");
  116. },
  117. onSubmitted: (str) {
  118. print("onSubmitted:$str");
  119. },
  120. controller: _controller,
  121. focusNode: _focusNode,
  122. ),
  123. )
  124. : AppWidget.inkWellEffectNone(
  125. onTap: () {},
  126. child: Container(
  127. margin: EdgeInsets.symmetric(horizontal: 20.cale),
  128. decoration: BoxDecoration(
  129. color: Colors.white,
  130. borderRadius: BorderRadius.circular(7.cale),
  131. ),
  132. height: 80.cale,
  133. child: Center(
  134. child: Text(
  135. '按住 说话',
  136. style: AppTextStyle.textStyle_30_000000,
  137. ),
  138. ),
  139. ),
  140. ),
  141. ),
  142. AppWidget.inkWellEffectNone(
  143. onTap: () {
  144. print('添加表情符号');
  145. },
  146. child: Padding(
  147. padding: EdgeInsets.only(bottom: 15.cale),
  148. child: Icon(
  149. AppIcon.faceHappy,
  150. size: 50.cale,
  151. color: Colors.black,
  152. ),
  153. ),
  154. ),
  155. AnimatedSwitcher(
  156. duration: const Duration(milliseconds: 50),
  157. transitionBuilder: (Widget child, Animation<double> animation) {
  158. return ScaleTransition(
  159. scale: animation,
  160. alignment: Alignment.centerRight,
  161. child: FadeTransition(
  162. opacity: animation,
  163. child: child,
  164. ),
  165. );
  166. },
  167. child: _inputType == 0 && _controller.value.text.isNotEmpty
  168. ? AppWidget.inkWellEffectNone(
  169. key: const ValueKey('发送'),
  170. onTap: () {
  171. print('发送');
  172. _controller.clear();
  173. },
  174. child: Container(
  175. margin: EdgeInsets.only(
  176. left: 20.cale, right: 24.cale, bottom: 10.cale),
  177. padding: EdgeInsets.symmetric(
  178. horizontal: 24.cale,
  179. vertical: 10.cale,
  180. ),
  181. decoration: BoxDecoration(
  182. color: AppColor.color05C160,
  183. borderRadius: BorderRadius.circular(12.cale),
  184. ),
  185. child: Center(
  186. child: Text(
  187. '发送',
  188. style: AppTextStyle.textStyle_30_FFFFFF,
  189. )),
  190. ),
  191. )
  192. : AppWidget.inkWellEffectNone(
  193. key: const ValueKey('AppIcon.add'),
  194. onTap: () {
  195. print('添加附件 图片视频');
  196. setState(() {
  197. if (_focusNode.hasFocus) {
  198. _focusNode.unfocus();
  199. }
  200. widget.providerChatContent.updateContentShow(true);
  201. // print(
  202. // '---------${DataInheritedWidget.of(context)?.dataEnvironment.keyboardHeight}');
  203. //InheritedKeyboard.of(context)?.updateKeyboard(true);
  204. });
  205. },
  206. child: Padding(
  207. padding: EdgeInsets.only(
  208. left: 10.cale, right: 20.cale, bottom: 10.cale),
  209. child: Icon(
  210. AppIcon.add,
  211. size: 58.cale,
  212. color: Colors.black,
  213. ),
  214. ),
  215. ),
  216. ),
  217. ],
  218. ),
  219. if (_keyboardShow)
  220. Container(
  221. width: double.infinity,
  222. margin: EdgeInsets.only(
  223. top: 20.cale,
  224. ),
  225. // padding: EdgeInsets.only(bottom: 200.cale),
  226. decoration: BoxDecoration(
  227. border: Border(
  228. top: BorderSide(width: 1.cale, color: AppColor.colordddddd),
  229. ),
  230. ),
  231. height: widget.providerChatContent.keyboardHeight,
  232. child: Wrap(
  233. runAlignment: WrapAlignment.center,
  234. alignment: WrapAlignment.center,
  235. //crossAxisAlignment: WrapCrossAlignment.center,
  236. spacing: 75.cale,
  237. runSpacing: 60.cale,
  238. children: _listOption
  239. .asMap()
  240. .map(
  241. (key, value) => MapEntry(
  242. key,
  243. SizedBox(
  244. width: 100.cale,
  245. height: 150.cale,
  246. child: Column(
  247. children: [
  248. Container(
  249. width: 100.cale,
  250. height: 100.cale,
  251. decoration: BoxDecoration(
  252. color: Colors.white,
  253. borderRadius: BorderRadius.circular(25.cale),
  254. ),
  255. child: Image.asset(
  256. value['icon'],
  257. width: 50.cale,
  258. height: 50.cale,
  259. ),
  260. ),
  261. Padding(
  262. padding: EdgeInsets.only(top: 16.cale),
  263. child: Text(
  264. value['title'],
  265. style: AppTextStyle.textStyle_20_656565,
  266. ),
  267. )
  268. ],
  269. ),
  270. ),
  271. ),
  272. )
  273. .values
  274. .toList(),
  275. ),
  276. )
  277. ],
  278. ),
  279. );
  280. }
  281. ///应用尺寸改变时回调,例如旋转 键盘
  282. @override
  283. void didChangeMetrics() {
  284. // TODO: implement didChangeMetrics
  285. super.didChangeMetrics();
  286. if (mounted) {
  287. // 键盘高度
  288. final double viewInsetsBottom = EdgeInsets.fromWindowPadding(
  289. WidgetsBinding.instance.window.viewInsets,
  290. WidgetsBinding.instance.window.devicePixelRatio)
  291. .bottom;
  292. if (viewInsetsBottom > 0) {
  293. widget.providerChatContent.updateKeyboardHeight(viewInsetsBottom);
  294. }
  295. }
  296. }
  297. @override
  298. void dispose() {
  299. // TODO: implement dispose
  300. _focusNode.dispose();
  301. _controller.dispose();
  302. WidgetsBinding.instance.removeObserver(this);
  303. super.dispose();
  304. }
  305. }

chat_element_other.dart

  1. import 'package:flutter/material.dart';
  2. import 'package:imflutter/const/app_colors.dart';
  3. import 'package:imflutter/wrap/extension/extension.dart';
  4. import 'package:imflutter/wrap/widget/app_widget.dart';
  5. class ChatElementOther extends StatefulWidget {
  6. /// 用户信息
  7. final Map userInfo;
  8. /// 消息
  9. final Map chatMessage;
  10. const ChatElementOther(
  11. {Key? key, required this.userInfo, required this.chatMessage})
  12. : super(key: key);
  13. @override
  14. State<ChatElementOther> createState() => _ChatElementOtherState();
  15. }
  16. class _ChatElementOtherState extends State<ChatElementOther> {
  17. @override
  18. Widget build(BuildContext context) {
  19. return Container(
  20. padding: EdgeInsets.only(top: 24.cale),
  21. child: Column(
  22. children: [
  23. Padding(
  24. padding: EdgeInsets.only(bottom: 40.cale),
  25. child: Text('11:25'),
  26. ),
  27. Row(
  28. mainAxisAlignment: MainAxisAlignment.start,
  29. crossAxisAlignment: CrossAxisAlignment.start,
  30. children: [
  31. Padding(
  32. padding: EdgeInsets.only(left: 24.cale),
  33. child: AppWidget.inkWellEffectNone(
  34. onTap: () {},
  35. child: ClipRRect(
  36. borderRadius: BorderRadius.circular(7.cale),
  37. child: AppWidget.cachedImage(widget.userInfo['icon'],
  38. width: 75.cale, height: 75.cale),
  39. ),
  40. ),
  41. ),
  42. _chatContent(),
  43. ],
  44. )
  45. ],
  46. ),
  47. );
  48. }
  49. Widget _chatContent() {
  50. /// 1 文本
  51. /// 2 图片
  52. /// 3 语音
  53. /// 4 视频
  54. /// 5 提示消息
  55. /// 6 提示消息
  56. switch (widget.chatMessage['type']) {
  57. case 1:
  58. return _chatType1();
  59. break;
  60. case 2:
  61. return _chatType2();
  62. break;
  63. case 3:
  64. return _chatType3();
  65. break;
  66. case 4:
  67. return _chatType4();
  68. break;
  69. case 5:
  70. return _chatType5();
  71. break;
  72. case 6:
  73. return _chatType6();
  74. break;
  75. default:
  76. return Container();
  77. break;
  78. }
  79. }
  80. Widget _chatType1() {
  81. return Stack(
  82. children: [
  83. Container(
  84. margin: EdgeInsets.only(left: 25.cale),
  85. constraints: BoxConstraints(maxWidth: 500.cale),
  86. decoration: BoxDecoration(
  87. color: Colors.white,
  88. borderRadius: BorderRadius.circular(12.cale),
  89. ),
  90. padding: EdgeInsets.symmetric(
  91. vertical: 18.cale,
  92. horizontal: 20.cale,
  93. ),
  94. child: Text(
  95. widget.chatMessage['content_1'],
  96. softWrap: true,
  97. ),
  98. ),
  99. Positioned(
  100. top: 25.cale,
  101. left: 10.cale,
  102. child: CustomPaint(
  103. size: Size(20.cale, 30.cale),
  104. painter: TrianglePainter(),
  105. ),
  106. ),
  107. ],
  108. );
  109. }
  110. Widget _chatType2() {
  111. return Container(
  112. constraints: BoxConstraints(
  113. maxWidth: 320.cale,
  114. maxHeight: 300.cale,
  115. minHeight: 120.cale,
  116. ),
  117. decoration: BoxDecoration(
  118. borderRadius: BorderRadius.circular(7.cale),
  119. border: Border.all(width: 1.cale / 2, color: AppColor.color636363),
  120. ),
  121. margin: EdgeInsets.only(left: 20.cale),
  122. child: ClipRRect(
  123. borderRadius: BorderRadius.circular(7.cale),
  124. child: AppWidget.cachedImage(
  125. widget.chatMessage['content_2']['picture_mini']['url'],
  126. ),
  127. ),
  128. );
  129. }
  130. Widget _chatType3() {
  131. return Container(
  132. color: Colors.white,
  133. padding: EdgeInsets.all(18.cale),
  134. child: Text("这是语音"),
  135. );
  136. }
  137. Widget _chatType4() {
  138. return Container(
  139. color: Colors.white,
  140. padding: EdgeInsets.all(18.cale),
  141. child: Text("这是视频"),
  142. );
  143. }
  144. Widget _chatType5() {
  145. return Container(
  146. color: Colors.white,
  147. padding: EdgeInsets.all(18.cale),
  148. child: Text("这是提示5"),
  149. );
  150. }
  151. Widget _chatType6() {
  152. return Container(
  153. color: Colors.white,
  154. padding: EdgeInsets.all(18.cale),
  155. child: Text("这是提示6"),
  156. );
  157. }
  158. }
  159. class TrianglePainter extends CustomPainter {
  160. @override
  161. void paint(Canvas canvas, Size size) {
  162. Paint paint = Paint()..color = Colors.white;
  163. Path path = Path();
  164. path.moveTo(0, size.height / 2);
  165. path.lineTo(size.width, 0);
  166. path.lineTo(size.width, size.height);
  167. path.close();
  168. canvas.drawPath(path, paint);
  169. return;
  170. }
  171. @override
  172. bool shouldRepaint(covariant CustomPainter oldDelegate) {
  173. // TODO: implement shouldRepaint
  174. return false;
  175. }
  176. }

chat_element_self.dart

  1. import 'package:flutter/material.dart';
  2. import 'package:imflutter/const/app_colors.dart';
  3. import 'package:imflutter/wrap/extension/extension.dart';
  4. import 'package:imflutter/wrap/widget/app_widget.dart';
  5. class ChatElementSelf extends StatefulWidget {
  6. /// 用户信息
  7. final Map userInfo;
  8. /// 消息
  9. final Map chatMessage;
  10. const ChatElementSelf(
  11. {Key? key, required this.userInfo, required this.chatMessage})
  12. : super(key: key);
  13. @override
  14. State<ChatElementSelf> createState() => _ChatElementSelfState();
  15. }
  16. class _ChatElementSelfState extends State<ChatElementSelf> {
  17. @override
  18. Widget build(BuildContext context) {
  19. return Container(
  20. padding: EdgeInsets.only(top: 24.cale),
  21. child: Column(
  22. children: [
  23. Padding(
  24. padding: EdgeInsets.only(bottom: 40.cale),
  25. child: Text('11:25'),
  26. ),
  27. Row(
  28. mainAxisAlignment: MainAxisAlignment.end,
  29. crossAxisAlignment: CrossAxisAlignment.start,
  30. children: [
  31. _chatContent(),
  32. Padding(
  33. padding: EdgeInsets.only(right: 24.cale),
  34. child: AppWidget.inkWellEffectNone(
  35. onTap: () {},
  36. child: ClipRRect(
  37. borderRadius: BorderRadius.circular(7.cale),
  38. child: AppWidget.cachedImage(widget.userInfo['icon'],
  39. width: 75.cale, height: 75.cale),
  40. ),
  41. ),
  42. ),
  43. ],
  44. )
  45. ],
  46. ),
  47. );
  48. }
  49. Widget _chatContent() {
  50. /// 1 文本
  51. /// 2 图片
  52. /// 3 语音
  53. /// 4 视频
  54. /// 5 提示消息
  55. /// 6 提示消息
  56. switch (widget.chatMessage['type']) {
  57. case 1:
  58. return _chatType1();
  59. break;
  60. case 2:
  61. return _chatType2();
  62. break;
  63. case 3:
  64. return _chatType3();
  65. break;
  66. case 4:
  67. return _chatType4();
  68. break;
  69. case 5:
  70. return _chatType5();
  71. break;
  72. case 6:
  73. return _chatType6();
  74. break;
  75. default:
  76. return Container();
  77. break;
  78. }
  79. }
  80. Widget _chatType1() {
  81. return Stack(
  82. children: [
  83. Container(
  84. margin: EdgeInsets.only(right: 25.cale),
  85. constraints: BoxConstraints(maxWidth: 500.cale),
  86. decoration: BoxDecoration(
  87. color: AppColor.color94ED6D,
  88. borderRadius: BorderRadius.circular(12.cale),
  89. ),
  90. padding: EdgeInsets.symmetric(
  91. vertical: 18.cale,
  92. horizontal: 20.cale,
  93. ),
  94. child: Text(
  95. widget.chatMessage['content_1'],
  96. softWrap: true,
  97. ),
  98. ),
  99. Positioned(
  100. top: 25.cale,
  101. right: 10.cale,
  102. child: CustomPaint(
  103. size: Size(20.cale, 30.cale),
  104. painter: TrianglePainter(),
  105. ),
  106. ),
  107. ],
  108. );
  109. }
  110. Widget _chatType2() {
  111. return Container(
  112. constraints: BoxConstraints(
  113. maxWidth: 320.cale,
  114. maxHeight: 300.cale,
  115. minHeight: 120.cale,
  116. ),
  117. decoration: BoxDecoration(
  118. borderRadius: BorderRadius.circular(7.cale),
  119. border: Border.all(width: 1.cale / 2, color: AppColor.color636363),
  120. ),
  121. margin: EdgeInsets.only(right: 20.cale),
  122. child: ClipRRect(
  123. borderRadius: BorderRadius.circular(7.cale),
  124. child: AppWidget.cachedImage(
  125. widget.chatMessage['content_2']['picture_mini']['url'],
  126. ),
  127. ),
  128. );
  129. }
  130. Widget _chatType3() {
  131. return Container(
  132. color: Colors.white,
  133. padding: EdgeInsets.all(18.cale),
  134. child: Text("这是语音"),
  135. );
  136. }
  137. Widget _chatType4() {
  138. return Container(
  139. color: Colors.white,
  140. padding: EdgeInsets.all(18.cale),
  141. child: Text("这是视频"),
  142. );
  143. }
  144. Widget _chatType5() {
  145. return Container(
  146. color: Colors.white,
  147. padding: EdgeInsets.all(18.cale),
  148. child: Text("这是提示5"),
  149. );
  150. }
  151. Widget _chatType6() {
  152. return Container(
  153. color: Colors.white,
  154. padding: EdgeInsets.all(18.cale),
  155. child: Text("这是提示6"),
  156. );
  157. }
  158. }
  159. class TrianglePainter extends CustomPainter {
  160. @override
  161. void paint(Canvas canvas, Size size) {
  162. Paint paint = Paint()..color = AppColor.color94ED6D;
  163. Path path = Path();
  164. // path.moveTo(0, 0);
  165. // path.lineTo(0, size.height);
  166. // path.lineTo(size.width, size.height);
  167. // path.lineTo(size.width, 0);
  168. path.moveTo(0, 0);
  169. path.lineTo(0, size.height);
  170. path.lineTo(size.width, size.height / 2);
  171. path.close();
  172. canvas.drawPath(path, paint);
  173. return;
  174. }
  175. @override
  176. bool shouldRepaint(covariant CustomPainter oldDelegate) {
  177. // TODO: implement shouldRepaint
  178. return false;
  179. }
  180. }

chat_input_box.dart

  1. import 'package:flutter/material.dart';
  2. import 'package:imflutter/const/app_colors.dart';
  3. import 'package:imflutter/wrap/extension/extension.dart';
  4. import '../../const/app_textStyle.dart';
  5. class ChatInputBox extends StatelessWidget {
  6. final String? hintText;
  7. final int? maxLength;
  8. final VoidCallback? onEditingComplete;
  9. final ValueChanged<String>? onSubmitted;
  10. final EdgeInsetsGeometry? contentPadding;
  11. final TextEditingController? controller;
  12. final String? errorText;
  13. final Widget? prefixIcon;
  14. final TextInputType? keyboardType;
  15. final BoxConstraints? prefixIconConstraints;
  16. final BoxDecoration? decoration;
  17. final TextStyle? style;
  18. final TextStyle? hintStyle;
  19. final FocusNode? focusNode;
  20. const ChatInputBox({
  21. Key? key,
  22. this.maxLength = 20,
  23. this.controller,
  24. this.errorText,
  25. this.prefixIcon,
  26. this.prefixIconConstraints,
  27. this.onEditingComplete,
  28. this.onSubmitted,
  29. this.contentPadding = EdgeInsets.zero,
  30. this.decoration,
  31. this.keyboardType,
  32. this.style,
  33. this.hintStyle,
  34. this.focusNode,
  35. this.hintText,
  36. }) : super(key: key);
  37. @override
  38. Widget build(BuildContext context) {
  39. return Container(
  40. // height: 75.cale,
  41. // margin: EdgeInsets.all(5.cale),
  42. constraints: BoxConstraints(
  43. minHeight: 75.cale,
  44. maxHeight: 350.cale,
  45. ),
  46. decoration: BoxDecoration(
  47. borderRadius: BorderRadius.circular(7.cale),
  48. color: Colors.white,
  49. ),
  50. child: TextField(
  51. // maxLength: maxLength,
  52. focusNode: focusNode,
  53. maxLines: null,
  54. maxLength: 200,
  55. cursorColor: AppColor.color3BAB71,
  56. controller: controller,
  57. textAlignVertical: TextAlignVertical.center,
  58. keyboardType: keyboardType,
  59. onEditingComplete: onEditingComplete,
  60. onSubmitted: onSubmitted,
  61. style: style ?? AppTextStyle.textStyle_28_333333,
  62. // inputFormatters: inputFormatters,
  63. decoration: InputDecoration(
  64. focusedBorder: const OutlineInputBorder(
  65. borderSide: BorderSide(width: 0, color: Colors.transparent)),
  66. disabledBorder: const OutlineInputBorder(
  67. borderSide: BorderSide(width: 0, color: Colors.transparent)),
  68. enabledBorder: const OutlineInputBorder(
  69. borderSide: BorderSide(width: 0, color: Colors.transparent)),
  70. border: OutlineInputBorder(
  71. borderSide: BorderSide.none,
  72. borderRadius: BorderRadius.circular(7.cale),
  73. //borderSide: BorderSide(width: 0, color: Colors.transparent),
  74. // borderSide: BorderSide(width: 0, color: Colors.transparent),
  75. ),
  76. hintText: hintText,
  77. prefixIcon: prefixIcon,
  78. prefixIconConstraints: prefixIconConstraints,
  79. hintStyle: hintStyle ?? AppTextStyle.textStyle_28_AAAAAA,
  80. counterText: '', //取消文字计数器
  81. // border: InputBorder.none,
  82. isDense: true,
  83. errorText: errorText,
  84. contentPadding: EdgeInsets.symmetric(
  85. horizontal: 16.cale,
  86. vertical: 20.cale,
  87. ),
  88. ),
  89. // contentPadding:
  90. // EdgeInsets.only(left: 16.cale, right: 16.cale, top: 20.cale),
  91. // errorText: "输入错误",
  92. ),
  93. );
  94. }
  95. }

page_chat_person.dart

  1. import 'package:flutter/material.dart';
  2. import 'package:imflutter/wrap/extension/extension.dart';
  3. import 'package:imflutter/wrap/navigator/app_navigator.dart';
  4. import 'package:imflutter/pages/chatCommon/chat_element_other.dart';
  5. import 'package:provider/provider.dart';
  6. import '../../const/app_colors.dart';
  7. import '../../const/app_icon.dart';
  8. import '../../const/app_textStyle.dart';
  9. import '../../wrap/widget/app_widget.dart';
  10. import 'provider_chat_content.dart';
  11. import 'chat_bottom.dart';
  12. import 'chat_element_self.dart';
  13. class PageChatPerson extends StatefulWidget {
  14. final Map userInfoOther;
  15. const PageChatPerson({Key? key, required this.userInfoOther})
  16. : super(key: key);
  17. @override
  18. State<PageChatPerson> createState() => _PageChatPersonState();
  19. }
  20. class _PageChatPersonState extends State<PageChatPerson> {
  21. /// 1 文本
  22. /// 2 图片
  23. /// 3 语音
  24. /// 4 视频
  25. /// 5 提示消息
  26. /// 6 提示消息
  27. final List<Map> _arrayChatMessage = [];
  28. @override
  29. void initState() {
  30. // TODO: implement initState
  31. super.initState();
  32. // for (int i = 13; i >= 0; i--) {
  33. // //_arrayChatMessage.addAll(_cache);
  34. // print("------------------i % 6 + 1:${i % 6 + 1}");
  35. // _arrayChatMessage.add({
  36. // 'id': i,
  37. // 'type': i % 6 + 1,
  38. // 'content_1': '你吃饭了吗2${i}-${i % 6}',
  39. // 'content_2': {
  40. // 'picture_mini': {
  41. // 'url':
  42. // 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  43. // 'width': 450,
  44. // 'height': 200
  45. // },
  46. // 'picture':
  47. // 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  48. // },
  49. // 'content_3':
  50. // 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  51. // 'content_4': '',
  52. // 'content_5': '',
  53. // 'content_6': '',
  54. // 'times': 1000000 + i
  55. // });
  56. // }
  57. _arrayChatMessage.add({
  58. 'id': 99,
  59. 'type': 1,
  60. 'content_1': '你吃饭了吗? ',
  61. 'content_2': {
  62. 'picture_mini': {
  63. 'url':
  64. 'https://img2.baidu.com/it/u=3202947311,1179654885&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500',
  65. 'width': 800,
  66. 'height': 500
  67. },
  68. 'picture':
  69. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  70. },
  71. 'content_3':
  72. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  73. 'content_4': '',
  74. 'content_5': '',
  75. 'content_6': '',
  76. 'times': 1000000 + 9
  77. });
  78. _arrayChatMessage.add({
  79. 'id': 100,
  80. 'type': 1,
  81. 'content_1': '我吃过了你呢? ',
  82. 'content_2': {
  83. 'picture_mini': {
  84. 'url':
  85. 'https://img2.baidu.com/it/u=3202947311,1179654885&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500',
  86. 'width': 800,
  87. 'height': 500
  88. },
  89. 'picture':
  90. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  91. },
  92. 'content_3':
  93. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  94. 'content_4': '',
  95. 'content_5': '',
  96. 'content_6': '',
  97. 'times': 1000000 + 9
  98. });
  99. _arrayChatMessage.add({
  100. 'id': 100,
  101. 'type': 2,
  102. 'content_1': ' ',
  103. 'content_2': {
  104. 'picture_mini': {
  105. 'url':
  106. 'https://img2.baidu.com/it/u=3202947311,1179654885&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500',
  107. 'width': 800,
  108. 'height': 500
  109. },
  110. 'picture':
  111. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  112. },
  113. 'content_3':
  114. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  115. 'content_4': '',
  116. 'content_5': '',
  117. 'content_6': '',
  118. 'times': 1000000 + 9
  119. });
  120. _arrayChatMessage.add({
  121. 'id': 100,
  122. 'type': 2,
  123. 'content_1': ' ',
  124. 'content_2': {
  125. 'picture_mini': {
  126. 'url':
  127. 'https://lmg.jj20.com/up/allimg/1114/033021091503/210330091503-6-1200.jpg',
  128. 'width': 800,
  129. 'height': 500
  130. },
  131. 'picture':
  132. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  133. },
  134. 'content_3':
  135. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  136. 'content_4': '',
  137. 'content_5': '',
  138. 'content_6': '',
  139. 'times': 1000000 + 9
  140. });
  141. _arrayChatMessage.add({
  142. 'id': 100,
  143. 'type': 2,
  144. 'content_1': ' ',
  145. 'content_2': {
  146. 'picture_mini': {
  147. 'url':
  148. 'https://lmg.jj20.com/up/allimg/1114/033021091503/210330091503-6-1200.jpg',
  149. 'width': 800,
  150. 'height': 500
  151. },
  152. 'picture':
  153. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  154. },
  155. 'content_3':
  156. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  157. 'content_4': '',
  158. 'content_5': '',
  159. 'content_6': '',
  160. 'times': 1000000 + 9
  161. });
  162. _arrayChatMessage.add({
  163. 'id': 100,
  164. 'type': 2,
  165. 'content_1': ' ',
  166. 'content_2': {
  167. 'picture_mini': {
  168. 'url':
  169. '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',
  170. 'width': 800,
  171. 'height': 500
  172. },
  173. 'picture':
  174. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  175. },
  176. 'content_3':
  177. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  178. 'content_4': '',
  179. 'content_5': '',
  180. 'content_6': '',
  181. 'times': 1000000 + 9
  182. });
  183. _arrayChatMessage.add({
  184. 'id': 100,
  185. 'type': 2,
  186. 'content_1': ' ',
  187. 'content_2': {
  188. 'picture_mini': {
  189. 'url':
  190. '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',
  191. 'width': 800,
  192. 'height': 500
  193. },
  194. 'picture':
  195. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  196. },
  197. 'content_3':
  198. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  199. 'content_4': '',
  200. 'content_5': '',
  201. 'content_6': '',
  202. 'times': 1000000 + 9
  203. });
  204. _arrayChatMessage.add({
  205. 'id': 100,
  206. 'type': 2,
  207. 'content_1': ' ',
  208. 'content_2': {
  209. 'picture_mini': {
  210. 'url': 'https://photo.tuchong.com/4274381/f/1139873881.jpg',
  211. 'width': 800,
  212. 'height': 500
  213. },
  214. 'picture':
  215. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  216. },
  217. 'content_3':
  218. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  219. 'content_4': '',
  220. 'content_5': '',
  221. 'content_6': '',
  222. 'times': 1000000 + 9
  223. });
  224. _arrayChatMessage.add({
  225. 'id': 100,
  226. 'type': 2,
  227. 'content_1': ' ',
  228. 'content_2': {
  229. 'picture_mini': {
  230. 'url': 'https://photo.tuchong.com/4274381/f/11398738812.jpg',
  231. 'width': 800,
  232. 'height': 500
  233. },
  234. 'picture':
  235. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  236. },
  237. 'content_3':
  238. 'https://user-info-1302720239.cos.ap-nanjing.myqcloud.com/userIcon/user_icon_000100.jpg',
  239. 'content_4': '',
  240. 'content_5': '',
  241. 'content_6': '',
  242. 'times': 1000000 + 9
  243. });
  244. //print('--datum:${widget.userInfoOther}');
  245. }
  246. @override
  247. Widget build(BuildContext context) {
  248. return Scaffold(
  249. resizeToAvoidBottomInset: false,
  250. appBar: AppBar(
  251. backgroundColor: AppColor.colorEDEDED,
  252. shadowColor: AppColor.colordddddd,
  253. elevation: 1.cale,
  254. leading: AppWidget.iconBack(() {
  255. AppNavigator().navigateBack();
  256. }),
  257. centerTitle: true,
  258. title: Text(
  259. widget.userInfoOther['name'],
  260. style: AppTextStyle.textStyle_34_000000,
  261. ),
  262. actions: [
  263. Padding(
  264. padding: EdgeInsets.only(right: 24.cale),
  265. child: AppWidget.inkWellEffectNone(
  266. onTap: () {},
  267. child: Icon(
  268. AppIcon.dot3,
  269. size: 46.cale,
  270. color: Colors.black,
  271. ),
  272. ),
  273. )
  274. ],
  275. ),
  276. body: ChangeNotifierProvider<ProviderChatContent>(
  277. create: (BuildContext context) => ProviderChatContent(),
  278. child: Builder(
  279. builder: (BuildContext context) {
  280. return Column(
  281. children: [
  282. Expanded(
  283. child: AppWidget.inkWellEffectNone(
  284. onTap: () {
  285. FocusScope.of(context).requestFocus(
  286. FocusNode(),
  287. );
  288. context
  289. .read<ProviderChatContent>()
  290. .updateContentShow(false);
  291. },
  292. child: ListView.builder(
  293. padding: EdgeInsets.symmetric(vertical: 30.cale),
  294. physics: const BouncingScrollPhysics(
  295. parent: AlwaysScrollableScrollPhysics(),
  296. ),
  297. shrinkWrap: false,
  298. reverse: _arrayChatMessage.length > 7,
  299. itemCount: _arrayChatMessage.length,
  300. // itemExtent: 188.cale,
  301. itemBuilder: (BuildContext context, int index) {
  302. if (index % 2 != 0) {
  303. return ChatElementSelf(
  304. userInfo: widget.userInfoOther,
  305. chatMessage: _arrayChatMessage[index],
  306. );
  307. } else {
  308. return ChatElementOther(
  309. userInfo: widget.userInfoOther,
  310. chatMessage: _arrayChatMessage[index]);
  311. }
  312. },
  313. ),
  314. ),
  315. ),
  316. Consumer(builder: (BuildContext context,
  317. ProviderChatContent providerChatContent, child) {
  318. return ChatBottom(
  319. providerChatContent: providerChatContent,
  320. );
  321. }),
  322. ],
  323. );
  324. },
  325. ),
  326. ),
  327. );
  328. }
  329. @override
  330. void dispose() {
  331. super.dispose();
  332. }
  333. }

provider_chat_content.dart

  1. import 'package:flutter/cupertino.dart';
  2. import 'package:imflutter/wrap/extension/extension.dart';
  3. ///用于 软键盘区/发送附件 域显示控制
  4. class ProviderChatContent extends ChangeNotifier {
  5. bool _contentShow = false;
  6. double _keyboardHeight = 200;
  7. /// 是否显示 附件区域
  8. bool get contentShow => _contentShow;
  9. /// 键盘高度
  10. double get keyboardHeight => _keyboardHeight - 20.cale;
  11. ///更新区域 展示
  12. void updateContentShow(bool isShow) {
  13. _contentShow = isShow;
  14. notifyListeners();
  15. }
  16. void updateKeyboardHeight(double height) {
  17. _keyboardHeight = height;
  18. notifyListeners();
  19. }
  20. }
标签: flutter android

本文转载自: https://blog.csdn.net/nicepainkiller/article/details/129256101
版权归原作者 nicepainkiller 所有, 如有侵权,请联系我们删除。

“flutter 微信聊天输入框”的评论:

还没有评论