pageview中的扑面回调导致异常
我正在尝试创建一个可以将小部件加载到另一个文件中定义的小部件的页面浏览量。 This works fine, except when I try to add a callback, I get the following error:
FlutterError (setState() or markNeedsBuild() called during build.
This error is triggered when the email entered is considered to be valid (that is, when email_entry.dart中的代码调用从account_onboard..dart文件传递的回调函数。 to Dart/Flutter, so I'm hoping someone can point out what's happening (and a fix) here.
Here is my code:
-Parent widget, account_onboarding.dart
import 'package:flutter/material.dart';
import 'package:page_view_indicators/page_view_indicators.dart';
import 'package:animated_title_screen/screens/email_entry.dart';
class AccountOnboarding extends StatefulWidget {
const AccountOnboarding({Key? key}) : super(key: key);
@override
State<AccountOnboarding> createState() => _AccountOnboardingState();
}
class _AccountOnboardingState extends State<AccountOnboarding> {
final _pController = PageController(initialPage: 0);
final _currentPageNotifier = ValueNotifier<int>(0);
final List<Widget> _pages = [];
bool validEmail = false; //Callback should set this variable
@override
void initState() {
super.initState();
_pages.add( //Add the EmailEntry widget to the list
EmailEntry(emailIsValid: (p0) {
setState(() {
validEmail = p0;
});
},),
);
_pages.add(
Container(
color: Colors.blue,
child: Text("Pg2"),
),
);
_pages.add(
Container(
color: Colors.green,
child: Text("Pg3"),
),
);
}
@override
void dispose() {
_pController.dispose();
_currentPageNotifier.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Create Account",
style: Theme.of(context).textTheme.headline5,
),
centerTitle: true,
actions: [
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
],
),
body: Stack(
fit: StackFit.expand,
children: [
Column(
children: [
Row(
children: [
Text(
"Step ${_currentPageNotifier.value + 1} of ${_pages.length}",
),
CirclePageIndicator(
dotColor: const Color(0xFF323232),
selectedDotColor: const Color(0xFFE4231F),
size: 10,
selectedSize: 10,
currentPageNotifier: _currentPageNotifier,
itemCount: _pages.length,
),
],
),
PageView(
controller: _pController,
onPageChanged: (index) {
setState(() {
_currentPageNotifier.value = index;
});
},
children: [
for (Widget p in _pages) p, //Display all pages in _pages[]
],
),
ElevatedButton(
child: const Text("Continue"),
onPressed: () => print("Pressed 2"),
style: ElevatedButton.styleFrom(
primary: validEmail ? const Color(0xFFE1251B) : Colors.black,
textStyle: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
],
),
],
),
);
}
}
Here is the email_entry.dart code:
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class EmailEntry extends StatefulWidget {
final Function(bool) emailIsValid; //Function required in constructor
const EmailEntry({Key? key, required this.emailIsValid}) : super(key: key);
@override
State<EmailEntry> createState() => _EmailEntryState();
}
class _EmailEntryState extends State<EmailEntry> {
final _emailController = TextEditingController();
FocusNode _emailFocus = FocusNode();
@override
void initState() {
super.initState();
_emailController.addListener(() => setState(() {}));
_emailFocus.addListener(() {
print("Focus email");
});
}
@override
void dispose() {
_emailController.dispose();
super.dispose();
}
bool validateEmail(String email) {
bool valid = RegExp(
r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*$")
.hasMatch(email);
if (valid) {
widget.emailIsValid(true); //Call the callback function
}
return valid;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Column(
children: [
Text(
"YOUR EMAIL",
style: Theme.of(context).textTheme.headline2,
),
Text(
"Please use an email address that you would like to make your login.",
style: Theme.of(context).textTheme.bodyText2,
textAlign: TextAlign.center,
),
Container(
child: Text(
"Email Address",
),
),
TextField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
focusNode: _emailFocus,
suffixIcon: getTextFieldSuffix(emailController, _emailFocus), //OFFENDING CODE
),
],
),
],
),
);
}
//THIS FUNCTION CAUSED THE ISSUE. It is code I got from a youtube //tutorial. Probably should have guessed.
Widget getTextFieldSuffix(TextEditingController controller, FocusNode node) {
if (controller.text.isNotEmpty && node.hasFocus) {
return IconButton(
color: Colors.grey.withAlpha(150),
onPressed: () => controller.clear(),
icon: const Icon(Icons.close));
} else if (controller.text.isNotEmpty && !node.hasFocus) {
return const Icon(
Icons.check,
color: Colors.green,
);
}
return Container(
width: 0,
);
}
}
I'm trying to create a pageview that I can load a widget into that is defined in another file. This works fine, except when I try to add a callback, I get the following error:
FlutterError (setState() or markNeedsBuild() called during build.
This error is triggered when the email entered is considered to be valid (that is, when the code in the email_entry.dart calls the callback function that was passed from the account_onboarding.dart file.) I haven't been able to determine why this is happening, and no tutorials on this subject seem to exist. I am still pretty new to Dart/Flutter, so I'm hoping someone can point out what's happening (and a fix) here.
Here is my code:
-Parent widget, account_onboarding.dart
import 'package:flutter/material.dart';
import 'package:page_view_indicators/page_view_indicators.dart';
import 'package:animated_title_screen/screens/email_entry.dart';
class AccountOnboarding extends StatefulWidget {
const AccountOnboarding({Key? key}) : super(key: key);
@override
State<AccountOnboarding> createState() => _AccountOnboardingState();
}
class _AccountOnboardingState extends State<AccountOnboarding> {
final _pController = PageController(initialPage: 0);
final _currentPageNotifier = ValueNotifier<int>(0);
final List<Widget> _pages = [];
bool validEmail = false; //Callback should set this variable
@override
void initState() {
super.initState();
_pages.add( //Add the EmailEntry widget to the list
EmailEntry(emailIsValid: (p0) {
setState(() {
validEmail = p0;
});
},),
);
_pages.add(
Container(
color: Colors.blue,
child: Text("Pg2"),
),
);
_pages.add(
Container(
color: Colors.green,
child: Text("Pg3"),
),
);
}
@override
void dispose() {
_pController.dispose();
_currentPageNotifier.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Create Account",
style: Theme.of(context).textTheme.headline5,
),
centerTitle: true,
actions: [
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
],
),
body: Stack(
fit: StackFit.expand,
children: [
Column(
children: [
Row(
children: [
Text(
"Step ${_currentPageNotifier.value + 1} of ${_pages.length}",
),
CirclePageIndicator(
dotColor: const Color(0xFF323232),
selectedDotColor: const Color(0xFFE4231F),
size: 10,
selectedSize: 10,
currentPageNotifier: _currentPageNotifier,
itemCount: _pages.length,
),
],
),
PageView(
controller: _pController,
onPageChanged: (index) {
setState(() {
_currentPageNotifier.value = index;
});
},
children: [
for (Widget p in _pages) p, //Display all pages in _pages[]
],
),
ElevatedButton(
child: const Text("Continue"),
onPressed: () => print("Pressed 2"),
style: ElevatedButton.styleFrom(
primary: validEmail ? const Color(0xFFE1251B) : Colors.black,
textStyle: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
],
),
],
),
);
}
}
Here is the email_entry.dart code:
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class EmailEntry extends StatefulWidget {
final Function(bool) emailIsValid; //Function required in constructor
const EmailEntry({Key? key, required this.emailIsValid}) : super(key: key);
@override
State<EmailEntry> createState() => _EmailEntryState();
}
class _EmailEntryState extends State<EmailEntry> {
final _emailController = TextEditingController();
FocusNode _emailFocus = FocusNode();
@override
void initState() {
super.initState();
_emailController.addListener(() => setState(() {}));
_emailFocus.addListener(() {
print("Focus email");
});
}
@override
void dispose() {
_emailController.dispose();
super.dispose();
}
bool validateEmail(String email) {
bool valid = RegExp(
r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*quot;)
.hasMatch(email);
if (valid) {
widget.emailIsValid(true); //Call the callback function
}
return valid;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Column(
children: [
Text(
"YOUR EMAIL",
style: Theme.of(context).textTheme.headline2,
),
Text(
"Please use an email address that you would like to make your login.",
style: Theme.of(context).textTheme.bodyText2,
textAlign: TextAlign.center,
),
Container(
child: Text(
"Email Address",
),
),
TextField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
focusNode: _emailFocus,
suffixIcon: getTextFieldSuffix(emailController, _emailFocus), //OFFENDING CODE
),
],
),
],
),
);
}
//THIS FUNCTION CAUSED THE ISSUE. It is code I got from a youtube //tutorial. Probably should have guessed.
Widget getTextFieldSuffix(TextEditingController controller, FocusNode node) {
if (controller.text.isNotEmpty && node.hasFocus) {
return IconButton(
color: Colors.grey.withAlpha(150),
onPressed: () => controller.clear(),
icon: const Icon(Icons.close));
} else if (controller.text.isNotEmpty && !node.hasFocus) {
return const Icon(
Icons.check,
color: Colors.green,
);
}
return Container(
width: 0,
);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在
initstate
中,您需要调用addpostFrameCallback
。像这样...
in
initState
,you need to calladdPostFrameCallback
.like this...
我发现我的生产版本中有一些代码,每当用户进入文本字段以获取电子邮件地址时,都会调用重新绘制。这引起了问题,因为屏幕已经被重新绘制了,我叫SetState再次重新绘制它。我将编辑上面显示的代码以包括有问题的代码。
I found out that there is some code in my production version that calls a redraw every time the user enters a letter into the textfield for the email address. This was causing a problem because the screen was already being redrawn, and I was calling setState to redraw it again. I will edit the code shown above to include the offending code.