You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

emoji_picker_flutter组件无法显示的问题排查求助

Flutter emoji_picker_flutter 无法在输入框下方正常显示的问题解决

在Flutter项目中使用emoji_picker_flutter组件,期望将其显示在TextInputField下方,但组件始终无法正常显示。尝试添加Config配置调整后问题仍未解决,询问问题原因及是否需要更换实现方式。

问题原因

  • 布局层级遮挡:当前代码中ListView()占据了整个屏幕空间,直接放在底部Column中的EmojiPicker会被ListView遮挡,无法正常显示。
  • 缺少显示控制逻辑:没有添加开关状态,EmojiPicker默认持续显示,既不符合点击表情按钮才弹出的交互逻辑,也会挤占输入栏布局空间。
  • 布局约束不合理:底部Column未设置最小尺寸约束,导致EmojiPicker显示时容易出现布局溢出或挤压输入栏的问题。

解决方案

不需要更换组件,只需调整布局结构并添加显示控制逻辑即可解决:

  1. 添加状态变量控制显示:在State类中新增布尔变量,用于切换EmojiPicker的显示/隐藏状态。
  2. 绑定输入框控制器:通过TextEditingController实现表情插入输入框、退格删除的完整交互。
  3. 调整Stack层级:将EmojiPicker放在Stack顶层,用Positioned固定位置,避免被ListView遮挡;用Visibility根据状态控制显示。
  4. 优化底部布局:设置底部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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 09:41:19