flutter - 在 flutter 中制作电话验证文本字段 UI

标签 flutter uitextfield

我正在制作一个 UI,其中包含对电话号码的验证。它有四个代码需要输入,您将通过我们后端的通知获得这些代码。由于我负责为此制作 UI,所以它真的不适合我。

这是我想要实现的用户界面:

Phone Verification TextField

我已尽最大努力实现这一目标,但每次都未能实现。

代码

Container(
   height: 64.0,
   width: 56.0,
   child: Card(
       color: Color.fromRGBO(173, 179, 191, 0.7),
       child: Padding(
            padding: EdgeInsets.only(left: 10.0, right: 10.0),
            child: TextEditorForPhoneVerify(this.codeOne)
       )
   )
)

我有自己的输入小部件:

class TextEditorForPhoneVerify extends StatelessWidget {
   final TextEditingController code;

   TextEditorForPhoneVerify(this.code);

   @override
   Widget build(BuildContext context) {
     return TextField(
       textAlign: TextAlign.center,
       keyboardType: TextInputType.number,
       controller: this.code,
       maxLength: 1,
       cursorColor: Theme.of(context).primaryColor,
       decoration: InputDecoration(
         hintText: "*",
         counterText: '',
         hintStyle: TextStyle(color: Colors.black, fontSize: 20.0)
       )
    );
  }
}

现在每次我使用 textAlign: TextAlgn.center 时,它都会崩溃,而且我的其他文本字段也会崩溃。我已经注意到不要对所有人使用相同的小部件,它们都有不同的小部件。当我热重启它时,它可以工作。但是这个 textAlign 有问题。

我真的很想实现这个,用上面的代码我得到了这个:

结果

Resultant

当我尝试将光标置于中心时出现的错误是:

flutter: ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
flutter: The following assertion was thrown during performLayout():
flutter: Please see the documentation for computeDistanceToActualBaseline for the required calling
flutter: conventions of this method.
flutter: 'package:flutter/src/rendering/box.dart': Failed assertion: line 1642 pos 12: '!_debugDoingBaseline'
flutter:
flutter: Either the assertion indicates an error in the framework itself, or we should provide substantially
flutter: more information in this error message to help you determine and fix the underlying cause.
flutter: In either case, please report this assertion by filing a bug on GitHub:
flutter:   https://github.com/flutter/flutter/issues/new?template=BUG.md
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #2      RenderBox.getDistanceToBaseline (package:flutter/src/rendering/box.dart:1642:12)
flutter: #3      _RenderDecoration._layout.layoutLineBox (package:flutter/src/material/input_decorator.dart:820:35)
flutter: #4      _RenderDecoration._layout (package:flutter/src/material/input_decorator.dart:857:18)
flutter: #5      _RenderDecoration.performLayout (package:flutter/src/material/input_decorator.dart:987:44)
flutter: #6      RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #7      _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #8      RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #9      _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #10     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #11     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #12     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #13     RenderPadding.performLayout (package:flutter/src/rendering/shifted_box.dart:199:11)
flutter: #14     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #15     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #16     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #17     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #18     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #19     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #20     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #21     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #22     _RenderCustomClip.performLayout (package:flutter/src/rendering/proxy_box.dart:1206:11)
flutter: #23     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #24     RenderPadding.performLayout (package:flutter/src/rendering/shifted_box.dart:199:11)
flutter: #25     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #26     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #27     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #28     RenderConstrainedBox.performLayout (package:flutter/src/rendering/proxy_box.dart:259:13)
flutter: #29     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #30     RenderFlex.performLayout (package:flutter/src/rendering/flex.dart:738:15)
flutter: #31     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #32     RenderFlex.performLayout (package:flutter/src/rendering/flex.dart:738:15)
flutter: #33     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #34     MultiChildLayoutDelegate.layoutChild (package:flutter/src/rendering/custom_layout.dart:142:11)
flutter: #35     _ScaffoldLayout.performLayout (package:flutter/src/material/scaffold.dart:350:7)
flutter: #36     MultiChildLayoutDelegate._callPerformLayout (package:flutter/src/rendering/custom_layout.dart:212:7)
flutter: #37     RenderCustomMultiChildLayoutBox.performLayout (package:flutter/src/rendering/custom_layout.dart:356:14)
flutter: #38     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #39     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #40     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #41     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #42     _RenderCustomClip.performLayout (package:flutter/src/rendering/proxy_box.dart:1206:11)
flutter: #43     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #44     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #45     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #46     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #47     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #48     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #49     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #50     RenderStack.performLayout (package:flutter/src/rendering/stack.dart:510:15)
flutter: #51     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #52     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #53     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #54     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #55     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #56     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #57     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #58     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #59     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #60     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #61     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #62     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #63     RenderOffstage.performLayout (package:flutter/src/rendering/proxy_box.dart:3032:13)
flutter: #64     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #65     RenderStack.performLayout (package:flutter/src/rendering/stack.dart:510:15)
flutter: #66     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #67     __RenderTheatre&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #68     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #69     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #70     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #71     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #72     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #73     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #74     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #75     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #76     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #77     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #78     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #79     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #80     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #81     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #82     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #83     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #84     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #85     RenderStack.performLayout (package:flutter/src/rendering/stack.dart:510:15)
flutter: #86     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #87     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #88     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #89     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #90     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #91     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #92     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #93     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #94     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #95     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #96     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)

Reloaded 0 of 567 libraries in 2,311ms.
flutter: #97     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #98     RenderOffstage.performLayout (package:flutter/src/rendering/proxy_box.dart:3032:13)
flutter: #99     RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #100    RenderStack.performLayout (package:flutter/src/rendering/stack.dart:510:15)
flutter: #101    RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #102    __RenderTheatre&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #103    RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #104    _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #105    RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #106    _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #107    RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #108    _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #109    RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #110    _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #111    RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #112    _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
flutter: #113    RenderObject.layout (package:flutter/src/rendering/object.dart:1632:7)
flutter: #114    RenderView.performLayout (package:flutter/src/rendering/view.dart:151:13)
flutter: #115    RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:1507:7)
flutter: #116    PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:766:18)
flutter: #117    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:329:19)
flutter: #118    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:701:13)
flutter: #119    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:268:5)
flutter: #120    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:988:15)
flutter: #121    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:928:9)
flutter: #122    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:749:7)
flutter: #124    _Timer._runTimers (dart:isolate/runtime/libtimer_impl.dart:382:19)
flutter: #125    _Timer._handleMessage (dart:isolate/runtime/libtimer_impl.dart:416:5)
flutter: #126    _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)
flutter: (elided 3 frames from class _AssertionError and package dart:async)
flutter:
flutter: The following RenderObject was being processed when the exception was fired:
flutter:   _RenderDecoration#10671 NEEDS-LAYOUT NEEDS-PAINT
flutter:   creator: _Decorator ← InputDecorator ← AnimatedBuilder ← Listener ← RawGestureDetector ←
flutter:   GestureDetector ← TextSelectionGestureDetector ← IgnorePointer ← Semantics ← TextField ←
flutter:   TextEditorForPhoneVerify ← Padding ← ⋯
flutter:   parentData: <none> (can use size)
flutter:   constraints: BoxConstraints(w=28.0, h=56.0)
flutter:   size: Size(28.0, 56.0)
flutter: This RenderObject had the following descendants (showing up to depth 5):
flutter:   RenderRepaintBoundary#9838d relayoutBoundary=up1 NEEDS-LAYOUT NEEDS-PAINT
flutter:     RenderPointerListener#95bb3 relayoutBoundary=up2 NEEDS-LAYOUT NEEDS-PAINT
flutter:       RenderSemanticsAnnotations#1d581 relayoutBoundary=up3 NEEDS-LAYOUT NEEDS-PAINT
flutter:         RenderIgnorePointer#7b37c relayoutBoundary=up4 NEEDS-LAYOUT NEEDS-PAINT
flutter:           RenderLeaderLayer#d035d relayoutBoundary=up5 NEEDS-LAYOUT NEEDS-PAINT
flutter:   RenderAnimatedOpacity#5b708 relayoutBoundary=up1 NEEDS-PAINT
flutter:     RenderParagraph#62bf0 relayoutBoundary=up2 NEEDS-PAINT
flutter:   RenderConstrainedBox#fb712 relayoutBoundary=up1 NEEDS-LAYOUT NEEDS-PAINT
flutter:   RenderCustomPaint#7c081 NEEDS-LAYOUT NEEDS-PAINT
flutter: ════════════════════════════════════════════════════════════════════════════════════════════════════
flutter: Another exception was thrown: Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.
flutter: Another exception was thrown: Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.
flutter: Another exception was thrown: Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.
flutter: Another exception was thrown: Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.
flutter: Another exception was thrown: Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.
flutter: Another exception was thrown: Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.
flutter: Another exception was thrown: Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.

另外,我也对这个更感兴趣:一旦输入数字,光标就应该自动转到下一个,甚至不需要按键盘上的任何键。正如您在大多数验证码布局中看到的那样。

请帮忙,因为我已经尝试了我的水平来达到一个水平,但无法得到想要的结果。谢谢

最佳答案

我做了自己的UI,喜欢就用吧,也可以根据自己的需要定制

import 'dart:async';

import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class Otp extends StatefulWidget {
  final String email;
  final String newEmail;
  final bool isGuestCheckOut;

  const Otp({
    Key key,
    @required this.email,
    this.newEmail = "",
    this.isGuestCheckOut,
  }) : super(key: key);

  @override
  _OtpState createState() => new _OtpState();
}

class _OtpState extends State<Otp> with SingleTickerProviderStateMixin {
  // Constants
  final int time = 30;
  AnimationController _controller;

  // Variables
  Size _screenSize;
  int _currentDigit;
  int _firstDigit;
  int _secondDigit;
  int _thirdDigit;
  int _fourthDigit;

  Timer timer;
  int totalTimeInSeconds;
  bool _hideResendButton;

  String userName = "";
  bool didReadNotifications = false;
  int unReadNotificationsCount = 0;

  // Returns "Appbar"
  get _getAppbar {
    return new AppBar(
      backgroundColor: Colors.transparent,
      elevation: 0.0,
      leading: new InkWell(
        borderRadius: BorderRadius.circular(30.0),
        child: new Icon(
          Icons.arrow_back,
          color: Colors.black54,
        ),
        onTap: () {
          Navigator.pop(context);
        },
      ),
      centerTitle: true,
    );
  }

  // Return "Verification Code" label
  get _getVerificationCodeLabel {
    return new Text(
      "Verification Code",
      textAlign: TextAlign.center,
      style: new TextStyle(
          fontSize: 28.0, color: Colors.black, fontWeight: FontWeight.bold),
    );
  }

  // Return "Email" label
  get _getEmailLabel {
    return new Text(
      "Please enter the OTP sent\non your registered Email ID.",
      textAlign: TextAlign.center,
      style: new TextStyle(
          fontSize: 18.0, color: Colors.black, fontWeight: FontWeight.w600),
    );
  }

  // Return "OTP" input field
  get _getInputField {
    return new Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: <Widget>[
        _otpTextField(_firstDigit),
        _otpTextField(_secondDigit),
        _otpTextField(_thirdDigit),
        _otpTextField(_fourthDigit),
      ],
    );
  }

  // Returns "OTP" input part
  get _getInputPart {
    return new Column(
      mainAxisSize: MainAxisSize.max,
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
        _getVerificationCodeLabel,
        _getEmailLabel,
        _getInputField,
        _hideResendButton ? _getTimerText : _getResendButton,
        _getOtpKeyboard
      ],
    );
  }

  // Returns "Timer" label
  get _getTimerText {
    return Container(
      height: 32,
      child: new Offstage(
        offstage: !_hideResendButton,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Icon(Icons.access_time),
            new SizedBox(
              width: 5.0,
            ),
            OtpTimer(_controller, 15.0, Colors.black)
          ],
        ),
      ),
    );
  }

  // Returns "Resend" button
  get _getResendButton {
    return new InkWell(
      child: new Container(
        height: 32,
        width: 120,
        decoration: BoxDecoration(
            color: Colors.black,
            shape: BoxShape.rectangle,
            borderRadius: BorderRadius.circular(32)),
        alignment: Alignment.center,
        child: new Text(
          "Resend OTP",
          style:
              new TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
        ),
      ),
      onTap: () {
        // Resend you OTP via API or anything
      },
    );
  }

  // Returns "Otp" keyboard
  get _getOtpKeyboard {
    return new Container(
        height: _screenSize.width - 80,
        child: new Column(
          children: <Widget>[
            new Expanded(
              child: new Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: <Widget>[
                  _otpKeyboardInputButton(
                      label: "1",
                      onPressed: () {
                        _setCurrentDigit(1);
                      }),
                  _otpKeyboardInputButton(
                      label: "2",
                      onPressed: () {
                        _setCurrentDigit(2);
                      }),
                  _otpKeyboardInputButton(
                      label: "3",
                      onPressed: () {
                        _setCurrentDigit(3);
                      }),
                ],
              ),
            ),
            new Expanded(
              child: new Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: <Widget>[
                  _otpKeyboardInputButton(
                      label: "4",
                      onPressed: () {
                        _setCurrentDigit(4);
                      }),
                  _otpKeyboardInputButton(
                      label: "5",
                      onPressed: () {
                        _setCurrentDigit(5);
                      }),
                  _otpKeyboardInputButton(
                      label: "6",
                      onPressed: () {
                        _setCurrentDigit(6);
                      }),
                ],
              ),
            ),
            new Expanded(
              child: new Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: <Widget>[
                  _otpKeyboardInputButton(
                      label: "7",
                      onPressed: () {
                        _setCurrentDigit(7);
                      }),
                  _otpKeyboardInputButton(
                      label: "8",
                      onPressed: () {
                        _setCurrentDigit(8);
                      }),
                  _otpKeyboardInputButton(
                      label: "9",
                      onPressed: () {
                        _setCurrentDigit(9);
                      }),
                ],
              ),
            ),
            new Expanded(
              child: new Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: <Widget>[
                  new SizedBox(
                    width: 80.0,
                  ),
                  _otpKeyboardInputButton(
                      label: "0",
                      onPressed: () {
                        _setCurrentDigit(0);
                      }),
                  _otpKeyboardActionButton(
                      label: new Icon(
                        Icons.backspace,
                        color: Colors.black,
                      ),
                      onPressed: () {
                        setState(() {
                          if (_fourthDigit != null) {
                            _fourthDigit = null;
                          } else if (_thirdDigit != null) {
                            _thirdDigit = null;
                          } else if (_secondDigit != null) {
                            _secondDigit = null;
                          } else if (_firstDigit != null) {
                            _firstDigit = null;
                          }
                        });
                      }),
                ],
              ),
            ),
          ],
        ));
  }

  // Overridden methods
  @override
  void initState() {
    totalTimeInSeconds = time;
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: time))
          ..addStatusListener((status) {
            if (status == AnimationStatus.dismissed) {
              setState(() {
                _hideResendButton = !_hideResendButton;
              });
            }
          });
    _controller.reverse(
        from: _controller.value == 0.0 ? 1.0 : _controller.value);
    _startCountdown();
  }

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

  @override
  Widget build(BuildContext context) {
    _screenSize = MediaQuery.of(context).size;
    return new Scaffold(
      appBar: _getAppbar,
      backgroundColor: Colors.white,
      body: new Container(
        width: _screenSize.width,
//        padding: new EdgeInsets.only(bottom: 16.0),
        child: _getInputPart,
      ),
    );
  }

  // Returns "Otp custom text field"
  Widget _otpTextField(int digit) {
    return new Container(
      width: 35.0,
      height: 45.0,
      alignment: Alignment.center,
      child: new Text(
        digit != null ? digit.toString() : "",
        style: new TextStyle(
          fontSize: 30.0,
          color: Colors.black,
        ),
      ),
      decoration: BoxDecoration(
//            color: Colors.grey.withOpacity(0.4),
          border: Border(
              bottom: BorderSide(
        width: 2.0,
        color: Colors.black,
      ))),
    );
  }

  // Returns "Otp keyboard input Button"
  Widget _otpKeyboardInputButton({String label, VoidCallback onPressed}) {
    return new Material(
      color: Colors.transparent,
      child: new InkWell(
        onTap: onPressed,
        borderRadius: new BorderRadius.circular(40.0),
        child: new Container(
          height: 80.0,
          width: 80.0,
          decoration: new BoxDecoration(
            shape: BoxShape.circle,
          ),
          child: new Center(
            child: new Text(
              label,
              style: new TextStyle(
                fontSize: 30.0,
                color: Colors.black,
              ),
            ),
          ),
        ),
      ),
    );
  }

  // Returns "Otp keyboard action Button"
  _otpKeyboardActionButton({Widget label, VoidCallback onPressed}) {
    return new InkWell(
      onTap: onPressed,
      borderRadius: new BorderRadius.circular(40.0),
      child: new Container(
        height: 80.0,
        width: 80.0,
        decoration: new BoxDecoration(
          shape: BoxShape.circle,
        ),
        child: new Center(
          child: label,
        ),
      ),
    );
  }

  // Current digit
  void _setCurrentDigit(int i) {
    setState(() {
      _currentDigit = i;
      if (_firstDigit == null) {
        _firstDigit = _currentDigit;
      } else if (_secondDigit == null) {
        _secondDigit = _currentDigit;
      } else if (_thirdDigit == null) {
        _thirdDigit = _currentDigit;
      } else if (_fourthDigit == null) {
        _fourthDigit = _currentDigit;

        var otp = _firstDigit.toString() +
            _secondDigit.toString() +
            _thirdDigit.toString() +
            _fourthDigit.toString();

        // Verify your otp by here. API call
      }
    });
  }

  Future<Null> _startCountdown() async {
    setState(() {
      _hideResendButton = true;
      totalTimeInSeconds = time;
    });
    _controller.reverse(
        from: _controller.value == 0.0 ? 1.0 : _controller.value);
  }

  void clearOtp() {
    _fourthDigit = null;
    _thirdDigit = null;
    _secondDigit = null;
    _firstDigit = null;
    setState(() {});
  }
}

class OtpTimer extends StatelessWidget {
  final AnimationController controller;
  double fontSize;
  Color timeColor = Colors.black;

  OtpTimer(this.controller, this.fontSize, this.timeColor);

  String get timerString {
    Duration duration = controller.duration * controller.value;
    if (duration.inHours > 0) {
      return '${duration.inHours}:${duration.inMinutes % 60}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}';
    }
    return '${duration.inMinutes % 60}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}';
  }

  Duration get duration {
    Duration duration = controller.duration;
    return duration;
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
        animation: controller,
        builder: (BuildContext context, Widget child) {
          return new Text(
            timerString,
            style: new TextStyle(
                fontSize: fontSize,
                color: timeColor,
                fontWeight: FontWeight.w600),
          );
        });
  }
}

enter image description here

关于flutter - 在 flutter 中制作电话验证文本字段 UI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55453821/

相关文章:

firebase - Flutter CloudFirestorePlugin.java 使用未经检查或不安全的操作。注意 : Recompile with -Xlint:unchecked for details

flutter - 返回 Flutter 中的 widget 列表

ios - 如何在 uiAlert Popup 中的输入字段的开头添加永久字符?

iOS - 可编辑 subview (TextView、标签等)返回 null

ios - 如何获取 UITextField 中插入符的坐标(即框架矩形)?

ios - 使用 UITextView 的子类和自定义屏幕键盘,调用 textField.textShouldChange

mobile - Flutter - 单击时渲染新的小部件

dart - 如何垂直扩展具有一个ListView的ListView

exception - 如何在 flutter 中捕获异步函数的异常?

iphone - resignFirstResponder 未按预期工作