Flutter聊天界面底部容器插件chat_bottom_container的使用

发布于 1周前 作者 itying888 来自 Flutter

Flutter聊天界面底部容器插件 chat_bottom_container 的使用

chat_bottom_container 是一个用于管理聊天页面底部容器的 Flutter 插件,可以实现键盘与其他面板之间的平滑切换。以下是如何使用该插件的详细说明和示例代码。

安装

pubspec.yaml 文件中添加 chat_bottom_container 依赖:

dependencies:
  chat_bottom_container: latest_version

导入 chat_bottom_container 包:

import 'package:chat_bottom_container/chat_bottom_container.dart';

对于 Android 项目,需要在项目的根 build.gradle 文件中添加 jitpack 仓库:

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

使用方法

页面布局

整体页面布局如下:

@override
Widget build(BuildContext context) {
  return Scaffold(
    // 需要设置为 false
    resizeToAvoidBottomInset: false,
    body: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Expanded(
          child: ListView.builder(
            // 这里是你的消息列表构建逻辑
          ),
        ),
        // 输入框视图
        _buildInputView(),
        // 底部容器
        _buildPanelContainer(),
      ],
    ),
  );
}

底部容器

定义自定义面板类型:

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

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) {
      // 记录当前面板类型
      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,
  );
}

切换底部面板类型

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

隐藏面板

hidePanel() {
  inputFocusNode.unfocus();
  if (ChatBottomPanelType.none == controller.currentPanelType) return;
  controller.updatePanelType(ChatBottomPanelType.none);
}

自定义底部安全区域高度

默认情况下,chat_bottom_container 会自动添加底部安全区域高度,但在某些场景下你可能不希望这样,可以通过设置 safeAreaBottom0 来自定义此高度:

return ChatBottomPanelContainer<PanelType>(
  ...
  safeAreaBottom: 0,
  ...
);

调整键盘面板高度

例如,在主页的聊天页面上,外层底部固定的 BottomNavigationBar 的高度需要减去:

return ChatBottomPanelContainer<PanelType>(
  ...
  changeKeyboardPanelHeight: (keyboardHeight) {
    final renderObj = bottomNavigationBarKey.currentContext?.findRenderObject();
    if (renderObj is! RenderBox) return keyboardHeight;
    return keyboardHeight - renderObj.size.height;
  },
  ...
);

示例 Demo

以下是一个完整的示例 demo:

import 'package:chat_bottom_container/chat_bottom_container.dart';
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class App {
  static final routeObserver = RouteObserver<ModalRoute>();
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorObservers: [
        App.routeObserver,
      ],
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  late TabController _tabController;

  final bottomNavigationBarKey = GlobalKey();

  ChatBottomPanelContainerController? controller;

  @override
  void initState() {
    super.initState();

    _tabController = TabController(
      length: 3,
      vsync: this,
    );
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(
        title: const Text('Chat Bottom Container Example'),
      ),
      bottomNavigationBar: _buildBottomNavBar(),
      body: _buildBody(),
    );
  }

  Widget _buildBody() {
    Widget resultWidget = TabBarView(
      controller: _tabController,
      children: [
        ChatPage(
          safeAreaBottom: 0,
          showAppBar: false,
          changeKeyboardPanelHeight: (keyboardHeight) {
            final renderObj =
                bottomNavigationBarKey.currentContext?.findRenderObject();
            if (renderObj is! RenderBox) return keyboardHeight;
            return keyboardHeight - renderObj.size.height;
          },
          onControllerCreated: (controller) {
            this.controller = controller;
          },
        ),
        Container(color: Colors.red),
        Container(color: Colors.blue),
      ],
    );
    resultWidget = Stack(
      children: [
        resultWidget,
        Positioned(right: 10, child: _buildFloatingView()),
      ],
    );
    return resultWidget;
  }

  Widget _buildBottomNavBar() {
    return BottomNavigationBar(
      key: bottomNavigationBarKey,
      items: const [
        BottomNavigationBarItem(
          icon: Icon(Icons.chat),
          label: 'Chat',
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.people),
          label: 'People',
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.settings),
          label: 'Settings',
        ),
      ],
    );
  }

  Widget _buildFloatingView() {
    Widget resultWidget = Column(
      children: [
        _buildFloatingBtn(
          icon: Icons.chevron_right_sharp,
          onPressed: () {
            Navigator.of(context).push(
              MaterialPageRoute(
                builder: (context) {
                  return const ChatPage();
                },
              ),
            );
          },
        ),
        const SizedBox(height: 10),
        _buildFloatingBtn(
          icon: Icons.keyboard_arrow_up_sharp,
          onPressed: () {
            showModalBottomSheet(
              context: context,
              isScrollControlled: true,
              builder: (context) {
                return const FractionallySizedBox(
                  heightFactor: 0.8,
                  child: ChatPage(),
                );
              },
            );
          },
        ),
      ],
    );
    return resultWidget;
  }

  Widget _buildFloatingBtn({
    required void Function()? onPressed,
    IconData? icon,
  }) {
    return ElevatedButton(
      onPressed: onPressed,
      style: ElevatedButton.styleFrom(
        padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(10.0),
        ),
        shadowColor: Colors.grey[50],
        elevation: 2,
      ),
      child: Icon(icon),
    );
  }
}

通过以上步骤和示例代码,您可以轻松地在 Flutter 应用中集成并使用 chat_bottom_container 插件来实现聊天界面的底部容器管理功能。


更多关于Flutter聊天界面底部容器插件chat_bottom_container的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter聊天界面底部容器插件chat_bottom_container的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,我可以为你提供一个关于如何使用 chat_bottom_container 插件来创建一个 Flutter 聊天界面底部容器的示例代码。这个插件通常用于在聊天应用中实现一个包含输入框和发送按钮的底部容器。

首先,你需要在你的 pubspec.yaml 文件中添加 chat_bottom_container 依赖:

dependencies:
  flutter:
    sdk: flutter
  chat_bottom_container: ^最新版本号 # 请替换为最新的版本号

然后运行 flutter pub get 来获取依赖。

以下是一个简单的示例代码,展示了如何使用 chat_bottom_container 插件:

import 'package:flutter/material.dart';
import 'package:chat_bottom_container/chat_bottom_container.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ChatScreen(),
    );
  }
}

class ChatScreen extends StatefulWidget {
  @override
  _ChatScreenState createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen> {
  final TextEditingController _textController = TextEditingController();

  void _sendMessage() {
    String message = _textController.text;
    if (message.isNotEmpty) {
      // 在这里处理发送消息的逻辑,例如将消息添加到聊天列表中
      print("Sending message: $message");
      _textController.clear();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Chat Screen'),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            // 聊天消息列表(这里只是一个示例,你可以使用ListView.builder来动态加载消息)
            Expanded(
              child: Container(
                child: Column(
                  children: <Widget>[
                    Text('Message 1 from User A'),
                    Text('Message 2 from User B'),
                    // ...更多消息
                  ],
                ),
              ),
            ),
            // 聊天底部容器
            ChatBottomContainer(
              child: Container(
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(16),
                ),
                child: Row(
                  children: <Widget>[
                    Expanded(
                      child: TextField(
                        controller: _textController,
                        decoration: InputDecoration(
                          border: InputBorder.none,
                          hintText: 'Type a message...',
                        ),
                      ),
                    ),
                    IconButton(
                      icon: Icon(Icons.send),
                      color: Colors.blue,
                      onPressed: _sendMessage,
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的聊天界面,其中包含一个 ChatBottomContainer 组件。这个组件内包含一个 TextField 用于输入消息和一个 IconButton 用于发送消息。当用户点击发送按钮时,会调用 _sendMessage 方法,该方法会打印消息内容并清空输入框。

请注意,这个示例只是一个简单的演示,实际的聊天应用可能需要处理更多的逻辑,例如将消息保存到数据库、显示消息时间戳、加载更多消息等。你可以根据需求进一步扩展这个示例。

回到顶部