显示和隐藏小部件给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)
]),
),
],
),
),
),
);
}

我不知道为什么会发生这种情况,所以我需要帮助。谢谢你。

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

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

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

});
});

如果您还没有这样做,请将其包含在main函数中:

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

最新更新