emoji_picker_flutter组件无法显示的问题排查求助
Flutter emoji_picker_flutter 无法在输入框下方正常显示的问题解决
在Flutter项目中使用emoji_picker_flutter组件,期望将其显示在TextInputField下方,但组件始终无法正常显示。尝试添加Config配置调整后问题仍未解决,询问问题原因及是否需要更换实现方式。
问题原因
- 布局层级遮挡:当前代码中
ListView()占据了整个屏幕空间,直接放在底部Column中的EmojiPicker会被ListView遮挡,无法正常显示。 - 缺少显示控制逻辑:没有添加开关状态,EmojiPicker默认持续显示,既不符合点击表情按钮才弹出的交互逻辑,也会挤占输入栏布局空间。
- 布局约束不合理:底部Column未设置最小尺寸约束,导致EmojiPicker显示时容易出现布局溢出或挤压输入栏的问题。
解决方案
不需要更换组件,只需调整布局结构并添加显示控制逻辑即可解决:
- 添加状态变量控制显示:在State类中新增布尔变量,用于切换EmojiPicker的显示/隐藏状态。
- 绑定输入框控制器:通过
TextEditingController实现表情插入输入框、退格删除的完整交互。 - 调整Stack层级:将EmojiPicker放在Stack顶层,用
Positioned固定位置,避免被ListView遮挡;用Visibility根据状态控制显示。 - 优化底部布局:设置底部Column为最小尺寸,避免不必要的空间占用。
修改后的代码示例
import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:emoji_picker_flutter/emoji_picker_flutter.dart'; import '../Model/ChatModel.dart'; class IndidivdualPage extends StatefulWidget { final ChatModel chatModel; const IndidivdualPage({Key? key, required this.chatModel}) : super(key: key); @override _IndividualPageState createState() => _IndividualPageState(); } class _IndividualPageState extends State<IndidivdualPage> { bool _showEmojiPicker = false; // 控制表情选择器显示状态 final TextEditingController _textController = TextEditingController(); // 输入框控制器 @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey, appBar: AppBar( leadingWidth: 70, titleSpacing: 0, leading: InkWell( onTap: () => Navigator.pop(context), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.arrow_back, size: 24), CircleAvatar( child: SvgPicture.asset( widget.chatModel.isGroup ? "assets/account-group.svg" : "assets/account.svg", color: Colors.white, height: 34, width: 34 ), radius: 20, backgroundColor: Colors.lightBlue, ), ], ), ), title: InkWell( onTap: () {}, child: Container( margin: EdgeInsets.all(6), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(widget.chatModel.name, style: TextStyle( fontSize: 18.5, fontWeight: FontWeight.bold )), Text("last seend today at 15:03", style: TextStyle( fontSize: 14, fontWeight: FontWeight.normal )) ], ), ), ), actions: [ IconButton(onPressed: () {}, icon: Icon(Icons.videocam)), IconButton(onPressed: () {}, icon: Icon(Icons.call)), PopupMenuButton<String>( onSelected: (value) => print(value), itemBuilder: (BuildContext context) => [ PopupMenuItem(child: Text("View Contact"), value:"View Contact"), ] ) ], ), body: Container( height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, child: Stack( children: [ ListView(), // 聊天消息列表 // 表情选择器:顶层布局,避免被遮挡 Visibility( visible: _showEmojiPicker, child: Positioned( bottom: 60, left: 0, right: 0, child: emojiSelect(), ), ), // 底部输入栏 Align( alignment: Alignment.bottomCenter, child: Column( mainAxisSize: MainAxisSize.min, // 仅占用必要空间 children: [ Row( children: [ Container( width: MediaQuery.of(context).size.width - 60, child: Card( margin: EdgeInsets.only(left: 2, right: 2, bottom: 8), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)), child: TextFormField( controller: _textController, textAlignVertical: TextAlignVertical.center, keyboardType: TextInputType.multiline, maxLines: 5, minLines: 1, decoration: InputDecoration( border: InputBorder.none, hintText: "Send a message", prefixIcon: IconButton( icon: Icon(Icons.emoji_emotions), onPressed: () => setState(() => _showEmojiPicker = !_showEmojiPicker), ), suffixIcon: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton(onPressed: () {}, icon: Icon(Icons.attach_file)), IconButton(onPressed: () {}, icon: Icon(Icons.camera_alt)), ], ), contentPadding: EdgeInsets.all(5), ), ), ), ), Padding( padding: const EdgeInsets.only(bottom: 8, right: 5, left: 2), child: CircleAvatar( radius: 25, backgroundColor: Colors.lightBlue, child: IconButton(icon: Icon(Icons.mic), color: Colors.white, onPressed: () {}), ), ), ], ), ], ), ) ], ), ), ); } Widget emojiSelect() { return EmojiPicker( onEmojiSelected: (Category? category, Emoji? emoji) { if (emoji != null) _textController.text += emoji.emoji; }, onBackspacePressed: () { if (_textController.text.isNotEmpty) { _textController.text = _textController.text.substring(0, _textController.text.length - 1); } }, config: Config( columns: 7, emojiSizeMax: 32 * (Platform.isIOS ? 1.30 : 1.0), verticalSpacing: 0, horizontalSpacing: 0, gridPadding: EdgeInsets.zero, initCategory: Category.RECENT, bgColor: Color(0xFFF2F2F2), indicatorColor: Colors.blue, iconColor: Colors.grey, iconColorSelected: Colors.blue, backspaceColor: Colors.blue, skinToneDialogBgColor: Colors.white, skinToneIndicatorColor: Colors.grey, enableSkinTones: true, showRecentsTab: true, recentsLimit: 28, noRecents: const Text( 'No Recents', style: TextStyle(fontSize: 20, color: Colors.black26), textAlign: TextAlign.center, ), loadingIndicator: const SizedBox.shrink(), tabIndicatorAnimDuration: kTabScrollDuration, categoryIcons: const CategoryIcons(), buttonMode: ButtonMode.MATERIAL, ), ); } }
额外说明
- 新增的
TextEditingController实现了表情插入输入框、退格删除的完整交互,让组件功能更完善。 Positioned组件固定了EmojiPicker的位置,确保它显示在输入框上方,不会被列表遮挡。mainAxisSize: MainAxisSize.min确保底部输入栏仅占用必要空间,避免布局溢出。
内容的提问来源于stack exchange,提问作者Test
相关产品推荐
相关产品推荐

