中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Flutter如何自定義應用程序內鍵盤

發布時間:2022-06-14 11:52:35 來源:億速云 閱讀:311 作者:iii 欄目:開發技術

這篇文章主要介紹“Flutter如何自定義應用程序內鍵盤”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Flutter如何自定義應用程序內鍵盤”文章能幫助大家解決問題。

效果:

Flutter如何自定義應用程序內鍵盤

Flutter如何自定義應用程序內鍵盤

創建關鍵小部件

Flutter的優點是,通過組合更簡單的小部件,可以輕松構建鍵盤等復雜布局。首先,您將創建幾個簡單的按鍵小部件。

文本鍵

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

Flutter如何自定義應用程序內鍵盤

顯示文本鍵(包括空格鍵)的自定義寫意紅色圓圈

將以下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小部件具有不同外觀和功能的退格鍵。

Flutter如何自定義應用程序內鍵盤

退格鍵

將以下小部件添加到您的項目中:

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,因為不需要將任何文本傳遞回鍵盤。

將按鍵組成鍵盤

一旦有了按鍵,鍵盤就很容易布局,因為它們只是列中的行。

Flutter如何自定義應用程序內鍵盤

包含三行的列

這是代碼。我省略了一些重復的部分,以便簡潔。不過,你可以在文章的末尾找到它。

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。這使得空格鍵占用了后空鍵寬度的四倍。

在應用程序中使用鍵盤

現在,您可以像這樣使用自定義鍵盤小部件:

Flutter如何自定義應用程序內鍵盤

代碼看起來是這樣的:

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方法。)然而,這仍然不是一個完美的解決方案,因為它不適用于像????????或????&zwj;????&zwj;????這樣的字素簇,它們由多個代理對組成。不過,至少它不會

以下是象形文字和表情符號鍵盤作為演示:

Flutter如何自定義應用程序內鍵盤

????????????&zwj;????&zwj;

如果您對此有意見,請參閱此堆棧溢出問題。

防止系統鍵盤顯示

如果您想將自定義鍵盤與aTextField一起使用,但系統鍵盤不斷彈出,那會有點煩人。這畢竟是默認行為。

防止系統鍵盤顯示的方法是將TextFieldreadOnly屬性設置為true

TextField(
  ...
  showCursor: true,
  readOnly: true,
),

此外,將showCursor設置為true,使光標在您使用自定義鍵盤時仍然可以工作。

在系統鍵盤和自定義鍵盤之間切換

如果您想讓用戶選擇使用系統鍵盤或自定義鍵盤,您只需為readOnly使用不同的值進行重建。

Flutter如何自定義應用程序內鍵盤

以下是演示應用程序中TextField的設置方式:

class _KeyboardDemoState extends State<KeyboardDemo> {
  TextEditingController _controller = TextEditingController();
  bool _readOnly = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      body: Column(
        children: [
          ...
          TextField(
            controller: _controller,
            decoration: ...,
            style: TextStyle(fontSize: 24),
            autofocus: true,
            showCursor: true,
            readOnly: _readOnly,
          ),
          IconButton(
            icon: Icon(Icons.keyboard),
            onPressed: () {
              setState(() {
                _readOnly = !_readOnly;
              });
            },
          ),

有趣的部分:

  • 當按下鍵盤IconButton時,更改_readOnly的值,然后重建布局。這會導致系統鍵盤隱藏或顯示。

  • Scaffold上的resizeToAvoidBottomInset設置為false,允許系統鍵盤覆蓋自定義鍵盤。另一個選項是在顯示系統鍵盤時隱藏自定義鍵盤。然而,當我在實驗中這樣做時,我發現我必須使用單獨的布爾值來隱藏自定義鍵盤,這樣我就可以延遲顯示它,直到系統鍵盤消失。否則,它會跳到系統鍵盤頂部一秒鐘。

就這樣!如您所見,制作自己的應用程序內鍵盤并不難。

完整代碼

以下是我在本文中使用的演示應用程序的完整代碼:

import 'package:flutter/material.dart';

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

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

class KeyboardDemo extends StatefulWidget {
  @override
  _KeyboardDemoState createState() => _KeyboardDemoState();
}

class _KeyboardDemoState extends State<KeyboardDemo> {
  TextEditingController _controller = TextEditingController();
  bool _readOnly = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("大前端之旅的自定義鍵盤"),
      ),
      resizeToAvoidBottomInset: false,
      body: Column(
        children: [
          Text("微信:xjg13690"),
          SizedBox(height: 50),
          TextField(
            controller: _controller,
            decoration: InputDecoration(
              border: OutlineInputBorder(
                borderRadius: BorderRadius.circular(3),
              ),
            ),
            style: TextStyle(fontSize: 24),
            autofocus: true,
            showCursor: true,
            readOnly: _readOnly,
          ),
          IconButton(
            icon: Icon(Icons.keyboard),
            onPressed: () {
              setState(() {
                _readOnly = !_readOnly;
              });
            },
          ),
          Spacer(),
          CustomKeyboard(
            onTextInput: (myText) {
              _insertText(myText);
            },
            onBackspace: () {
              _backspace();
            },
          ),
        ],
      ),
    );
  }

  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,
    );
  }

  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;
  }

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

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,
          ),
        ],
      ),
    );
  }
}

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!)),
            ),
          ),
        ),
      ),
    );
  }
}

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),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

關于“Flutter如何自定義應用程序內鍵盤”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

隆昌县| 西乡县| 都匀市| 望谟县| 英德市| 荥经县| 金门县| 高要市| 紫云| 马鞍山市| 吴堡县| 天津市| 云浮市| 巧家县| 新和县| 朔州市| 依兰县| 淮安市| 嘉善县| 荔浦县| 松滋市| 铅山县| 天台县| 临城县| 石林| 澄江县| 萨嘎县| 东明县| 全椒县| 道真| 建始县| 宁国市| 麻江县| 五寨县| 始兴县| 图们市| 铜川市| 乌兰察布市| 夹江县| 青龙| 吉林省|