Language: English| 中文简体
Flutter plugin for create custom keyboards quickly.
Run flutter pub add extended_keyboard
, or add extended_keyboard
to pubspec.yaml dependencies manually.
dependencies:
extended_keyboard: ^latest_version
A singleton class that manages system keyboard height and provides functionality to handle keyboard layout changes.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await SystemKeyboard().init();
runApp(const MyApp());
}
if we want to close the keyboard without losing textfield focus, we can't use SystemChannels.textInput.invokeMethod<void>('TextInput.hide')
any more. related issue flutter/flutter#16863
Following code is a workaround.
TextField(
showCursor: true,
readOnly: true,
)
A widget that listens to changes in the CustomKeyboardController
and builds a widget accordingly.
KeyboardTypeBuilder(
builder: (
BuildContext context,
CustomKeyboardController controller,
) =>
ToggleButton(
builder: (bool active) => Icon(
Icons.sentiment_very_satisfied,
color: active ? Colors.orange : null,
),
activeChanged: (bool active) {
_keyboardPanelType = KeyboardPanelType.emoji;
if (active) {
controller.showCustomKeyboard();
if (!_focusNode.hasFocus) {
SchedulerBinding.instance
.addPostFrameCallback((Duration timeStamp) {
_focusNode.requestFocus();
});
}
} else {
controller.showSystemKeyboard();
}
},
active: controller.isCustom &&
_keyboardPanelType == KeyboardPanelType.emoji,
),
),
A controller for managing the keyboard type and notifying listeners.
KeyboardType
: The current keyboard typeisCustom
: whether current keyboard is customshowCustomKeyboard
: show the custom keyboardhideCustomKeyboard
: hide the custom keyboardshowSystemKeyboard
: show the system keyboard (set readOnly to false, it works if the input is on hasFocus)unfocus
: make the input lost focus and hide the system keyboard or custom keyboard
if Scaffold
is used, make sure set Scaffold.resizeToAvoidBottomInset
to false.
Using the KeyboardBuilder
widget to encapsulate the area containing the input field allows for the creation of a custom keyboard layout within its builder
callback. The builder
function receives a parameter named systemKeyboardHeight
, which represents the height of the last system keyboard displayed. This parameter can be utilized to set an appropriate height for your custom keyboard, ensuring a seamless and intuitive user experience.
parameter | description | default |
---|---|---|
builder | A builder function that returns a widget based on the system keyboard height. | required |
bodyBuilder | The main body widget builder with a parameter readOnly |
required |
resizeToAvoidBottomInset | The same as Scaffold.resizeToAvoidBottomInset . |
true |
controller | The controller for the custom keyboard. | null |
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(title: const Text('ChatDemo(KeyboardBuilder)')),
body: SafeArea(
bottom: true,
child: KeyboardBuilder(
resizeToAvoidBottomInset: true,
builder: (BuildContext context, double? systemKeyboardHeight) {
return Container();
},
bodyBuilder: (bool readOnly) => Column(children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: TextField(
readOnly: readOnly,
showCursor: true,
onTap: () {
_customKeyboardController.showSystemKeyboard();
},
),
),
KeyboardTypeBuilder(
builder: (
BuildContext context,
CustomKeyboardController controller,
) =>
ToggleButton(
builder: (bool active) => Icon(
Icons.sentiment_very_satisfied,
color: active ? Colors.orange : null,
),
activeChanged: (bool active) {
_keyboardPanelType = KeyboardPanelType.emoji;
if (active) {
controller.showCustomKeyboard();
if (!_focusNode.hasFocus) {
SchedulerBinding.instance
.addPostFrameCallback((Duration timeStamp) {
_focusNode.requestFocus();
});
}
} else {
controller.showSystemKeyboard();
}
},
active: controller.isCustom &&
_keyboardPanelType == KeyboardPanelType.emoji,
),
),
],
),
]),
),
),
);
You can directly use KeyboardBinding
or mix the KeyboardBindingMixin
into your WidgetsFlutterBinding
.
Future<void> main() async {
KeyboardBinding();
await SystemKeyboard().init();
runApp(const MyApp());
}
This configuration includes how the keyboard should be built, its animation durations, and how it should behave with respect to resizing.
parameter | description | default |
---|---|---|
getKeyboardHeight | Function that calculates the height of the custom keyboard. | required |
builder | The main body widget. | required |
keyboardName | The name of the keyboard | required |
showDuration | Duration for the keyboard's show animation. | const Duration(milliseconds: 200) |
hideDuration | Duration for the keyboard's hide animation. | const Duration(milliseconds: 200) |
resizeToAvoidBottomInset | The same as Scaffold.resizeToAvoidBottomInset . if it's null, it's equal to TextInputScope.resizeToAvoidBottomInset |
null |
KeyboardConfiguration(
getKeyboardHeight: (double? systemKeyboardHeight) =>
systemKeyboardHeight ?? 346,
builder: () {
return Container();
},
keyboardName: 'custom_number1',
resizeToAvoidBottomInset: true,
),
if Scaffold
is used, make sure set Scaffold.resizeToAvoidBottomInset
to false.
parameter | description | default |
---|---|---|
body | The main body widget. | required |
configurations | A list of KeyboardConfiguration |
required |
keyboardHeight | The default height of the keyboard. | 346 |
resizeToAvoidBottomInset | The same as Scaffold.resizeToAvoidBottomInset . |
true |
late List<KeyboardConfiguration> _configurations;
@override
void initState() {
super.initState();
_configurations = <KeyboardConfiguration>[
KeyboardConfiguration(
getKeyboardHeight: (double? systemKeyboardHeight) =>
systemKeyboardHeight ?? 346,
builder: () {
return Container();
},
keyboardName: 'custom_number',
),
];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('TextInputDemo'),
),
resizeToAvoidBottomInset: false,
body: SafeArea(
bottom: true,
child: TextInputScope(
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: Column(
children: <Widget>[
TextField(
keyboardType: _configurations[0].keyboardType,
controller: _controller,
decoration: InputDecoration(
hintText:
'The keyboardType is ${_configurations[0].keyboardType.name}',
),
),
],
),
),
configurations: _configurations,
),
),
);
}
The extension for TextEditingController
void insertText(String text)
Insert text at the current selection or replace the current selection withvoid delete()
Delete the character before the current selection or delete the current selectionTextEditingValue deleteText()
Delete the character before the current selection or delete the current selection and handle the TextEditingValue base on your casevoid performAction(TextInputAction action)
The same asTextInputClient.performAction