{"id":19272997,"url":"https://github.com/fluttercandies/extended_keyboard","last_synced_at":"2025-07-19T15:33:08.915Z","repository":{"id":254710446,"uuid":"844388925","full_name":"fluttercandies/extended_keyboard","owner":"fluttercandies","description":"Flutter plugin for create custom keyboards quickly.","archived":false,"fork":false,"pushed_at":"2025-04-20T12:27:31.000Z","size":729,"stargazers_count":15,"open_issues_count":1,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-20T13:29:42.970Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fluttercandies.png","metadata":{"files":{"readme":"README-ZH.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"custom":"http://zmtzawqlp.gitee.io/my_images/images/qrcode.png"}},"created_at":"2024-08-19T06:48:16.000Z","updated_at":"2025-04-20T12:27:35.000Z","dependencies_parsed_at":null,"dependency_job_id":"3ba44f39-6aef-48cf-9f24-89fffe4899a2","html_url":"https://github.com/fluttercandies/extended_keyboard","commit_stats":null,"previous_names":["fluttercandies/extended_keyboard"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fextended_keyboard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fextended_keyboard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fextended_keyboard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fextended_keyboard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fluttercandies","download_url":"https://codeload.github.com/fluttercandies/extended_keyboard/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250144331,"owners_count":21382198,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T20:39:35.239Z","updated_at":"2025-07-19T15:33:08.908Z","avatar_url":"https://github.com/fluttercandies.png","language":"Dart","funding_links":["http://zmtzawqlp.gitee.io/my_images/images/qrcode.png"],"categories":[],"sub_categories":[],"readme":"# extended_keyboard\n\n[![pub package](https://img.shields.io/pub/v/extended_keyboard.svg)](https://pub.dartlang.org/packages/extended_keyboard) [![GitHub stars](https://img.shields.io/github/stars/fluttercandies/extended_keyboard)](https://github.com/fluttercandies/extended_keyboard/stargazers) [![GitHub forks](https://img.shields.io/github/forks/fluttercandies/extended_keyboard)](https://github.com/fluttercandies/extended_keyboard/network) [![GitHub license](https://img.shields.io/github/license/fluttercandies/extended_keyboard)](https://github.com/fluttercandies/extended_keyboard/blob/master/LICENSE) [![GitHub issues](https://img.shields.io/github/issues/fluttercandies/extended_keyboard)](https://github.com/fluttercandies/extended_keyboard/issues) \u003ca href=\"https://qm.qq.com/q/ZyJbSVjfSU\"\u003e![FlutterCandies QQ 群](https://img.shields.io/badge/dynamic/yaml?url=https%3A%2F%2Fraw.githubusercontent.com%2Ffluttercandies%2F.github%2Frefs%2Fheads%2Fmain%2Fdata.yml\u0026query=%24.qq_group_number\u0026label=QQ%E7%BE%A4\u0026logo=qq\u0026color=1DACE8)\n\nLanguage: [English](README.md) | 中文简体\n\n用于快速创建自定义键盘的插件。\n\n- [extended\\_keyboard](#extended_keyboard)\n  - [安装](#安装)\n  - [使用](#使用)\n    - [SystemKeyboard](#systemkeyboard)\n    - [KeyboardBuilder](#keyboardbuilder)\n      - [KeyboardTypeBuilder](#keyboardtypebuilder)\n      - [CustomKeyboardController](#customkeyboardcontroller)\n      - [KeyboardBuilder](#keyboardbuilder-1)\n    - [TextInputScope](#textinputscope)\n      - [KeyboardBinding / KeyboardBindingMixin](#keyboardbinding--keyboardbindingmixin)\n      - [KeyboardConfiguration](#keyboardconfiguration)\n      - [TextInputScope](#textinputscope-1)\n    - [extension](#extension)\n\n\n## 安装\n\n运行 flutter pub add `extended_keyboard`, 或者直接手动添加 `extended_keyboard` 到 pubspec.yaml 中的 dependencies.\n\n``` yaml\ndependencies:\n  extended_keyboard: ^latest_version\n```\n\n## 使用\n\n### SystemKeyboard\n\n用于管理系统键盘的高度并提供处理键盘布局更改的功能。\n\n``` yaml\nFuture\u003cvoid\u003e main() async {\n  WidgetsFlutterBinding.ensureInitialized();\n  await SystemKeyboard().init();\n  runApp(const MyApp());\n}\n```\n### KeyboardBuilder\n\n如果我们想要关闭系统键盘，并且保持输入框的不丢失焦点，我们没法再使用 `SystemChannels.textInput.invokeMethod\u003cvoid\u003e('TextInput.hide')` 了. 相关问题 https://github.com/flutter/flutter/issues/16863\n\n下面的代码是一种变通方案\n\n``` dart\nTextField(\n  showCursor: true,\n  readOnly: true,\n)\n```\n\n#### KeyboardTypeBuilder\n\n用于监听 `KeyboardType` 改变的组件，并且提供 `CustomKeyboardController` 来控制自定义键盘的开关。\n\n``` dart\n   KeyboardTypeBuilder(\n     builder: (\n       BuildContext context,\n       CustomKeyboardController controller,\n     ) =\u003e\n         ToggleButton(\n       builder: (bool active) =\u003e Icon(\n         Icons.sentiment_very_satisfied,\n         color: active ? Colors.orange : null,\n       ),\n       activeChanged: (bool active) {\n         _keyboardPanelType = KeyboardPanelType.emoji;\n         if (active) {\n           controller.showCustomKeyboard();\n           if (!_focusNode.hasFocus) {\n             SchedulerBinding.instance\n                 .addPostFrameCallback((Duration timeStamp) {\n               _focusNode.requestFocus();\n             });\n           }\n         } else {\n           controller.showSystemKeyboard();\n         }\n       },\n       active: controller.isCustom \u0026\u0026\n           _keyboardPanelType == KeyboardPanelType.emoji,\n     ),\n   ),\n```\n\n#### CustomKeyboardController\n\n用于通知 `KeyboardType` 改变，并且控制自定义键盘的开关。\n\n* `KeyboardType` : 当前键盘的类型\n* `isCustom` :  是否是自定义键盘\n* `showCustomKeyboard` : 打开自定义键盘\n* `hideCustomKeyboard` : 关闭自定义键盘\n* `showSystemKeyboard` : 打开系统键盘 (通过将 readOnly 设置成 false)\n* `unfocus` : 使输入框失去焦点, 并且关闭系统和自定义键盘\n\n#### KeyboardBuilder\n\n如果使用 `Scaffold`，请确保将 `Scaffold.resizeToAvoidBottomInset` 设置为 `false`。\n\n使用 `KeyboardBuilder` 小部件来封装包含输入字段的区域，允许在其 `builder` 回调中创建自定义键盘布局。`builder` 函数接收一个名为 `systemKeyboardHeight` 的参数，该参数表示最后显示的系统键盘的高度。此参数可用于为您的自定义键盘设置适当的高度，从而确保无缝且直观的用户体验。\n\n| parameter                | description                                        | default  |\n| ------------------------ | -------------------------------------------------- | -------- |\n| builder                  | 一个构建器函数，它根据系统键盘高度返回一个小部件。 | required |\n| bodyBuilder              | 一个带 `readOnly` 参数的组件回调                   | required |\n| resizeToAvoidBottomInset | 跟 `Scaffold.resizeToAvoidBottomInset` 作用一致    | true     |\n| controller               | 自定义键盘控制器                                   | null     |\n\n\n``` dart\n  return Scaffold(\n    resizeToAvoidBottomInset: false,\n    appBar: AppBar(title: const Text('ChatDemo(KeyboardBuilder)')),\n    body: SafeArea(\n      bottom: true,\n      child: KeyboardBuilder(\n        resizeToAvoidBottomInset: true,\n        builder: (BuildContext context, double? systemKeyboardHeight) {\n          return Container();\n        },\n        bodyBuilder: (bool readOnly) =\u003e Column(children: \u003cWidget\u003e[\n          Row(\n            children: \u003cWidget\u003e[\n              Expanded(\n                child: TextField(\n                  readOnly: readOnly, \n                  showCursor: true,\n                  onTap: () {\n                    _customKeyboardController.showSystemKeyboard();\n                  },\n                ),\n              ),\n              KeyboardTypeBuilder(\n                builder: (\n                  BuildContext context,\n                  CustomKeyboardController controller,\n                ) =\u003e\n                    ToggleButton(\n                  builder: (bool active) =\u003e Icon(\n                    Icons.sentiment_very_satisfied,\n                    color: active ? Colors.orange : null,\n                  ),\n                  activeChanged: (bool active) {\n                    _keyboardPanelType = KeyboardPanelType.emoji;\n                    if (active) {\n                      controller.showCustomKeyboard();\n                      if (!_focusNode.hasFocus) {\n                        SchedulerBinding.instance\n                            .addPostFrameCallback((Duration timeStamp) {\n                          _focusNode.requestFocus();\n                        });\n                      }\n                    } else {\n                      controller.showSystemKeyboard();\n                    }\n                  },\n                  active: controller.isCustom \u0026\u0026\n                      _keyboardPanelType == KeyboardPanelType.emoji,\n                ),\n              ),\n            ],\n          ),\n        ]),\n      ),\n    ),\n  );\n```\n\n![img](https://github.com/fluttercandies/flutter_candies/blob/master/gif/extended_keyboard/chat_demo.gif)\n\n[Full Demo](https://github.com/fluttercandies/extended_keyboard/blob/main/example/lib/src/pages/chat_demo.dart)\n\n### TextInputScope\n\n#### KeyboardBinding / KeyboardBindingMixin\n\n你可以直接使用 `KeyboardBinding` ，或者将 `KeyboardBindingMixin` 混入到你的 `WidgetsFlutterBinding` 中。\n\n``` yaml\nFuture\u003cvoid\u003e main() async {\n  KeyboardBinding();\n  await SystemKeyboard().init();\n  runApp(const MyApp());\n}\n```\n\n#### KeyboardConfiguration\n\n这个配置包括键盘应该如何构建，它的动画持续时间，它的名字。\n\n| parameter                | description                                                                                                              | default                           |\n| ------------------------ | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------- |\n| getKeyboardHeight        | 返回自定义键盘的高度                                                                                                     | required                          |\n| builder                  | 包含输入框的主体                                                                                                         | required                          |\n| keyboardName             | 自定义键盘的名字                                                                                                         | required                          |\n| showDuration             | 自定义键盘打开的时间                                                                                                     | const Duration(milliseconds: 200) |\n| hideDuration             | 自定义键盘隐藏的时间                                                                                                     | const Duration(milliseconds: 200) |\n| resizeToAvoidBottomInset | 跟 `Scaffold.resizeToAvoidBottomInset` 一样的意思. 如果它不设置，将和 `TextInputScope.resizeToAvoidBottomInset` 的值相同 | null                              |\n\n``` dart\n  KeyboardConfiguration(\n    getKeyboardHeight: (double? systemKeyboardHeight) =\u003e\n        systemKeyboardHeight ?? 346,\n    builder: () {\n      return Container();\n    },\n    keyboardName: 'custom_number1',\n    resizeToAvoidBottomInset: true,\n  ),\n```\n\n\n#### TextInputScope\n\n如果使用 `Scaffold`，请确保将 `Scaffold.resizeToAvoidBottomInset` 设置为 `false`。\n\n| parameter                | description                                        | default  |\n| ------------------------ | -------------------------------------------------- | -------- |\n| body                     | 包含输入框的主体                                   | required |\n| configurations           | 自定义键盘配置                                     | required |\n| keyboardHeight           | 默认的自定义键盘高度                               | 346      |\n| resizeToAvoidBottomInset | 跟 `Scaffold.resizeToAvoidBottomInset` 的意思一样. | true     |\n\n``` dart\n  late List\u003cKeyboardConfiguration\u003e _configurations;\n  @override\n  void initState() {\n    super.initState();\n    _configurations = \u003cKeyboardConfiguration\u003e[\n      KeyboardConfiguration(\n        getKeyboardHeight: (double? systemKeyboardHeight) =\u003e\n            systemKeyboardHeight ?? 346,\n        builder: () {\n          return Container();\n        },\n        keyboardName: 'custom_number',\n      ),\n    ];\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: const Text('TextInputDemo'),\n      ),\n      resizeToAvoidBottomInset: false,\n      body: SafeArea(\n        bottom: true,\n        child: TextInputScope(\n          body: Padding(\n            padding: const EdgeInsets.symmetric(horizontal: 5),\n            child: Column(\n              children: \u003cWidget\u003e[\n                TextField(\n                  keyboardType: _configurations[0].keyboardType,\n                  controller: _controller,\n                  decoration: InputDecoration(\n                    hintText:\n                        'The keyboardType is ${_configurations[0].keyboardType.name}',\n                  ),\n                ),\n              ],\n            ),\n          ),\n          configurations: _configurations,\n        ),\n      ),\n    );\n  }\n```\n\n![img](https://github.com/fluttercandies/flutter_candies/blob/master/gif/extended_keyboard/text_input_demo.gif)\n\n[Full Demo](https://github.com/fluttercandies/extended_keyboard/blob/main/example/lib/src/pages/text_input_demo.dart)\n\n ### extension\n\nTextEditingController 的扩展方法\n\n*  `void insertText(String text)` 在当前位置插入文本\n*  `void delete()` 删除一个字符\n*  `TextEditingValue deleteText()`  删除一个字符并且返回删除之后的值，可以根据自己的情况再处理\n*  `void performAction(TextInputAction action)` 跟 `TextInputClient.performAction` 一样的作用","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffluttercandies%2Fextended_keyboard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffluttercandies%2Fextended_keyboard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffluttercandies%2Fextended_keyboard/lists"}