Flutter :setState() 或 markNeedsBuild() 在构建期间调用

Posted

技术标签:

【中文标题】Flutter :setState() 或 markNeedsBuild() 在构建期间调用【英文标题】:Flutter :setState() or markNeedsBuild() called during build 【发布时间】:2021-01-06 19:42:58 【问题描述】:

我是 Flutter 的新手,我已经为此尝试了所有解决方案!我不知道还能做什么!我正在尝试获取共享首选项字符串,但是当我这样做时,应用程序崩溃了,我不知道为什么! 我想这可能是因为弯曲的导航栏,我做错了什么吗? 它一直显示此错误:

The following assertion was thrown building Builder:
setState() or markNeedsBuild() called during build.

This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets.  A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: Overlay-[LabeledGlobalKey<OverlayState>#19d5d]
  state: OverlayState#13f72(entries: [OverlayEntry#3b74c(opaque: true; maintainState: false), OverlayEntry#91b13(opaque: false; maintainState: true), OverlayEntry#b0313(opaque: false; maintainState: false), OverlayEntry#03775(opaque: false; maintainState: true), OverlayEntry#304cd(opaque: false; maintainState: false), OverlayEntry#7aa01(opaque: false; maintainState: true)])
The widget which was currently being built when the offending call was made was: Builder
The relevant error-causing widget was: 
  MaterialApp file:///C:/Users/MuneF/Desktop/mscmu/lib/main.dart:30:10
When the exception was thrown, this was the stack: 
#0      Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:4167:11)
#1      Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4182:6)
#2      State.setState (package:flutter/src/widgets/framework.dart:1253:14)
#3      OverlayState.rearrange (package:flutter/src/widgets/overlay.dart:415:5)
#4      NavigatorState._flushHistoryUpdates (package:flutter/src/widgets/navigator.dart:3069:16)
...

我不知道为什么会这样。 我的完整代码是这样的: main.dart:

import 'package:curved_navigation_bar/curved_navigation_bar.dart';
import 'package:flutter/material.dart';
import 'package:custom_splash/custom_splash.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'info_card.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:async';
import 'dart:core';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:io';
import 'package:flutter_custom_clippers/flutter_custom_clippers.dart';
import 'package:form_field_validator/form_field_validator.dart';
import 'package:data_connection_checker/data_connection_checker.dart';
import 'package:giffy_dialog/giffy_dialog.dart';
import 'package:bottom_navy_bar/bottom_navy_bar.dart';

String token = '';
String type = '';
String name = '';
Future<void> main() async 
 WidgetsFlutterBinding.ensureInitialized();
 SharedPreferences preferences = await SharedPreferences.getInstance();
 token = preferences.getString('Loggedin');
 type = preferences.getString('type');
 name = preferences.getString('name');
 print(token);
 print(type);
 runApp(MaterialApp(

   home: SplashScreen(),
 ));



class SplashScreen extends StatefulWidget 
 @override
 _SplashScreenState createState() => _SplashScreenState();


class _SplashScreenState extends State<SplashScreen> 
 @override
 Widget build(BuildContext context) 
   return CustomSplash(
     imagePath: 'images/logo.png',
     backGroundColor: Color(0xff131535),
     animationEffect: 'zoom-out',
     logoSize: 200,
     home: token == null ? Login() : Home(),
     duration: 4000,
   );
 


class Intro extends StatefulWidget 
 @override
 _IntroState createState() => _IntroState();


class _IntroState extends State<Intro> 
 @override
 Widget build(BuildContext context) 
   return Container();
 


class Home extends StatefulWidget 
 @override
 _HomeState createState() => _HomeState();


class _HomeState extends State<Home> 
 @override
 void initState() 
   if (type == 'representative') 
     Navigator.pushReplacement(
         context, MaterialPageRoute(builder: (context) => RepresentativeUi()));
    else if (type == 'student') 
     Navigator.pushReplacement(
         context, MaterialPageRoute(builder: (context) => StudentUi()));
   
   super.initState();
 

 @override
 Widget build(BuildContext context) 
   return Container(
     child: Text('hello'),
   );
 


class Login extends StatefulWidget 
 @override
 _LoginState createState() => _LoginState();


class _LoginState extends State<Login> 
 TextEditingController email = TextEditingController();
 TextEditingController pass = TextEditingController();

 final _key = GlobalKey<FormState>();
 bool _secureText = true;
 showHide() 
   setState(() 
     _secureText = !_secureText;
   );
 

 check() 
   final form = _key.currentState;
   if (form.validate()) 
     form.save();
     _login();
   
 

 Future<List> _login() async 
   final response = await http.post('http://msc-mu.com/login_app.php', body: 
     'email': email.text,
     'password': pass.text,
   );
   final userdata = json.decode(response.body);
   String emailAPI = userdata[0]['email'];
   String nameAPI = userdata[0]['name'];
   String id = userdata[0]['id'];
   String year = userdata[0]['year'];
   String type = userdata[0]['type'];
   setState(() 
     savePref(token, emailAPI, nameAPI, id, year, type);
     print(savePref(token, emailAPI, nameAPI, id, year, type));
   );
   if (userdata.length == 0) 
     setState(() 
       toast('Login Failed');
     );
    else if (type == 'representative') 
     toast('Successfully Logged in');
     Navigator.pushReplacement(
         context, MaterialPageRoute(builder: (context) => RepresentativeUi()));
    else if (type == 'student') 
     toast('Successfully Logged in');
     Navigator.pushReplacement(
         context, MaterialPageRoute(builder: (context) => StudentUi()));
   
   return userdata;
 

 String token = 'Logged in';
 savePref(String token, String email, String name, String id, String year,
     String type) async 
   SharedPreferences preferences = await SharedPreferences.getInstance();
   setState(() 
     preferences.setString('Loggedin', token);
     preferences.setString('name', name);
     preferences.setString('email', email);
     preferences.setString('id', id);
     preferences.setString('year', year);
     preferences.setString('type', type);

     preferences.commit();
   );
 

 @override
 void initState() 
   var listener = DataConnectionChecker();
   listener.onStatusChange.listen((status) 
     switch (status) 
       case DataConnectionStatus.connected:
         toast('Connected');
         break;
       case DataConnectionStatus.disconnected:
         showDialog(
             context: context,
             builder: (context) => AssetGiffyDialog(
               image: Image.asset('images/wait_connection.gif'),
               title: Text(
                 'Your are not Connected',
                 style: TextStyle(
                     fontSize: 22.0, fontWeight: FontWeight.w600),
               ),
               description: Text(
                 'please reconnect your phone to the internet and press OK',
                 textAlign: TextAlign.center,
               ),
               entryAnimation: EntryAnimation.TOP_RIGHT,
               onOkButtonPressed: () 
                 setState(() 
                   void check() async 
                     try 
                       final result =
                       await InternetAddress.lookup('google.com');
                       if (result.isNotEmpty &&
                           result[0].rawAddress.isNotEmpty) 
                         Navigator.pop(context, true);
                       
                      on SocketException catch (_) 
                       toast('Still not connected');
                     
                   

                   check();
                 );
               ,
               onCancelButtonPressed: () ,
             ));
         break;
     
   );
   super.initState();
 

 @override
 Widget build(BuildContext context) 
   return Scaffold(
     body: SingleChildScrollView(
       child: Container(
         height: MediaQuery.of(context).size.height,
         child: Column(
           children: <Widget>[
             ClipPath(
               clipper: OvalBottomBorderClipper(),
               child: Image(
                 image: AssetImage('images/logo1.png'),
                 width: double.infinity,
                 height: MediaQuery.of(context).size.height / 3.5,
                 fit: BoxFit.cover,
               ),
             ),
             SizedBox(
               height: 10.0,
             ),
             Form(
               key: _key,
               child: Column(
                 mainAxisAlignment: MainAxisAlignment.center,
                 children: <Widget>[
                   SizedBox(
                     height: 80.0,
                   ),
                   Card(
                     elevation: 6.0,
                     child: TextFormField(
                       controller: email,
                       style: TextStyle(
                         color: Colors.black,
                         fontSize: 16,
                         fontWeight: FontWeight.w300,
                       ),
                       decoration: InputDecoration(
                           prefixIcon: Padding(
                             padding: EdgeInsets.only(left: 20, right: 15),
                             child: Icon(Icons.person, color: Colors.black),
                           ),
                           contentPadding: EdgeInsets.all(18),
                           labelText: "Email"),
                     ),
                   ),
                   SizedBox(
                     height: 12.0,
                   ),
                   Card(
                     elevation: 6.0,
                     child: TextFormField(
                       validator: MultiValidator([
                         RequiredValidator(errorText: 'Password is required'),
                         MinLengthValidator(8,
                             errorText:
                             'Password must be at least 8 digits long')
                       ]),
                       obscureText: _secureText,
                       controller: pass,
                       style: TextStyle(
                         color: Colors.black,
                         fontSize: 16,
                         fontWeight: FontWeight.w300,
                       ),
                       decoration: InputDecoration(
                         labelText: "Password",
                         prefixIcon: Padding(
                           padding: EdgeInsets.only(left: 20, right: 15),
                           child:
                           Icon(Icons.phonelink_lock, color: Colors.black),
                         ),
                         suffixIcon: IconButton(
                           onPressed: showHide,
                           icon: Icon(_secureText
                               ? Icons.visibility_off
                               : Icons.visibility),
                         ),
                         contentPadding: EdgeInsets.all(18),
                       ),
                     ),
                   ),
                   SizedBox(
                     height: 50.0,
                   ),
                   Row(
                     mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                     children: [
                       SizedBox(
                         width: 150.0,
                         height: 44.0,
                         child: RaisedButton(
                             shape: RoundedRectangleBorder(
                                 borderRadius: BorderRadius.circular(15.0)),
                             child: Text(
                               "Login",
                               style: TextStyle(fontSize: 18.0),
                             ),
                             textColor: Colors.white,
                             color: Theme.of(context).primaryColor,
                             onPressed: () 
                               setState(() 
                                 check();
                               );
                             ),
                       ),
                       SizedBox(
                         width: 150.0,
                         height: 44.0,
                         child: RaisedButton(
                             shape: RoundedRectangleBorder(
                                 borderRadius: BorderRadius.circular(15.0)),
                             child: Text(
                               "GoTo Register",
                               style: TextStyle(fontSize: 18.0),
                             ),
                             textColor: Colors.white,
                             color: Theme.of(context).primaryColor,
                             onPressed: () 
                               Navigator.push(
                                 context,
                                 MaterialPageRoute(
                                     builder: (context) => Register()),
                               );
                             ),
                       ),
                     ],
                   ),
                 ],
               ),
             ),
           ],
         ),
       ),
     ),
   );
 


class Register extends StatefulWidget 
 @override
 _RegisterState createState() => _RegisterState();


class _RegisterState extends State<Register> 
 TextEditingController email = TextEditingController();
 TextEditingController password = TextEditingController();
 TextEditingController name = TextEditingController();
 TextEditingController year = TextEditingController();
 TextEditingController type = TextEditingController();
 final _key = GlobalKey<FormState>();
 bool _secureText = true;
 showHide() 
   setState(() 
     _secureText = !_secureText;
   );
 

 check() 
   final form = _key.currentState;
   if (form.validate()) 
     form.save();
     register();
   
 

 void register() async 
   http.post('http://msc-mu.com/register.php', body: 
     'email': email.text,
     'password': password.text,
     'name': name.text,
     'year': year.text,
     'type': type.text
   );
 

 String _selectYear;

 List years = List();

 Future getYears() async 
   final response = await http.get('http://msc-mu.com/getYears.php');
   var resBody = json.decode(response.body);
   setState(() 
     years = resBody;
   );
 

 String _selectType;

 List types = List();

 Future getTypes() async 
   var response = await http.post('http://msc-mu.com/getlevel.php');
   var resBody = json.decode(response.body);
   setState(() 
     types = resBody;
   );
 

 @override
 void initState() 
   super.initState();
   this.getYears();
   this.getTypes();
   year.text = _selectYear;
   type.text = _selectType;
 

 @override
 Widget build(BuildContext context) 
   return Scaffold(
     body: SingleChildScrollView(
       child: Container(
         height: MediaQuery.of(context).size.height,
         child: Column(
           children: <Widget>[
             ClipPath(
               clipper: OvalBottomBorderClipper(),
               child: Image(
                 image: AssetImage('images/logo1.png'),
                 width: double.infinity,
                 height: MediaQuery.of(context).size.height / 3.5,
                 fit: BoxFit.cover,
               ),
             ),
             Form(
               key: _key,
               child: Column(
                 mainAxisAlignment: MainAxisAlignment.center,
                 children: <Widget>[
                   SizedBox(
                     height: 25.0,
                   ),
                   Card(
                     elevation: 6.0,
                     child: TextFormField(
                       controller: name,
                       validator: MinLengthValidator(8,
                           errorText: ('Name must be at least 8 digit long')),
                       style: TextStyle(
                         color: Colors.black,
                         fontSize: 16,
                         fontWeight: FontWeight.w300,
                       ),
                       decoration: InputDecoration(
                           prefixIcon: Padding(
                             padding: EdgeInsets.only(left: 20, right: 15),
                             child: Icon(Icons.person, color: Colors.black),
                           ),
                           contentPadding: EdgeInsets.all(18),
                           labelText: "FullName"),
                     ),
                   ),
                   Card(
                     elevation: 6.0,
                     child: TextFormField(
                       controller: email,
                       validator: EmailValidator(
                           errorText: 'Please enter a valid email address'),
                       style: TextStyle(
                         color: Colors.black,
                         fontSize: 16,
                         fontWeight: FontWeight.w300,
                       ),
                       decoration: InputDecoration(
                           prefixIcon: Padding(
                             padding: EdgeInsets.only(left: 20, right: 15),
                             child: Icon(Icons.email, color: Colors.black),
                           ),
                           contentPadding: EdgeInsets.all(18),
                           labelText: "Email"),
                     ),
                   ),
                   Card(
                     elevation: 6.0,
                     child: TextFormField(
                       controller: password,
                       validator: MultiValidator([
                         RequiredValidator(errorText: 'Password is Required'),
                         MinLengthValidator(8,
                             errorText:
                             'Password must be at least 8 digit long')
                       ]),
                       obscureText: _secureText,
                       style: TextStyle(
                         color: Colors.black,
                         fontSize: 16,
                         fontWeight: FontWeight.w300,
                       ),
                       decoration: InputDecoration(
                           suffixIcon: IconButton(
                             onPressed: showHide,
                             icon: Icon(_secureText
                                 ? Icons.visibility_off
                                 : Icons.visibility),
                           ),
                           prefixIcon: Padding(
                             padding: EdgeInsets.only(left: 20, right: 15),
                             child: Icon(Icons.phonelink_lock,
                                 color: Colors.black),
                           ),
                           contentPadding: EdgeInsets.all(18),
                           labelText: "Password"),
                     ),
                   ),
                   Row(
                     mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                     children: <Widget>[
                       Card(
                         elevation: 6.0,
                         child: DropdownButton<String>(
                           hint: Text('Choose your role'),
                           icon: Icon(Icons.arrow_drop_down),
                           iconSize: 24.0,
                           elevation: 16,
                           style:
                           TextStyle(color: Colors.black, fontSize: 16.0),
                           underline: Container(
                             height: 2,
                             color: Theme.of(context).primaryColor,
                           ),
                           items: types.map((item) 
                             return DropdownMenuItem(
                               child: Text(item['Levelname']),
                               value: item['Levelname'].toString(),
                             );
                           ).toList(),
                           onChanged: (newVal) 
                             setState(() 
                               _selectType = newVal;
                               type.text = _selectType;
                             );
                           ,
                           value: _selectType,
                         ),
                       ),
                       Card(
                         elevation: 6.0,
                         child: DropdownButton<String>(
                           hint: Text('Choose your Year'),
                           icon: Icon(Icons.arrow_drop_down),
                           iconSize: 24.0,
                           elevation: 16,
                           style:
                           TextStyle(color: Colors.black, fontSize: 16.0),
                           underline: Container(
                             height: 2,
                             color: Theme.of(context).primaryColor,
                           ),
                           items: years.map((item) 
                             return DropdownMenuItem(
                               child: Text(item['name']),
                               value: item['name'].toString(),
                             );
                           ).toList(),
                           onChanged: (newVal) 
                             setState(() 
                               _selectYear = newVal;
                               year.text = _selectYear;
                             );
                           ,
                           value: _selectYear,
                         ),
                       ),
                     ],
                   ),
                   SizedBox(
                     height: 20.0,
                   ),
                   Row(
                     mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                     children: [
                       SizedBox(
                         width: 150.0,
                         height: 44.0,
                         child: RaisedButton(
                             shape: RoundedRectangleBorder(
                                 borderRadius: BorderRadius.circular(15.0)),
                             child: Text(
                               "Register",
                               style: TextStyle(fontSize: 18.0),
                             ),
                             textColor: Colors.white,
                             color: Theme.of(context).primaryColor,
                             onPressed: () 
                               setState(() 
                                 check();
                                 toast(
                                     'Successfully Registered , Can you please Login again ? ');
                                 Navigator.pushReplacement(
                                     context,
                                     MaterialPageRoute(
                                         builder: (context) => Login()));
                               );
                             ),
                       ),
                       SizedBox(
                         width: 150.0,
                         height: 44.0,
                         child: RaisedButton(
                             shape: RoundedRectangleBorder(
                                 borderRadius: BorderRadius.circular(15.0)),
                             child: Text(
                               "GoTo Login",
                               style: TextStyle(fontSize: 18.0),
                             ),
                             textColor: Colors.white,
                             color: Theme.of(context).primaryColor,
                             onPressed: () 
                               setState(() 
                                 Navigator.pushReplacement(
                                     context,
                                     MaterialPageRoute(
                                         builder: (context) => Login()));
                               );
                             ),
                       ),
                     ],
                   ),
                 ],
               ),
             ),
           ],
         ),
       ),
     ),
   );
 


class RepresentativeUi extends StatefulWidget 
 @override
 _RepresentativeUiState createState() => _RepresentativeUiState();


class _RepresentativeUiState extends State<RepresentativeUi> 
 int _currentIndex = 0;
 // navy bottom pages
 final _newsFeedPage = RNewsFeed();
 final _profilePage = RProfile();
 final _addPostPage = RAddPost();
 final _libraryPage = RLibrary();
 Widget _showPage = RNewsFeed();
 Widget _pageChooser(int page) 
   switch (page) 
     case 0:
       return _newsFeedPage;
       break;
     case 1:
       return _addPostPage;
       break;
     case 2:
       return _libraryPage;
       break;
     case 3:
       return _profilePage;
       break;
   
 

 @override
 Widget build(BuildContext context) 
   return Scaffold(
     bottomNavigationBar: CurvedNavigationBar(
       index: _currentIndex,
       items: <Widget>[
         Icon(
           FontAwesomeIcons.newspaper,
           size: 30,
         ),
         Icon(
           FontAwesomeIcons.plus,
           size: 30,
         ),
         Icon(
           FontAwesomeIcons.book,
           size: 30,
         ),
         Icon(
           FontAwesomeIcons.user,
           size: 30,
         ),
       ],
       color: Colors.white,
       buttonBackgroundColor: Colors.white,
       backgroundColor: Color(0xff131535),
       animationCurve: Curves.easeIn,
       animationDuration: Duration(milliseconds: 400),
       onTap: (int tappedIndex) 
         setState(() 
           _showPage = _pageChooser(tappedIndex);
         );
       ,
     ),
     body: Container(
       child: _showPage,
     ),
   );
 


class RProfile extends StatefulWidget 
 @override
 _RProfileState createState() => _RProfileState();



class _RProfileState extends State<RProfile> 
 String _name ,_email , _class , _level ;
 getPref() async 
   SharedPreferences preferences = await SharedPreferences.getInstance();
   setState(() 
     _name = preferences.getString('name');
     _email = preferences.getString('email');
     _class = preferences.getString('year');
     _level = preferences.getString('level');

   );
    
 @override
 void initState() 
   super.initState();
   getPref();
 
 @override
 Widget build(BuildContext context) 
   return Scaffold(
     body: SafeArea(
       child: Column(
      
           ),
           Text(
             '_name',
             style: TextStyle(
               fontSize: 40.0,
               color: Colors.white,
               fontWeight: FontWeight.bold,
               fontFamily: 'Pacifico',
             ),
    


如果问题与 initstat 和 setstate 有关,为什么它之前没有出现任何错误,正如你所看到的,我之前做过几次! 那我该怎么办?

【问题讨论】:

【参考方案1】:

您在构建完成之前正在做某事。在做某事之前,我们必须等待构建完成......

.without code sn-p cant pin point .so 一旦构建完成,我该怎么做。 你可以使用

addPostFrameCallback 方法

此回调在一帧期间运行,就在持久帧回调之后(即刷新主渲染管道时)。如果帧正在进行中并且帧后回调尚未执行,则注册的回调仍在该帧期间执行。否则,注册的回调将在下一帧执行。

回调按照添加的顺序执行。

后帧回调不能取消注册。它们只被调用一次。

对你的initState进行如下修改

WidgetsBinding.instance.addPostFrameCallback((_)
      if (type == 'representative') 
     Navigator.pushReplacement(
         context, MaterialPageRoute(builder: (context) => RepresentativeUi()));
    else if (type == 'student') 
     Navigator.pushReplacement(
         context, MaterialPageRoute(builder: (context) => StudentUi()));
   
  );

你可以看看这个blog by diddier

【讨论】:

感谢您说清楚,但我能知道错误到底在哪里,为什么它告诉我它在 main 的材料应用程序中? 我已经更新了我的答案,同时检查答案中发布的链接将回答为什么 非常感谢,那代码是我唯一的问题吗? 尚未检查所有行,我会检查并回复您,如果对您有用,请标记为答案。谢谢 你成就了我的一天!

以上是关于Flutter :setState() 或 markNeedsBuild() 在构建期间调用的主要内容,如果未能解决你的问题,请参考以下文章

Flutter:在构建期间调用 setState() 或 markNeedsBuild()

在构建期间调用 setState() 或 markNeedsBuild() - Flutter

Flutter:在构建错误期间调用了 setState() 或 markNeedsBuild()

Flutter :setState() 或 markNeedsBuild() 在构建期间调用

Flutter 错误 - 在构建期间调用了 setState() 或 markNeedsBuild()

Flutter VideoPlayerController “在构建期间调用了 setState() 或 markNeedsBuild()。”