Flutter - Smooth switching between chat keyboard and panel

LinXunFeng
4 min readJul 10, 2024

--

Overview

I believe everyone will encounter this problem when you make a chat app, that is, how to implement smooth switching between the keyboard and other panels on the chat page. This problem has also been raised by issue , such as

It can be seen that when switching from the emoji panel to the keyboard, it will jitter, which affects the user experience.

But several years have passed and it still hasn’t been solved. In addition, I also have this little trouble, so I developed this chat_bottom_container together with GitLqr (GitHub: github.com/GitLqr) to help us implement this smooth switching effect quickly.

Effect

Let’s take a look at the effect first, so that everyone can feel it intuitively~

OK, let’s take a look at how to use it.

Integration

Add chat_bottom_container to your pubspec.yaml file.

dependencies:
chat_bottom_container: latest_version

Please copy the latest version on pub.dev , the link is: https://pub.dev/packages/chat_bottom_container

The Android side needs to add the jitpack to the root build.gradle file of your project at the end of repositories.

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

Import chat_bottom_container where needed:

import 'package:chat_bottom_container/chat_bottom_container.dart';

Use

Page Layout

The first thing you need to do is determine the overall page layout.

@override
Widget build(BuildContext context) {
return Scaffold(
// Set to false
resizeToAvoidBottomInset: false,
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: ListView.builder(
...
),
),
// Input box view
_buildInputView(),
// Bottom panel container
_buildPanelContainer(),
],
),
);
}

Here are two points to note:

  1. Place the bottom container at the bottom.
  2. The resizeToAvoidBottomInset must be set to false .

Bottom container

There are various styles of input box view, so I won’t go into them here. You can implement them however you like. Next, let’s take a look at how to use the chat_bottom_container package to implement the bottom container. The code is as follows:

/// Custom bottom panel type
enum PanelType {
none,
keyboard,
emoji,
tool,
}

/// The controller of chat_bottom_container, it is used to inform
/// chat_bottom_container that the panel type needs to be switched.
/// Note: The generic PanelType is passed here to associate with the external
/// panel type.
final controller = ChatBottomPanelContainerController<PanelType>();

/// The focus object of the input box.
final FocusNode inputFocusNode = FocusNode();

/// Record the current bottom panel type.
PanelType currentPanelType = PanelType.none;

Widget _buildPanelContainer() {
return ChatBottomPanelContainer<PanelType>(
controller: controller,
inputFocusNode: inputFocusNode,
otherPanelWidget: (type) {
// Return the customized panel view.
if (type == null) return const SizedBox.shrink();
switch (type) {
case PanelType.emoji: // emoji panel
return _buildEmojiPickerPanel();
case PanelType.tool: // tools panel
return _buildToolPanel();
default:
return const SizedBox.shrink();
}
},
onPanelTypeChange: (panelType, data) {
// This callback can be used to record the current panel type
// (this operation is not necessary~)
// panelType: There are three types of panels defined in
// chat_bottom_container(.none|.keyboard|.other).
// data: External custom bottom panel type.
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;
}
},
// Bottom panel container background color.
panelBgColor: panelBgColor,
);
}

There are only three internally defined panel types.

enum ChatBottomPanelType {
none,
keyboard,
other,
}

Because chat_bottom_container doesn't care how many external panel types you have besides the keyboard, it is uniformly regarded as ChatBottomPanelType.other here.

It is impossible for us business developers not to care, so when we click the emoji panel button, we call controller.updatePanelType to switch the bottom panel type, and pass in the external customized PanelType for the parameter data , as shown in the following code.

controller.updatePanelType(
// Set the current bottom panel type of ChatBottomPanelContainer.
// Can be passed to ChatBottomPanelType.keyboard | ChatBottomPanelType.other | ChatBottomPanelType.none
ChatBottomPanelType.other,
// Callback will include the PanelType customized by external developers,
// this parameter must be passed when ChatBottomPanelType.other.
data: PanelType.emoji, // PanelType.tool
);

So the generics mentioned above are to associate the type of parameter data in the otherPanelWidget and onPanelTypeChange callbacks for our convenience.

Finally

Okay, the above are the core usage steps of this package. I have published the chat_bottom_container package to GitHub : https://github.com/LinXunFeng/flutter_chat_packages/tree/main/packages/chat_bottom_container.

Open source is not easy. If you also think this package is useful, please give it a Star 👍 .

--

--