无法将小部件标记为需要构建,因为框架已经在构建小部件的过程中

Posted

技术标签:

【中文标题】无法将小部件标记为需要构建,因为框架已经在构建小部件的过程中【英文标题】:Widget cannot be marked as needing to build because the framework is already in the process of building widgets 【发布时间】:2020-07-06 05:42:57 【问题描述】:

我正在尝试实现 Provider,它似乎工作正常,但我收到以下消息:

这个 _DefaultInheritedProviderScope 小部件不能 标记为需要构建,因为框架已经在 构建小部件的过程。可以将小部件标记为需要 仅当其祖先之一当前处于构建阶段时才构建 建筑。允许此异常,因为框架构建 孩子之前的父小部件,这意味着脏后代将 总是被建造。否则,框架可能不会访问这个小部件 在此构建阶段。 setState() 或 markNeedsBuild() 被称为: _DefaultInheritedProviderScope 值:“UserProfile”实例监听值当前小部件 发出违规调用时正在构建的是:FutureBuilder 脏状态:_FutureBuilderState#bf6ec 异常发生时 抛出,这是堆栈:

0 元素.markNeedsBuild。 (包:flutter/src/widgets/framework.dart:3896:11)

1 Element.markNeedsBuild(包:flutter/src/widgets/framework.dart:3911:6)

2 _InheritedProviderScopeMixin.markNeedsNotifyDependents(包:provider/src/inherited_provider.dart:268:5)

3 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:206:21)

4 UserProfile.user= (package:mdd/core/services/user_info.dart:13:5) ... UserProfile

发送通知是:'UserProfile'的实例

我的代码如下:

class HomePage extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    final authService = Provider.of<AuthService>(context);
    final userProfile =    Provider.of<UserProfile>(context);

    return StatefulWrapper(
      onInit: () 
        FirebaseNotifications().setUpFirebase();
      ,
      child: FutureBuilder<User>(
        future: authService.getUser(),
        builder: (context, AsyncSnapshot<User> snapshot) 
          if (snapshot.connectionState == ConnectionState.done) 
            if (snapshot.error != null) 
              return Text(snapshot.error.toString());
            
            userProfile.user = snapshot.data;
           // FirebaseUser user = snapshot.data;

            return snapshot.hasData ? ListScreen() : LoginScreen();
           else 
            return Scaffold(
              appBar: AppBar(),
              body: Container(),
            );
          
        ,
      )
    );
  

这是 UserProfile 类:

class UserProfile with ChangeNotifier 
  User _user = User();

  get user 
    return _user;
  

  set user(User user) 
    this._user = user;
    notifyListeners();
  

以及用于获取配置文件的 AuthService 部分:

Future<User> getUser() async 
  print('GETTING THE USER');
  final fbaseUser = await _auth.currentUser();
  final snapshot = await _db.collection('users')
      .document(fbaseUser.uid)
      .get();
  if (snapshot.data != null) 
    Map<dynamic, dynamic> jsres = snapshot.data;
    _user = User.fromJson(jsres);
    return _user;
  

为什么会出现此错误?我究竟做错了什么?有人可以帮我解决这个问题吗?

【问题讨论】:

【参考方案1】:

您可以在下面复制粘贴运行完整代码,完整代码修复此问题 原因: 此行userProfile.user = snapshot.data; 导致错误FutureBuilder 是构建数据,并接收notifyListeners()

来自 Flutter 团队的建议,https://github.com/flutter/flutter/issues/16218#issuecomment-403995076FutureBuilderbuilder 应该只构建小部件,它不应该有任何逻辑。建设者可以被任意调用。

解决方案: 在用户情况下,getUser() 后面可以直接设置UserProfile.user 第 1 步:删除 final userProfile = Provider.of&lt;UserProfile&gt;(context); 第 2 步:将 userProfile.user = snapshot.data; 逻辑移动到 futureBuilderfuture

FutureBuilder<User>(
          future: _future.then((value) =>
              Provider.of<UserProfile>(context, listen: false).user = value),

完整代码

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() 
  runApp(
    ChangeNotifierProvider(
      create: (context) => UserProfile(),
      child: MyApp(),
    ),
  );


class StatefulWrapper extends StatefulWidget 
  final Function onInit;
  final Widget child;
  const StatefulWrapper(@required this.onInit, @required this.child);
  @override
  _StatefulWrapperState createState() => _StatefulWrapperState();


class _StatefulWrapperState extends State<StatefulWrapper> 
  @override
  void initState() 
    if (widget.onInit != null) 
      widget.onInit();
    
    super.initState();
  

  @override
  Widget build(BuildContext context) 
    return widget.child;
  


class User 
  String name;

  User(this.name);


Future<User> getUser() async 
  print("getUser");
  return User(name: "test");


class UserProfile with ChangeNotifier 
  User _user = User();

  get user 
    return _user;
  

  set user(User user) 
    this._user = user;
    notifyListeners();
  


class HomePage extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    //final authService = Provider.of<AuthService>(context);
    //final userProfile = Provider.of<UserProfile>(context, listen: false);
    Future _future = getUser();

    return StatefulWrapper(
        onInit: () 
          //FirebaseNotifications().setUpFirebase();
        ,
        child: FutureBuilder<User>(
          future: _future.then((value) =>
              Provider.of<UserProfile>(context, listen: false).user = value),
          builder: (context, AsyncSnapshot<User> snapshot) 
            if (snapshot.connectionState == ConnectionState.done) 
              if (snapshot.error != null) 
                return Text(snapshot.error.toString());
              


              if (snapshot.hasData) 
                return ListScreen();
               else 
                return LoginScreen();
              
             else 
              return Scaffold(
                appBar: AppBar(),
                body: Container(),
              );
            
          ,
        ));
  


class ListScreen extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Text("ListScreen");
  


class LoginScreen extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Text("LoginScreen");
  


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  

【讨论】:

谢谢!所以维护最终的 authService = Provider.of(context); 是没有问题的并使用它来能够在我想的未来参数中使用 authService.getUser 函数。我的意思是,我就是这样做的,而且效果很好,但只是为了确保这是一种愚蠢的做法。 如果遇到FutureBuilder不必要的重建,可以参考github.com/flutter/flutter/issues/11426#issuecomment-414047398【参考方案2】:

有问题的代码:

if(condition)

  ...
  ...
  notifyListners(); // problem
else
  await Future....
  ...
  ...
  notifyListners();  //not getting problem here    

带有解决方案的代码:我没有使用等待,这就是为什么会出现错误。所以在使用 notifyListners() 之前,在异步函数中使用 await

if(condition)

  ...
  ...
  await Future.delayed(Duration(milliseconds: 1)); // use await 
  notifyListners(); 
else
  await Future....
  ...
  ...
  notifyListners();      

【讨论】:

【参考方案3】:

当我收到这条消息时,发现在作为侦听器的小部件中有一个 notifylisteners() 调用。

我刚刚删除了多余的 notifyListeners() 调用,一切正常。

【讨论】:

【参考方案4】:

之前

class UserProfile with ChangeNotifier 
  User _user = User();

  get user 
    return _user;
  

  set user(User user) 
    this._user = user;
    notifyListeners();
  

再添加一个这样的方法


  void Adduser(User user) 
    this._user = user;

  

在\Solution :now 通知模型类之后

class UserProfile with ChangeNotifier 
  User _user = User();

  get user 
    return _user;
  

  set user(User user) 
    this._user = user;
    notifyListeners();
  

  void Adduser(User user) 
    this._user = user;

  

这样打电话

userProfile.Adduser(snapshot.data);

问题:

///problem is here and notifylistner
userProfile.user = snapshot.data;

每当我们调用它时,notifylistner() 都会重建小部件

【讨论】:

以上是关于无法将小部件标记为需要构建,因为框架已经在构建小部件的过程中的主要内容,如果未能解决你的问题,请参考以下文章

将小部件从单独的 ui 文件加载到主窗口

如何将小部件放置在具有成比例大小和位置的小部件上方

将小部件名称与小部件一起添加到主屏幕

flutter 多选项目插件

安装后自动将小部件放在桌面上

如何将小部件尺寸传递给 __init__