FSA全栈行动 FSA全栈行动
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr

公众号:FSA全栈行动

记录学习过程中的知识
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr
  • AndroidUI

  • Android第三方SDK

  • Android混淆

  • Android仓库

  • Android新闻

  • Android系统开发

  • Android源码

  • Android注解AOP

  • Android脚本

  • AndroidTv开发

  • AndroidNDK

  • Android音视频

  • Android热修复

  • Android性能优化

  • Android云游戏

  • Android插件化

  • iOSUI

  • iOS工具

  • iOS底层原理与应用

  • iOS组件化

  • iOS音视频

  • iOS疑难杂症

  • iOS之Swift

  • iOS之RxSwift

  • iOS开源项目

  • iOS逆向

  • Flutter开发

    • Dart - 抽象类的实例化
    • Flutter - 打印好用的Debug日志
    • Flutter - 混合开发
    • Flutter - 解决混合开发iOS脚本打包遇到的问题
    • Flutter - 低版本在iOS14上遇到的问题与解决方案
    • Flutter - 解决原生弹窗的触摸事件被Flutter响应的问题
    • Flutter - 实现列表上下拉切换header
    • Flutter - 获取ListView当前正在显示的Widget信息
    • Flutter - 列表滚动定位超强辅助库,墙裂推荐!🔥
    • Flutter - 快速实现聊天会话列表的效果,完美💯
    • Flutter - 聊天输入框更新文本时的必备优化点🔖
    • Flutter - 我给官方提PR,解决run命令卡住问题 😃
    • Flutter - 探索run命令到底做了什么 🤔
    • Flutter - 引擎调试(iOS篇)🛠
    • Flutter - 引擎调试bug到提交PR实战 🐞
    • Flutter - 船新升级😱支持观察第三方构建的滚动视图💪
    • Flutter - 瀑布流交替播放视频 🎞
    • Flutter - IM保持消息位置大升级(支持ChatGPT生成式消息) 🤖
    • Flutter - 滚动视图中的表单防遮挡 🗒
    • Flutter - 秒杀1/2曝光统计 📊
    • 一天内加入 Flutter 和 FlutterCandies 两大组织是什么体验 🧐
    • Flutter - 如何快速搓一个微信通讯录列表(azlist) 📓
    • Flutter - 混编项目集成Shorebird热更新🐦(安卓篇)
    • Flutter - 混编项目集成Shorebird热更新🐦(iOS篇)
    • Flutter - 解决返回原生页面时dispose方法未被触发的问题 🐞
    • Flutter - 升级3.19之后页面多次rebuild?🤨
    • Flutter - 热更新 Shorebird 1.0 正式版来了 🐦
    • Flutter - 使用Pigeon实现视频缓存插件 🐌
    • Flutter - 轻松搞定屏幕旋转功能 😎
    • Flutter - 解决Connection closed before full header was received
    • Flutter - 实现聊天键盘与功能面板的丝滑切换 🍻
    • Flutter - 支持观察NestedScrollView,兼容性更强 😈
    • Flutter - 聊天键盘与面板丝滑切换的强势升级 🍻
    • Flutter - 升级到3.24后页面还会多次rebuild吗?🧐
    • Flutter - 轻松实现PageView卡片偏移效果
    • Flutter - 轻松搞定炫酷视差(Parallax)效果
    • Flutter - 危!3.24版本苹果审核被拒!
    • Flutter - 子部件任意位置观察滚动数据
    • Flutter - iOS编译加速
    • Flutter - Xcode16 还原编译速度
    • Flutter - GetX Helper 助你规范应用 tag
    • Flutter - GetX Helper 如何应用于旧页面
    • Flutter - 聊天面板库动画生硬?这次让你丝滑个够
      • 一、概述
      • 二、效果
      • 三、集成
      • 四、使用
      • 五、细节
        • 构建内置面板
        • AnimatedSwitcher 动画
        • 面板视图保持状态
      • 六、最后
  • 移动端
  • Flutter开发
LinXunFeng
2025-07-20
目录

Flutter - 聊天面板库动画生硬?这次让你丝滑个够

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

# 一、概述

  • Flutter - 实现聊天键盘与功能面板的丝滑切换 🍻
  • Flutter - 聊天键盘与面板丝滑切换的强势升级 🍻

距离以上两篇文章的发布已经快 1 年了, 当时的最新版本是 0.2.0,现在,chat_bottom_container 迎来了 0.4.0 版本。

这次的更新主要是带来了自定义底部容器的功能,即容器的结构可完全由外部去定义,那可以通过这个功能去做什么呢?

  • 保持面板视图的状态
  • 实现任意动画(这次谁再说动画生硬,那就是你自己的问题了 😒)

# 二、效果

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

例子 效果图
Fade
Cube
Concentric
Rotation
ZoomIn

好了,更多效果大家可以跑示例去看看,下面我们一起来看看怎么使用吧。

# 三、集成

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

dependencies:
  chat_bottom_container: ^0.4.0

最新版本可到 pub.dev 上复制粘贴 https://pub.dev/packages/chat_bottom_container (opens new window)

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

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

注:无论是 iOS 还是 Android,在下载依赖时都需要使用魔法❗️❗️❗️ 特别是 iOS,下载失败了,请先考虑是自己的节点问题,而不是别人的问题 🫵

# 四、使用

页面结构如下

return Scaffold(
  resizeToAvoidBottomInset: false,
  appBar: _buildAppBar(),
  body: const Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      // 列表
      Expanded(child: ChatAnimationListView()),
      // 功能条(输入框、表情按钮、工具按钮等)
      ChatAnimationPanelBar(),
      // 底部容器
      ChatAnimationPanelContainer(),
    ],
  ),
);

这里主要看底部容器 ChatAnimationPanelContainer

对应的面板类型枚举

enum ChatAnimationPanelType {
  none,
  keyboard,
  emoji,
  tool,
}

底部容器的实现

ChatBottomPanelContainer<ChatAnimationPanelType>(
  // 控制器(必传,ChatBottomPanelContainerController)
  controller: state.controller,
  // 输入框焦点对象(必传,FocusNode)
  inputFocusNode: state.inputFocusNode,
  // 面板类型切换回调(按需实现,主要用来记录当前的面板类型是什么)
  onPanelTypeChange: logic.onPanelTypeChange,
  // 自定义容器回调
  customPanelContainer: (panelType, data) {
    if (!mounted) return const SizedBox.shrink();

    Widget? container = state.customPanelContainer;
    if (container != null) {
      // 已创建,刷新自定义容器,可以按需只刷新自定义容器里的子部件
      logic.update([
        ChatAnimationUpdateType.customPanelContainer,
      ]);
      return container;
    }
    // 未创建,则创建
    container = const ChatAnimationFadePanelContainer();
    // 记录起来
    state.customPanelContainer = container;
    return container;
  },
);

这里以上述效果图中的 Fade 为例,即 ChatAnimationFadePanelContainer,主要是通过 animated_size_and_fade 这个第三方库去实现切换动画。

// ChatAnimationFadePanelContainer

@override
Widget build(BuildContext context) {
  return GetBuilder<ChatAnimationLogic>(
    tag: logicTag,
    id: ChatAnimationUpdateType.customPanelContainer,
    builder: (_) {
      if (!mounted) return const SizedBox.shrink();
      // 构建对应类型的视图
      // 如:常规安全区、键盘占位视图、表情面板、功能面板等
      Widget resultWidget = logic.buildPanelWidget(
        state.currentPanelType,
        useKeyboardHeight: false,
      );
      // 包裹 AnimatedSizeAndFade 实现切换动画
      resultWidget = AnimatedSizeAndFade(
        fadeDuration: const Duration(milliseconds: 500),
        sizeDuration: const Duration(milliseconds: 300),
        child: resultWidget,
      );
      return resultWidget;
    },
  );
}

至此,通过 customPanelContainer 实现自定义容器的大体用法已经展示完毕了~

不过,相信聪明的你肯定会问,难道要完全自定义?能不能保留部分视图由 chat_bottom_container 帮我实现呢?比如常规安全区、键盘占位视图。

当然可以,接下来讲讲几点细节。

# 五、细节

# 构建内置面板

enum ChatBottomPanelType {
  // 常规安全区
  none,
  // 键盘占位视图
  keyboard,
  // 其它面板(Emoji、Tool)
  other,
}

Widget? buildInPanel(ChatBottomPanelType panelType) {
  switch (panelType) {
    case ChatBottomPanelType.none:
      return _state?.buildSafeArea.call();
    case ChatBottomPanelType.keyboard:
      return _state?.buildKeyboardPlaceholderPanel.call();
    case ChatBottomPanelType.other:
      return _state?.buildOtherPanel.call();
  }
}

通过调用 ChatBottomPanelContainerController 的 buildInPanel 方法,并传入 .none 或 .keyboard 时即可获取到相应的内置面板视图,而 .other 是获取外部 otherPanelWidget 回调返回的视图,未实现则得到 null。

以下是构建对应类型的视图的示例代码

Widget buildPanelWidget(
  ChatAnimationPanelType type,
  ...
) {
  final panelController = state.controller;

  ...
  Widget resultWidget;
  switch (type) {
    case ChatAnimationPanelType.none:
      // 常规安全区
      resultWidget = panelController.buildInPanel(ChatBottomPanelType.none) ??
          const SizedBox();
      ...
      break;
    case ChatAnimationPanelType.keyboard:
      // 键盘占位视图
      resultWidget = panelController.buildInPanel(ChatBottomPanelType.keyboard) ??
          const SizedBox.shrink();
      ...
      break;
    case ChatAnimationPanelType.emoji:
      // 表情面板
      resultWidget = ChatAnimationEmojiPanel(
        ...
      );
      break;
    case ChatAnimationPanelType.tool:
      // 工具面板
      resultWidget = ChatAnimationToolPanel(
        ...
      );
      break;
  }
  ...

  // 设置 key,这对 AnimatedSwitcher 来说很重要
  resultWidget = SizedBox(
    key: ValueKey('$type'),
    width: double.infinity,
    child: resultWidget,
  );

  return resultWidget;
}

# AnimatedSwitcher 动画

如果你使用 AnimatedSwitcher 来实现面板切换动画,那么建议你与上述代码一样,给每个面板都设置一个 key(通常使用 ValueKey),这是为了避免在切换面板时,AnimatedSwitcher 认为前后面板是同一个而导致动画失效。

# 面板视图保持状态

其实是否能保持面板的状态取决于你的布局实现,比如可以使用 IndexedStack,如果想要在保持状态的同时还有动画,就去找相应的动画库即可,比如示例中使用的 transitioned_indexed_stack。

# 六、最后

好了,上述便是该库的更新内容,如果你有其它好用的动画库,可以留言跟大家分享 🫶

这里惯例附上 GitHub 地址: https://github.com/LinXunFeng/flutter_chat_packages/tree/main/packages/chat_bottom_container (opens new window) ,如果接入上有什么问题,可以在链接中查看 demo 演示代码。

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

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

#Dart#Flutter#Chat
上次更新: 2025/07/20, 09:21:12
Flutter - GetX Helper 如何应用于旧页面

← Flutter - GetX Helper 如何应用于旧页面

最近更新
01
Flutter - GetX Helper 如何应用于旧页面
06-14
02
Flutter - GetX Helper 助你规范应用 tag
06-08
03
Flutter - Xcode16 还原编译速度
04-05
更多文章>
Theme by Vdoing | Copyright © 2020-2025 FSA全栈行动
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×