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

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

一、概述

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

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

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

二、效果

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

例子 效果图
Fade
Cube
Concentric
Rotation
ZoomIn

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

三、集成

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

yaml 复制代码
dependencies:
  chat_bottom_container: ^0.4.0

最新版本可到 pub.dev 上复制粘贴 pub.dev/packages/ch...

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

gradle 复制代码
allprojects {
  repositories {
    ...
    maven { url 'https://jitpack.io' }
  }
}

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

四、使用

页面结构如下

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

这里主要看底部容器 ChatAnimationPanelContainer

对应的面板类型枚举

dart 复制代码
enum ChatAnimationPanelType {
  none,
  keyboard,
  emoji,
  tool,
}

底部容器的实现

dart 复制代码
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 这个第三方库去实现切换动画。

dart 复制代码
// 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 帮我实现呢?比如常规安全区、键盘占位视图。

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

五、细节

构建内置面板

dart 复制代码
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();
  }
}

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

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

dart 复制代码
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 地址: github.com/LinXunFeng/... ,如果接入上有什么问题,可以在链接中查看 demo 演示代码。

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

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

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

相关推荐
造糖主义14 分钟前
vue-el-upload上传组件自定义删除-预览按钮遮罩层,不受原有的上传打开文件夹
前端·javascript·vue.js
小猫会后空翻1 小时前
XSS相关理解
前端·xss
brzhang1 小时前
我十几个项目都是这套Flutter 快速开发框架,今天开源了,从此你只用关心业务了
前端·后端·架构
BLACK5952 小时前
Nuxt3中PC端与移动端适配的三种方式(含Nuxt官网同款实现方式)
前端·vue.js·nuxt.js
小宁爱Python2 小时前
TypeScript 泛型详解:从基础到实战应用
前端·javascript·typescript
鱼樱前端2 小时前
一文讲解时下比较火的Rust语言之-rust开发环境搭建
前端·javascript
0wioiw03 小时前
Flutter基础(前端教程①⑤-API请求转化为模型列成列表展示实战)
flutter
草梅友仁3 小时前
草梅 Auth 1.0.0 发布与第三方登录接入指南 | 2025 年第 29 周草梅周报
开源·github·ai编程
Moment3 小时前
Next.js 15.4 正式发布:Turbopack 全面稳定,预热 Next.js 16 😍😍😍
前端·javascript·node.js
虚!!!看代码3 小时前
uni-app 跳转页面传参
前端·vue.js·uni-app