Flutter - 实现聊天键盘与功能面板的丝滑切换 🍻

3,188 阅读3分钟

欢迎关注微信公众号:FSA全栈行动 👋

BiliBili: www.bilibili.com/video/BV1gA…

一、概述

相信大家接触【即时通信应用】的时候都会遇到这个问题,那就是如何去实现聊天页面的键盘和其它功能面板的丝滑切换,这种问题也有人向 Flutter 官方提出过 issue,如下这两个:

可以看到,从表情面板切到文本输入框时会抖动一下,影响用户体验。

但几年过去了依旧没有得到解决,再加上我自己也有这个小烦恼,所以我与 GitLqr(GitHub: github.com/GitLqr)一起开发了这个 chat_bottom_container,来帮助我们快速实现这个丝滑切换的效果。

二、效果

这里先附上效果图,好让大家直观感受一下~

下面我们一起来看看怎么使用吧。

三、集成

将 chat_bottom_container 添加到你的 pubspec.yaml 文件中

dependencies:
  chat_bottom_container: latest_version

具体版本大家到 pub.dev 上复制粘贴最新的吧,附上链接:pub.dev/packages/ch…

Android 端需要添加 jitpack 仓库到你的项目根目录下的 build.gradle 文件中:

allprojects {
  repositories {
    ...
    maven { url 'https://jitpack.io' }
  }
}

在需要使用的地方导入 chat_bottom_container :

import 'package:chat_bottom_container/chat_bottom_container.dart';

四、使用

页面布局

首先第一件事,确定整体的页面布局

@override
Widget build(BuildContext context) {
  return Scaffold(
    // 设置为 false
    resizeToAvoidBottomInset: false,
    body: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Expanded(
          child: ListView.builder(
            ...
          ),
        ),
        // 输入框视图
        _buildInputView(),
        // 底部容器
        _buildPanelContainer(),
      ],
    ),
  );
}

这里就两个注意点:

  1. 底部容器放在最底下
  2. resizeToAvoidBottomInset 必须设置为 false

底部容器

输入框视图的样式各种各样,这里就不说了,大家爱怎么实现都行,接下来我们来看看怎么使用 chat_bottom_container 这个库实现底部容器,代码如下:

/// 自定义底部面板类型
enum PanelType {
  // 收起
  none,
  // 键盘
  keyboard,
  // 表情
  emoji,
  // 其它工具
  tool,
}

/// chat_bottom_container 的控制器,用来告知 chat_bottom_container 需要切面板类型了
/// 注:这里传泛型传了 PanelType,是为了与外部的面板类型相关联
final controller = ChatBottomPanelContainerController<PanelType>();

/// 输入框的焦点对象
final FocusNode inputFocusNode = FocusNode();

/// 记录当前的底部面板类型
PanelType currentPanelType = PanelType.none;

Widget _buildPanelContainer() {
  return ChatBottomPanelContainer<PanelType>(
    controller: controller,
    inputFocusNode: inputFocusNode,
    otherPanelWidget: (type) {
      // 返回自定义的面板视图
      if (type == null) return const SizedBox.shrink();
      switch (type) {
        case PanelType.emoji: // 表情面板
          return _buildEmojiPickerPanel();
        case PanelType.tool: // 其它工具面板
          return _buildToolPanel();
        default:
          return const SizedBox.shrink();
      }
    },
    onPanelTypeChange: (panelType, data) {
      // 可用来记录当前的面板类型(该操作非必须~)
      // panelType: chat_bottom_container 内部定义的面板类型,就三种(.none|.keyboard|.other)
      // data: 外部自定义的底部面板类型
      switch (panelType) {
        case ChatBottomPanelType.none:
          currentPanelType = PanelType.none;
          break;
        case ChatBottomPanelType.keyboard:
          currentPanelType = PanelType.keyboard;
          break;
        case ChatBottomPanelType.other:
          if (data == null) return;
          switch (data) {
            case PanelType.emoji:
              currentPanelType = PanelType.emoji;
              break;
            case PanelType.tool:
              currentPanelType = PanelType.tool;
              break;
            default:
              currentPanelType = PanelType.none;
              break;
          }
          break;
      }
    },
    // 底部面板容器背景色
    panelBgColor: panelBgColor,
  );
}

chat_bottom_container 内部定义的面板类型,就三种

enum ChatBottomPanelType {
  // 无
  none,
  // 键盘
  keyboard,
  // 其它
  other,
}

因为 chat_bottom_container 并不关心你外部除了键盘外还有多少种面板类型,所以这里统一视为是 ChatBottomPanelType.other

而我们业务开发者不可能不在乎,所以我们在点击表情面板按钮时,调用 controller.updatePanelType 去切换底部面板类型,并为 data 这个参数传入外部自定义的底部面板类型,如下代码所示

controller.updatePanelType(
  // 设置 ChatBottomPanelContainer 当前的底部面板类型
  // 可传入 ChatBottomPanelType.keyboard | ChatBottomPanelType.other | ChatBottomPanelType.none
  ChatBottomPanelType.other,
  // 回调给外部开发者自定义的 PanelType,当 ChatBottomPanelType.other 时必传
  data: PanelType.emoji, // PanelType.tool
);

所以上面提到的泛型就是为了关联 otherPanelWidgetonPanelTypeChange 回调中的类型,方便我们拿来使用。

五、最后

好了,上述便是该库的核心使用步骤, 我已将上述的 Flutter 聊天底部面板容器库发布至 GitHub: github.com/LinXunFeng/… ,如果接入上有什么问题,可以在链接中查看 demo 演示代码。

开源不易,如果你也觉得这个库好用,请不吝给个 Star 👍 ,并多多支持!

本篇到此结束,感谢大家的支持,我们下次再见! 👋

如果文章对您有所帮助, 请不吝点击关注一下我的微信公众号:FSA全栈行动, 这将是对我最大的激励. 公众号不仅有 iOS 技术,还有 AndroidFlutterPython 等文章, 可能有你想要了解的技能知识点哦~