首页 > 解决方案 > 显示和隐藏小部件在构建错误期间调用 SetState() 或 markNeedsBuild()

问题描述

我在隐藏和显示小部件时遇到了一些麻烦。所以,我有一个 OTP 验证页面来验证发送给用户的代码。在该页面中,当用户单击文本按钮重新发送 otp 代码时,该按钮将被隐藏并由倒数计时器替换。

         showCountdown ?
              Center(
                child: CountdownTimer(
                  textStyle: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
                  endTime: endTime,
                  onEnd: () {
                    setState(() {
                      showCountdown = false;
                    });
                  },
                ),
              )
             :
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text(
                    "Tidak Menerima Kode Verifikasi? ",
                    style: TextStyle(color: Colors.black54, fontSize: 15),
                  ),
                  TextButton(
                    onPressed: () {
                      // userService.resendOtp(widget.phoneNumber.toString()).then((value) => snackBar(value));
                      setState(() {
                        showCountdown = true;
                        clicked++;
                      });
                    },
                    child: Text(
                      "Kirim Ulang",
                      style: TextStyle(
                          color: Constant.color,
                          fontWeight: FontWeight.bold,
                          fontSize: 16),
                    )
                  )
                ],
              ),

第一次点击没问题,第二次点击就报这个错误

The following assertion was thrown building NotificationListener<KeepAliveNotification>:
setState() or markNeedsBuild() called during build.

它表明错误可能在这部分

 onEnd: () {
    setState(() {
      showCountdown = false;
    });
 },

构建方法的完整代码

Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        onTap: () {},
        child: Container(
          height: MediaQuery.of(context).size.height,
          width: MediaQuery.of(context).size.width,
          decoration: BoxDecoration(
              image: DecorationImage(
                  image: AssetImage("assets/images/Background.png"),
                  alignment: Alignment.center,
                  fit: BoxFit.cover
              )
          ),
          child: ListView(
            children: <Widget>[
              // SizedBox(height: 30),
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 8.0),
                child: Text(
                  'Verifikasi Nomor HP',
                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 22),
                  textAlign: TextAlign.center,
                ),
              ),
              Padding(
                padding:
                const EdgeInsets.symmetric(horizontal: 30.0, vertical: 8),
                child: RichText(
                  text: TextSpan(
                      text: "Kode Dikirim Ke Nomor ",
                      children: [
                        TextSpan(
                            text: "${widget.phoneNumber}",
                            style: TextStyle(
                                color: Colors.black,
                                fontWeight: FontWeight.bold,
                                fontSize: 15)),
                      ],
                      style: TextStyle(color: Colors.black54, fontSize: 15)),
                  textAlign: TextAlign.center,
                ),
              ),
              SizedBox(
                height: 20,
              ),
              Form(
                key: formKey,
                child: Padding(
                    padding: const EdgeInsets.symmetric(
                        vertical: 8.0, horizontal: 30),
                    child: PinCodeTextField(
                      appContext: context,
                      pastedTextStyle: TextStyle(
                        color: Constant.color,
                        fontWeight: FontWeight.bold,
                      ),
                      length: 6,
                      obscureText: false,
                      obscuringCharacter: '*',
                      blinkWhenObscuring: true,
                      animationType: AnimationType.fade,
                      validator: (v) {
                        if (v!.length < 6) {
                          return "Harap Masukan Kode OTP Yang Benar";
                        } else {
                          return null;
                        }
                      },
                      pinTheme: PinTheme(
                        shape: PinCodeFieldShape.box,
                        borderRadius: BorderRadius.circular(5),
                        fieldHeight: 50,
                        fieldWidth: 40,
                        activeFillColor: Colors.white,
                      ),
                      cursorColor: Colors.black,
                      animationDuration: Duration(milliseconds: 300),
                      errorAnimationController: errorController,
                      controller: textEditingController,
                      keyboardType: TextInputType.number,
                      boxShadows: [
                        BoxShadow(
                          offset: Offset(0, 1),
                          color: Colors.black12,
                          blurRadius: 10,
                        )
                      ],
                      onCompleted: (v) {
                        print("Completed");
                      },
                      onChanged: (value) {
                        print(value);
                        setState(() {
                          currentText = value;
                        });
                      },
                      enablePinAutofill: true,
                      beforeTextPaste: (text) {
                        print("Allowing to paste $text");
                        //if you return true then it will show the paste confirmation dialog. Otherwise if false, then nothing will happen.
                        //but you can show anything you want here, like your pop up saying wrong paste format or etc
                        return true;
                      },
                    )),
              ),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 30.0),
                child: Text(
                  hasError ? "*Please fill up all the cells properly" : "",
                  style: TextStyle(
                      color: Colors.white,
                      fontSize: 12,
                      fontWeight: FontWeight.w400),
                ),
              ),
              SizedBox(
                height: 20,
              ),
              showCountdown ?
              Center(
                child: CountdownTimer(
                  textStyle: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
                  endTime: endTime,
                  onEnd: () {
                    setState(() {
                      showCountdown = false;
                    });
                  },
                ),
              )
             :
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text(
                    "Tidak Menerima Kode Verifikasi? ",
                    style: TextStyle(color: Colors.black54, fontSize: 15),
                  ),
                  TextButton(
                    onPressed: () {
                      // userService.resendOtp(widget.phoneNumber.toString()).then((value) => snackBar(value));
                      setState(() {
                        showCountdown = true;
                        clicked++;
                      });
                    },
                    child: Text(
                      "Kirim Ulang",
                      style: TextStyle(
                          color: Constant.color,
                          fontWeight: FontWeight.bold,
                          fontSize: 16),
                    )
                  )
                ],
              ),
              SizedBox(
                height: 14,
              ),
              Container(
                margin:
                const EdgeInsets.symmetric(vertical: 16.0, horizontal: 30),
                child: ButtonTheme(
                  height: 50,
                  child: TextButton(
                    onPressed: () {
                      formKey.currentState!.validate();
                      // conditions for validating
                      if (currentText.length != 6 ) {
                        errorController!.add(ErrorAnimationType.shake); // Triggering error shake animation
                        setState(() => hasError = true);
                      } else {
                        userService.verifyOtp(widget.phoneNumber.toString(), currentText).then((value) {
                          if(value == "Kode OTP Salah") {
                            errorController!.add(ErrorAnimationType.shake);
                            textEditingController.clear();
                            setState(() {
                              hasError = true;
                              snackBar(value);
                            });
                          } else {
                            setState(() {
                              hasError = false;
                              snackBar(value);
                            });
                            Future.delayed(Duration(seconds: 3)).then( (value) =>
                                Navigator.of(context).pushReplacement(
                                    new MaterialPageRoute(builder: (context) => new SignIn()))
                            );
                          }
                        });
                      }
                    },
                    child: Center(
                        child: Text(
                          "Verifikasi Kode OTP".toUpperCase(),
                          style: TextStyle(
                              color: Colors.white,
                              fontSize: 18,
                              fontWeight: FontWeight.bold),
                        )),
                  ),
                ),
                decoration: BoxDecoration(
                    color: Constant.color,
                    borderRadius: BorderRadius.circular(5),
                    boxShadow: [
                      BoxShadow(
                          color: Colors.green.shade200,
                          offset: Offset(1, -2),
                          blurRadius: 5),
                      BoxShadow(
                          color: Colors.green.shade200,
                          offset: Offset(-1, 2),
                          blurRadius: 5)
                    ]),
              ),
            ],
          ),
        ),
      ),
    );
  }

我不知道为什么会这样,所以我需要帮助。谢谢你。

标签: flutter

解决方案


这个错误意味着 Flutter Engine 被指示构建一些东西(setState例如使用),但此时另一个构建已经在进行中。

所以你必须找出你的哪些setState语句导致了这个错误,它可能是 at onEnd,你可以调试你的代码来确定。你可以添加一个回调,它将在当前构建完成后执行:

WidgetsBinding.instance.addPostFrameCallback((_){
    setState(() {
      
    });
});

如果您还没有完成,请将其包含在您的main函数中:

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp()); // or whatever you app class name is
}

推荐阅读