详解Flutter自定义应用程序内键盘的实现方法

目录
  • 创建关键小部件
    • 文本键
    • Backspace键
  • 将按键组成键盘
  • 在应用程序中使用键盘
    • 处理文本输入
    • 处理退格
  • 防止系统键盘显示
    • 在系统键盘和自定义键盘之间切换
  • 完整代码

本文将向您展示如何创建自定义键盘小部件,用于在您自己的应用程序中的Flutter TextField中输入文本。使用案例包括特殊字符或语言的文本输入,其中系统键盘可能不存在或用户可能没有安装正确的键盘。

我们今天将制作一个更简单的版本:

注意 :本文不会告诉您如何构建用户在任何应用程序中安装和使用的系统键盘。这只是一种基于小部件的方法,可以在您自己的应用程序中使用。

完整的代码在文章的底部。

创建关键小部件

Flutter的优点是,通过组合更简单的小部件,可以轻松构建键盘等复杂布局。首先,您将创建几个简单的按键小部件。

文本键

我已经圈出了由您首先制作的TextKey小部件制作的键。

显示文本键(包括空格键)的自定义写意红色圆圈

将以下TextKey小部件添加到您的项目中:

class TextKey extends StatelessWidget {
  const TextKey({
    Key key,
    @required this.text,
    this.onTextInput,
    this.flex = 1,
  }) : super(key: key);
  final String text;
  final ValueSetter<String> onTextInput;
  final int flex;
  @override
  Widget build(BuildContext context) {
    return Expanded(
      flex: flex,
      child: Padding(
        padding: const EdgeInsets.all(1.0),
        child: Material(
          color: Colors.blue.shade300,
          child: InkWell(
            onTap: () {
              onTextInput?.call(text);
            },
            child: Container(
              child: Center(child: Text(text)),
            ),
          ),
        ),
      ),
    );
  }
}

以下是有趣的部分:

  • flex属性允许您的按键均匀分布在一行之间,甚至占据行的更大比例(如上图中的空格键)。
  • 按下按键后,它将以anonTextInput回调的形式将其值传递给键盘。

Backspace键

您还需要一个与TextKey小部件具有不同外观和功能的退格键。

退格键

将以下小部件添加到您的项目中:

​
class BackspaceKey extends StatelessWidget {
  const BackspaceKey({
    Key? key,
    this.onBackspace,
    this.flex = 1,
  }) : super(key: key);
​
  final VoidCallback? onBackspace;
  final int flex;
​
  @override
  Widget build(BuildContext context) {
    return Expanded(
      flex: flex,
      child: Padding(
        padding: const EdgeInsets.all(1.0),
        child: Material(
          color: Colors.blue.shade300,
          child: InkWell(
            onTap: () {
              onBackspace?.call();
            },
            child: Container(
              child: Center(
                child: Icon(Icons.backspace),
              ),
            ),
          ),
        ),
      ),
    );
  }

备注:

  • TextKey代码有点重复,因此一些重构是为了使其更加简介。
  • onBackspaceVoidCallback,因为不需要将任何文本传递回键盘。

将按键组成键盘

一旦有了按键,键盘就很容易布局,因为它们只是列中的行。

包含三行的列

这是代码。我省略了一些重复的部分,以便简洁。不过,你可以在文章的末尾找到它。

​
class CustomKeyboard extends StatelessWidget {
  CustomKeyboard({
    Key? key,
    this.onTextInput,
    this.onBackspace,
  }) : super(key: key);
​
  final ValueSetter<String>? onTextInput;
  final VoidCallback? onBackspace;
​
  void _textInputHandler(String text) => onTextInput?.call(text);
​
  void _backspaceHandler() => onBackspace?.call();
​
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 160,
      color: Colors.blue,
      child: Column(
        children: [
          buildRowOne(),
          buildRowTwo(),
          buildRowThree(),
          buildRowFour(),
          buildRowFive()
        ],
      ),
    );
  }
​
  Expanded buildRowOne() {
    return Expanded(
      child: Row(
        children: [
          TextKey(
            text: '坚',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '果',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '祝',
            onTextInput: _textInputHandler,
          ),
        ],
      ),
    );
  }
​
  Expanded buildRowTwo() {
    return Expanded(
      child: Row(
        children: [
          TextKey(
            text: 'I',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: 'n',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: 'f',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: 'o',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: 'Q',
            onTextInput: _textInputHandler,
          ),
        ],
      ),
    );
  }
​
  Expanded buildRowThree() {
    return Expanded(
      child: Row(
        children: [
          TextKey(
            text: '十',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '五',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '周',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '年',
            onTextInput: _textInputHandler,
          ),
        ],
      ),
    );
  }
​
  Expanded buildRowFour() {
    return Expanded(
      child: Row(
        children: [
          TextKey(
            text: '生',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '日',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '快',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '乐',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '!',
            onTextInput: _textInputHandler,
          ),
        ],
      ),
    );
  }
​
  Expanded buildRowFive() {
    return Expanded(
      child: Row(
        children: [
          TextKey(
            text: ' ',
            flex: 2,
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: ' ',
            flex: 2,
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '',
            flex: 2,
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '',
            flex: 2,
            onTextInput: _textInputHandler,
          ),
          BackspaceKey(
            onBackspace: _backspaceHandler,
          ),
        ],
      ),
    );
  }
}

有趣的部分:

  • 键盘收集按键的回调并传递它们。这样,任何使用CustomKeyboard的人都会收到回调。
  • 您可以看到第三行如何使用flex。空格键的弯曲为4,而退格的默认弯曲为1。这使得空格键占用了后空键宽度的四倍。

在应用程序中使用键盘

现在,您可以像这样使用自定义键盘小部件:

代码看起来是这样的:

CustomKeyboard(
  onTextInput: (myText) {
    _insertText(myText);
  },
  onBackspace: () {
    _backspace();
  },
),

处理文本输入

以下是_insertText方法的样子:

void _insertText(String myText) {
  final text = _controller.text;
  final textSelection = _controller.selection;
  final newText = text.replaceRange(
    textSelection.start,
    textSelection.end,
    myText,
  );
  final myTextLength = myText.length;
  _controller.text = newText;
  _controller.selection = textSelection.copyWith(
    baseOffset: textSelection.start + myTextLength,
    extentOffset: textSelection.start + myTextLength,
  );
}

_controllerTextFieldTextEditingController。你必须记住,可能有一个选择,所以如果有的话,请用密钥传递的文本替换它。

感谢这个,以提供帮助。*

处理退格

您会认为退格很简单,但有一些不同的情况需要考虑:

  • 有一个选择(删除选择)
  • 光标在开头(什么都不要做)
  • 其他任何事情(删除之前的角色)

以下是_backspace方法的实现:

void _backspace() {
  final text = _controller.text;
  final textSelection = _controller.selection;
  final selectionLength = textSelection.end - textSelection.start;
  // There is a selection.
  if (selectionLength > 0) {
    final newText = text.replaceRange(
      textSelection.start,
      textSelection.end,
      '',
    );
    _controller.text = newText;
    _controller.selection = textSelection.copyWith(
      baseOffset: textSelection.start,
      extentOffset: textSelection.start,
    );
    return;
  }
  // The cursor is at the beginning.
  if (textSelection.start == 0) {
    return;
  }
  // Delete the previous character
  final previousCodeUnit = text.codeUnitAt(textSelection.start - 1);
  final offset = _isUtf16Surrogate(previousCodeUnit) ? 2 : 1;
  final newStart = textSelection.start - offset;
  final newEnd = textSelection.start;
  final newText = text.replaceRange(
    newStart,
    newEnd,
    '',
  );
  _controller.text = newText;
  _controller.selection = textSelection.copyWith(
    baseOffset: newStart,
    extentOffset: newStart,
  );
}
bool _isUtf16Surrogate(int value) {
  return value & 0xF800 == 0xD800;
}

即使删除之前的角色也有点棘手。如果您在有表情符号或其他代理对时只回退单个代码单元这将导致崩溃。作为上述代码中的变通办法,我检查了上一个字符是否是UFT-16代理,如果是,则后退了两个字符。(我从Flutter TextPainter源代码中获得了_isUtf16Surrogate方法。)然而,这仍然不是一个完美的解决方案,因为它不适用于像

(0)

相关推荐

  • Flutter 全局点击空白处隐藏键盘实战

    老孟导读:为什么要实现点击空白处隐藏键盘?因为这是 iOS 平台的默认行为,Android 平台由于其弹出的键盘右上角默认带有关闭键盘的按钮,所以点击空白处不会隐藏键盘. 对于单个页面来说,通过为 TextField 添加 focusNode,点击空白处时使 TextField 失去焦点,实现如下: class DismissKeyboardDemo extends StatelessWidget { final FocusNode focusNode = FocusNode(); @overr

  • 详解Flutter点击空白隐藏键盘的全局做法

    开发原生页面的时候,在处理键盘事件上,通常的需求是,点击输入框外屏幕,要隐藏键盘,同样的,这样的需求也需要在 Flutter 上实现, Android 上的实现方式是在基类 Activity 里实现事件分发,判断触摸位置是否在输入框内. /** * 获取点击事件 */ @CallSuper @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.MotionEv

  • Android自定义View软键盘实现搜索

    1. xml文件中加入自定义 搜索view <com.etoury.etoury.ui.view.IconCenterEditText android:id="@+id/search_et" style="@style/StyleEditText" android:hint="搜索景点信息" /> 2. 自定义的   view java文件 IconCenterEditText.java package com.etoury.etou

  • Android自定义软键盘的步骤记录

    目录 效果图 实现自定义软键盘 1.通过xml定义键盘 2.将xml文件与keyboardview绑定起来 3.处理点击事件onKey 附赠一些实用的效果处理 总结 效果图 还是咱们的老规矩,先放最终效果图

  • Android 仿微信自定义数字键盘的实现代码

    本文介绍了Android 仿微信自定义数字键盘的实现代码,分享给大家,希望对大家有帮助 最终效果: 实现这个自定义键盘的思路很简单: 要写出一个数字键盘的布局: 与 Edittext 结合使用,对每个按键的点击事件进行处理: 禁用系统软键盘. 有了思路,实现起来就不难了. 1. 实现键盘的 xml 布局 网格样式的布局用 GridView 或者 RecyclerView 都可以实现,其实用 GridView 更方便一些,不过我为了多熟悉 RecyclerView 的用法,这里选择用了 Recyc

  • 详解Flutter自定义应用程序内键盘的实现方法

    目录 创建关键小部件 文本键 Backspace键 将按键组成键盘 在应用程序中使用键盘 处理文本输入 处理退格 防止系统键盘显示 在系统键盘和自定义键盘之间切换 完整代码 本文将向您展示如何创建自定义键盘小部件,用于在您自己的应用程序中的Flutter TextField中输入文本.使用案例包括特殊字符或语言的文本输入,其中系统键盘可能不存在或用户可能没有安装正确的键盘. 我们今天将制作一个更简单的版本: 注意 :本文不会告诉您如何构建用户在任何应用程序中安装和使用的系统键盘.这只是一种基于小

  • 详解Flutter和Dart取消Future的三种方法

    目录 使用异步包(推荐) 完整示例 使用 timeout() 方法 快速示例 将Future转换为流 快速示例 结论 使用异步包(推荐) async包由 Dart 编程语言的作者开发和发布.它提供了dart:async风格的实用程序来增强异步计算.可以帮助我们取消Future的是CancelableOperation类: var myCancelableFuture = CancelableOperation.fromFuture( Future<T> inner, { FutureOr on

  • 详解Flutter如何完全自定义TabBar

    目录 前言 实现过程 完整代码 总结 前言 在App中TabBar形式交互是非常常见的,但是系统提供的的样式大多数又不能满足我们产品和UI的想法,这篇就记录下在Flutter中我在实现自定义TabBar的一个思路和过程,希望对你也有所帮助~ 先看下我最终的效果图: 实现过程 首先我们先看下TabBar的构造方法: const TabBar({ Key? key, required this.tabs,// tab组件列表 this.controller,// tabBar控制器 this.isS

  • 详解Flutter的路由导航

    Flutter 的路由导航 路由管理或导航管理:从一个页面平滑地过渡到另一个页面,我们需要有一个统一的机制来管理页面之间的跳转.在原生的Android 开发,是通过startActivity或startActivityForResult 来完成页面的跳转的,在Flutter 中如何实现呢? 在 Flutter 中,页面之间的跳转是通过 Route 和 Navigator 来管理的: Route 是页面的抽象,主要负责创建对应的界面,接收参数,响应 Navigator 打开和关闭: 而 Navig

  • 详解Flutter Widget

    目录 概述: Widget的本质: 分类: Widget StatelessWidget StatefulWidget State ParentDataWidget RenderObjectWidget 小结 概述: 所有的一切都可以被称为widget 在开发 Flutter 应用过程中,接触最多的无疑就是Widget,是『描述』 Flutter UI 的基本单元,通过Widget可以做到: 描述 UI 的层级结构 (通过Widget嵌套): 定制 UI 的具体样式 (如:font.color等

  • 详解Flutter中视频播放器插件的使用教程

    目录 创建一个新的视频播放器 添加播放和暂停按钮 创建一个快进 添加一个视频进度指示器 应用视频的字幕 结论 您已经看到很多包含视频内容的应用程序,比如带有视频教程的食谱应用程序.电影应用程序和体育相关的应用程序.您是否想知道如何将视频内容添加到您的下一个Flutter应用程序中? 从头开始实现视频功能将是一项繁重的任务.但有几个插件可以让开发者的生活变得轻松.视频播放器插件是可用于 Flutter 的最佳插件之一,可满足这一要求. 在这篇文章中,您将学习如何应用视频播放器插件以及控制视频播放器

  • 详解Flutter中Dart集合使用教程

    目录 前言 优先使用集合的特有语法 不要使用.length 属性判断集合是不是为空 避免使用 forEach 迭代元素 不要使用 List.from(),除非你想要更改结果的类型 使用 whereType 过滤类型 避免使用 cast() 做强制转换 总结 前言 集合是应用程序中最为常见的数据结构,Dart 一共支持如下四种集合,其中核心的 List, Map 和 Set 在基础框架中,而 Queue 在 dart:collection 库定义. 列表:也就是 List类,可动态增长的数组: k

  • 详解Flutter 响应式状态管理框架GetX

    目录 一.状态管理框架对比 Provider BLoC GetX 二.基本使用 2.1 安装与引用 2.2 使用GetX改造Counter App 2.3 GetX代码插件 三.其他功能 3.1 路由管理 3.2 依赖关系管理 3.3 工具 3.4 改变主题 3.5 GetConnect 3.6 GetPage中间件 Priority Redirect onPageCalled OnBindingsStart OnPageBuildStart 3.7 全局设置和手动配置 3.8 StateMix

  • 详解Flutter中StatefulBuilder组件的使用

    目录 例子 预览 编码 结论 本文是关于 Flutter 中的 StatefulBuilder 小部件.我将介绍小部件的基础知识,然后检查一个在实际中使用它的完整示例..StatefulBuilder 小部件可以在这些区域的状态发生变化时仅重建某些小区域而无需付出太多努力.这提高了应用程序的性能. StatefulBuilder({ Key? key, required StatefulWidgetBuilder builder r}) builder 函数有两个参数:context和一个用于在

随机推荐