【问题标题】:How to update Pin on keyboard press?如何在键盘按下时更新 Pin?
【发布时间】:2021-03-09 16:58:32
【问题描述】:

如何使用屏幕键盘的输入更新 PinInput 框?据我了解,只要状态发生变化,小部件就会被重建。因此,从下面开始,我所做的就是在屏幕键盘检测到点击时更新文本。然后由于状态发生了变化,我假设它将重建所有包含 PinInput 小部件的小部件,这是正确的,因为每当有变化时我都会测试文本。然后我做了 _pinPutController.text = text;更改 PinInput 的输入,但它不起作用。

当我硬编码 _pinPutController.text = '123' 时,它可以工作。所以问题是它没有重建。我是否正确理解这一点?我怎样才能实现我想要的?

import 'package:flutter/material.dart';
import 'package:numeric_keyboard/numeric_keyboard.dart';
import 'package:pinput/pin_put/pin_put.dart';

import '../../../../constants.dart';
import '../../../../size_config.dart';

class InputForm extends StatefulWidget {
  @override
  _InputFormState createState() => _InputFormState();
}

class _InputFormState extends State<InputForm> {
  String text = '';

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        PinInput(text: text),
        NumericKeyboard(
            onKeyboardTap: (value) {
              setState(() {
                text += value;
              });
            },
            textColor: Colors.red,
            rightButtonFn: () {
              setState(() {
                text = text.substring(0, text.length - 1);
              });
            },
            rightIcon: Icon(
              Icons.backspace,
              color: Colors.red,
            ),
            mainAxisAlignment: MainAxisAlignment.spaceEvenly),
      ],
    );
  }
}

class PinInput extends StatelessWidget {
  const PinInput({
    Key key,
    this.text,
  }) : super(key: key);

  final String text;

  @override
  Widget build(BuildContext context) {
    final size = getProportionateScreenHeight(60);
    final TextEditingController _pinPutController = TextEditingController();
    final FocusNode _pinPutFocusNode = FocusNode();
    _pinPutFocusNode.unfocus();
    print(text);
    _pinPutController.text = text;

    return PinPut(
      fieldsCount: 4,
      onSubmit: (String pin) => {},
      focusNode: _pinPutFocusNode,
      controller: _pinPutController,
      preFilledWidget: Align(
        alignment: Alignment.bottomCenter,
        child: Divider(
          color: kPrimaryColor,
          thickness: 2.5,
          indent: 7.5,
          endIndent: 7.5,
        ),
      ),
      textStyle: TextStyle(
        fontSize: getProportionateScreenHeight(24),
      ),
      eachFieldPadding: EdgeInsets.all(
        getProportionateScreenHeight(10),
      ),
      eachFieldMargin: EdgeInsets.all(
        getProportionateScreenWidth(5),
      ),
      eachFieldHeight: size,
      eachFieldWidth: size,
      submittedFieldDecoration: boxDecoration(),
      selectedFieldDecoration: boxDecoration(),
      followingFieldDecoration: boxDecoration(),
      inputDecoration: InputDecoration(
        border: InputBorder.none,
        focusedBorder: InputBorder.none,
        enabledBorder: InputBorder.none,
        counterText: '',
      ),
      withCursor: true,
      pinAnimationType: PinAnimationType.scale,
      animationDuration: kAnimationDuration,
    );
  }

  BoxDecoration boxDecoration() {
    return BoxDecoration(
      color: Colors.white,
      shape: BoxShape.rectangle,
      borderRadius: BorderRadius.circular(
        getProportionateScreenWidth(10),
      ),
    );
  }
}

【问题讨论】:

    标签: flutter dart flutter-layout


    【解决方案1】:

    问题是您在每次重建PinInput 小部件时都重新创建一个新的TextEditingController。但是,如果您检查pinput packagePinPutState,它会保留对您在其initState 方法中提供的第一个TextEditingController 的引用:

    @override
    void initState() {
      _controller = widget.controller ?? TextEditingController();
      [...]
    }
    

    所以,你必须一直保持相同的TextEditingController

    解决此问题的最简单方法是将TextEditingController 提高到InputForm 的状态。而不是String text,只需使用控制器:

    class InputForm extends StatefulWidget {
      @override
      _InputFormState createState() => _InputFormState();
    }
    
    class _InputFormState extends State<InputForm> {
      TextEditingController _controller = TextEditingController(text: '');
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: [
            PinInput(controller: _controller),
            NumericKeyboard(
              onKeyboardTap: (value) => _controller.text += value,
              textColor: Colors.red,
              rightButtonFn: () => _controller.text =
                  _controller.text.substring(0, _controller.text.length - 1),
              rightIcon: Icon(Icons.backspace, color: Colors.red),
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            ),
          ],
        );
      }
    }
    

    注意:由于您使用TextEditingController 而不是String,因此您可以摆脱所有setState 方法。

    完整源代码:

    import 'package:flutter/material.dart';
    import 'package:numeric_keyboard/numeric_keyboard.dart';
    import 'package:pinput/pin_put/pin_put.dart';
    
    void main() {
      runApp(
        MaterialApp(
          debugShowCheckedModeBanner: false,
          title: 'Flutter Demo',
          home: HomePage(),
        ),
      );
    }
    
    class HomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(child: InputForm()),
        );
      }
    }
    
    class InputForm extends StatefulWidget {
      @override
      _InputFormState createState() => _InputFormState();
    }
    
    class _InputFormState extends State<InputForm> {
      TextEditingController _controller = TextEditingController(text: '');
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: [
            PinInput(controller: _controller),
            NumericKeyboard(
              onKeyboardTap: (value) => _controller.text += value,
              textColor: Colors.red,
              rightButtonFn: () => _controller.text =
                  _controller.text.substring(0, _controller.text.length - 1),
              rightIcon: Icon(Icons.backspace, color: Colors.red),
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            ),
          ],
        );
      }
    }
    
    class PinInput extends StatelessWidget {
      const PinInput({
        Key key,
        this.controller,
      }) : super(key: key);
    
      final TextEditingController controller;
    
      @override
      Widget build(BuildContext context) {
        final size = 60.0;
        final FocusNode _pinPutFocusNode = FocusNode();
        _pinPutFocusNode.unfocus();
    
        return PinPut(
          fieldsCount: 4,
          onSubmit: (String pin) => {},
          focusNode: _pinPutFocusNode,
          controller: controller,
          preFilledWidget: Align(
            alignment: Alignment.bottomCenter,
            child: Divider(
              color: Theme.of(context).primaryColor,
              thickness: 2.5,
              indent: 7.5,
              endIndent: 7.5,
            ),
          ),
          textStyle: TextStyle(
            fontSize: 24,
          ),
          eachFieldPadding: EdgeInsets.all(
            10,
          ),
          eachFieldMargin: EdgeInsets.all(
            5,
          ),
          eachFieldHeight: size,
          eachFieldWidth: size,
          submittedFieldDecoration: boxDecoration(),
          selectedFieldDecoration: boxDecoration(),
          followingFieldDecoration: boxDecoration(),
          inputDecoration: InputDecoration(
            border: InputBorder.none,
            focusedBorder: InputBorder.none,
            enabledBorder: InputBorder.none,
            counterText: '',
          ),
          withCursor: true,
          pinAnimationType: PinAnimationType.scale,
          animationDuration: Duration(milliseconds: 500),
        );
      }
    
      BoxDecoration boxDecoration() {
        return BoxDecoration(
          color: Colors.white,
          shape: BoxShape.rectangle,
          borderRadius: BorderRadius.circular(
            10,
          ),
        );
      }
    }
    

    更新:如何隐藏键盘...

    ...同时保持焦点闪烁光标

    1。禁用对 PinPut 字段的关注:

    为此,我使用了 here 描述的类:

    class AlwaysDisabledFocusNode extends FocusNode {
      @override
      bool get hasFocus => false;
    }
    

    ...作为focusNodePinPut

    class PinInput extends StatelessWidget {
      [...]
      @override
      Widget build(BuildContext context) {
        final size = 60.0;
        final FocusNode _pinPutFocusNode = AlwaysDisabledFocusNode();
        // _pinPutFocusNode.unfocus();
    
        return PinPut(
          [...]
          focusNode: _pinPutFocusNode,
          [...]
        );
      }
    }
    

    所以,现在,PinPut 永远不会成为焦点。软键盘没有显示,但是,嘿!,我们失去了闪烁的光标!

    不用担心,我们会以编程方式使其始终保持开启状态。

    2。保持闪烁的光标一直亮着

    不过,为此,我们必须更改pinput package 的代码。

    目前,在PinPutState 中,如果withCursor 设置为true 并且PinPut 具有焦点,则闪烁的光标会显示在下一个字段中。相反,如果 withCursor 设置为 true,我们将始终显示闪烁的光标:

    if (widget.withCursor /* && _focusNode!.hasFocus */ && index == pin.length) {
      return _buildCursor();
    }
    

    瞧!对你有用吗?

    PinPut GitHub 问题跟踪器 [ref] 中提到了有关在使用自定义键盘时禁用设备键盘的问题。

    【讨论】:

    • 感谢@Thierry 简洁明了的回答。另一个问题是如何禁用每次有焦点时出现的操作系统键盘?自动对焦应该保持真实,但只是没有操作系统键盘
    • 我可能有一个解决方案。
    猜你喜欢
    • 2019-11-19
    • 1970-01-01
    • 2015-10-01
    • 1970-01-01
    • 2021-04-21
    • 2014-03-11
    • 1970-01-01
    • 2021-02-06
    • 1970-01-01
    相关资源
    最近更新 更多