Flutter创建自定义的软键盘

参考代码:

Flutter - Create Custom Keyboard Examples

本文贴出的代码实现了一个输入十六进制数据的键盘:

(1)支持长按退格键连续删除字符;

(2)可通过退格键删除选中的文字;

(3)监听文本变化(包括粘贴剪切导致的变化)。 

hex_keyboard.dart

import 'dart:async';

import 'package:flutter/material.dart';

class HexKeyboard extends StatefulWidget {
  final TextEditingController controller;
  final void Function() onDone;
  final void Function(String) onTextChanged;

  const HexKeyboard({
    super.key,
    required this.controller,
    required this.onDone,
    required this.onTextChanged,
  });

  @override
  State<HexKeyboard> createState() => _HexKeyboardState();
}

class _HexKeyboardState extends State<HexKeyboard> {
  late TextEditingController _controller;

  final Widget _horizontalPadding = const SizedBox(width: 1.0);
  final Widget _verticalPadding = const SizedBox(height: 1.0);
  Timer? _timer;

  @override
  void initState() {
    super.initState();
    _controller = widget.controller;
  }

  void _handleType(String text) {
    int position = _controller.selection.base.offset;
    var value = _controller.text;

    if (value.isEmpty) {
      _controller.text = text;
    } else {
      _controller.text = value.substring(0, position) +
          text +
          value.substring(position, value.length);
    }

    _controller.selection =
        TextSelection.fromPosition(TextPosition(offset: position + 1));
    widget.onTextChanged(_controller.text);
  }

  void _handleBackspace() {
    final value = _controller.text;

    if (value.isNotEmpty) {
      final start = _controller.selection.start;
      final end = _controller.selection.end;
      print("selection.start=$start, selection.end=$end");
      final int offset;
      if(end > 0) {
        if(start == end) {
          _controller.text = value.substring(0, start - 1) +
              value.substring(start, value.length);
          offset = start - 1;
        } else {
          _controller.text = value.substring(0, start) +
              value.substring(end, value.length);
          offset = start;
        }
        _controller.selection =
            TextSelection.fromPosition(TextPosition(offset: offset));
        widget.onTextChanged(_controller.text);
      }
    }
  }

  Widget _buildButton(String text,
      {VoidCallback? onPressed,
      VoidCallback? onLongPressStart,
      VoidCallback? onLongPressUp}) {
    return Expanded(
      child: Container(
        color: Colors.white,
        child: GestureDetector(
          onLongPressStart: (e) {
            onLongPressStart?.call();
          },
          onLongPressUp: onLongPressUp,
          child: TextButton(
            onPressed: onPressed ?? () => _handleType(text),
            child: Text(
              text,
              style: const TextStyle(color: Colors.black, fontSize: 16),
            ),
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return _buildButtonKeyboard();
  }

  Widget _buildButtonRow(String key1, String key2, String key3) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        _horizontalPadding,
        _buildButton(key1),
        _horizontalPadding,
        _buildButton(key2),
        _horizontalPadding,
        _buildButton(key3),
        _horizontalPadding,
      ],
    );
  }

  Widget _buildButtonKeyboard() {
    return Container(
      color: const Color(0xffdddddd),
      child: Column(
        children: [
          _verticalPadding,
          _buildButtonRow('A', 'B', 'C'),
          _verticalPadding,
          _buildButtonRow('D', 'E', 'F'),
          _verticalPadding,
          _buildButtonRow('1', '2', '3'),
          _verticalPadding,
          _buildButtonRow('4', '5', '6'),
          _verticalPadding,
          _buildButtonRow('7', '8', '9'),
          _verticalPadding,
          Row(
            children: [
              _horizontalPadding,
              _buildButton(
                '⌫',
                onPressed: _handleBackspace,
                onLongPressStart: () {
                  _timer =
                      Timer.periodic(const Duration(milliseconds: 50), (timer) {
                    _handleBackspace();
                  });
                },
                onLongPressUp: () {
                  _timer?.cancel();
                },
              ),
              _horizontalPadding,
              _buildButton('0'),
              _horizontalPadding,
              _buildButton(
                'Done',
                onPressed: widget.onDone,
              ),
              _horizontalPadding,
            ],
          ),
          _verticalPadding,
        ],
      ),
    );
  }
}

 hex_input_screen.dart

import 'package:flutter/material.dart';

class HexInputScreen extends StatefulWidget {
  final String text;

  const HexInputScreen({super.key, required this.text});

  @override
  State<HexInputScreen> createState() => _HexInputScreenState();
}

class _HexInputScreenState extends State<HexInputScreen> {
  late TextEditingController _controller;
  final FocusNode _focus = FocusNode();
  late ValueNotifier<bool> _focusValueNotifier;
  int _byteCount = 0;

  int _toByteCount(String hex) {
    return hex.length % 2 == 0 ? hex.length ~/ 2 : hex.length ~/ 2 + 1;
  }

  void _onTextChanged(String text) {
    //更新字节数
    setState(() {
      _byteCount = _toByteCount(text);
    });
  }

  @override
  void initState() {
    _controller = TextEditingController(text: widget.text);
    _focus.addListener(_handleFocusChange);
    _focusValueNotifier = ValueNotifier<bool>(_focus.hasFocus);
    _focus.requestFocus();
    setState(() {
      _byteCount = widget.text.length;
    });
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
    _focus.removeListener(_handleFocusChange);
    _focus.dispose();
  }

  void _handleFocusChange() {
    _focusValueNotifier.value = _focus.hasFocus;
  }

  void _onDone() {
    print(_controller.text);
    Navigator.pop(context, _controller.text);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('HEX' /*, style: TextStyle(color: Colors.white)*/),
        // backgroundColor: Colors.black,
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        children: [
          const SizedBox(height: 10),
          Text('已输入 $_byteCount 字节'),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: TextField(
              decoration: const InputDecoration(
                border: OutlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.grey,
                    width: 1,
                  ),
                ),
              ),
              controller: _controller,
              keyboardType: TextInputType.none,
              focusNode: _focus,
              maxLines: 12,
              maxLength: 1024,
              onChanged: _onTextChanged,//这里监听粘贴剪切导致的变化
            ),
          ),
          const Spacer(),
          ListenableBuilder(
            listenable: _focusValueNotifier,
            builder: (BuildContext context, Widget? child) {
              return _focus.hasFocus
                  ? HexKeyboard(
                      controller: _controller,
                      onDone: _onDone,
                      onTextChanged: _onTextChanged,//这里监听自定义键盘导致的变化
                    )
                  : Container();
            },
          ),
        ],
      ),
    );
  }
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Flutter中,可以使用自定义Widget来实现自定义键盘。以下是实现自定义键盘的一般步骤: 1. 创建一个StatefulWidget,该Widget将显示自定义键盘。 2. 实现一个键盘布局。可以使用Flutter提供的各种Widget(如Container、Row、Column等)来创建键盘布局,并使用GestureDetector Widget来处理键盘按钮的点击事件。 3. 在需要使用自定义键盘的地方,使用Flutter的FocusNode来控制输入框的焦点,并将自定义键盘与输入框关联起来。 下面是一个简单的实现自定义键盘的示例代码: ```dart import 'package:flutter/material.dart'; class CustomKeyboard extends StatefulWidget { @override _CustomKeyboardState createState() => _CustomKeyboardState(); } class _CustomKeyboardState extends State<CustomKeyboard> { TextEditingController _textEditingController = TextEditingController(); @override Widget build(BuildContext context) { return Container( child: Column( children: [ TextField( controller: _textEditingController, focusNode: FocusNode(), decoration: InputDecoration( hintText: 'Input something', ), ), Row( children: [ CustomButton('1'), CustomButton('2'), CustomButton('3'), ], ), Row( children: [ CustomButton('4'), CustomButton('5'), CustomButton('6'), ], ), Row( children: [ CustomButton('7'), CustomButton('8'), CustomButton('9'), ], ), Row( children: [ CustomButton('0'), CustomButton('backspace'), ], ), ], ), ); } } class CustomButton extends StatelessWidget { final String text; CustomButton(this.text); @override Widget build(BuildContext context) { return GestureDetector( onTap: () { if (text == 'backspace') { String currentText = CustomKeyboard.of(context) .controller .text; if (currentText.isNotEmpty) { CustomKeyboard.of(context) .controller .text = currentText.substring(0, currentText.length - 1); } } else { CustomKeyboard.of(context) .controller .text += text; } }, child: Container( width: 100.0, height: 80.0, decoration: BoxDecoration( border: Border.all( color: Colors.grey, ), ), child: Center( child: Text(text), ), ), ); } } class CustomKeyboardController { final TextEditingController controller; CustomKeyboardController(this.controller); void clear() { controller.clear(); } void close() { FocusScope.of(controller.context).requestFocus(FocusNode()); } } class CustomKeyboardScope extends InheritedWidget { final CustomKeyboardController controller; CustomKeyboardScope({ Key key, @required Widget child, }) : assert(child != null), controller = CustomKeyboardController(TextEditingController()), super(key: key, child: child); static CustomKeyboardController of(BuildContext context) { return (context.dependOnInheritedWidgetOfExactType<CustomKeyboardScope>()) .controller; } @override bool updateShouldNotify(CustomKeyboardScope oldWidget) { return controller != oldWidget.controller; } } ``` 在上面的示例代码中,`CustomKeyboard` Widget是自定义键盘的主Widget,它包含了一个`TextField`和一些自定义的按钮。 `CustomButton` Widget是自定义键盘中的按键,它使用`GestureDetector`来处理按钮点击事件。 `CustomKeyboardController`是自定义键盘的控制器,用于控制输入框的文本内容。 最后,使用`CustomKeyboardScope` Widget来将`CustomKeyboardController`注入到自定义键盘中。 在需要使用自定义键盘的地方,可以使用以下代码来显示自定义键盘: ```dart CustomKeyboardScope( child: CustomKeyboard(), ); ``` 同时,需要将`CustomKeyboardController`与输入框关联起来,可以使用以下代码: ```dart TextField( controller: CustomKeyboardScope.of(context).controller.textEditingController, focusNode: FocusNode(), ); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值