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

19

我试图实现Provider,看起来它工作正常,但是我得到了这个错误消息:

 

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

 

0     Element.markNeedsBuild。(package:flutter/src/widgets/framework.dart:3896:11)

 

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

 

2     _InheritedProviderScopeMixin.markNeedsNotifyDependents(package: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;
  }
}

为什么会出现这个错误?我做错了什么?有谁能帮我解决一下吗?

6个回答

15

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

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

解决方案:
在用户情况下,在getUser()之后,您可以直接设置UserProfile.user
步骤1:删除final userProfile = Provider.of<UserProfile>(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(),
    );
  }
}

谢谢!所以维护final authService = Provider.of<AuthService>(context);没有问题,并且在未来的参数中使用authService.getUser函数是可行的。我的意思是,我是这样做的,它可以工作,但只是为了确保这是一个好的实践。 - John Smith Optional
如果你遇到了FutureBuilder不必要的重建,可以参考这个链接:https://github.com/flutter/flutter/issues/11426#issuecomment-414047398 - chunhunghan

8

在使用 notifylisteners() 更新状态时,我也遇到了同样的问题。

解决方案:

WidgetsBinding.instance?.addPostFrameCallback((_) {
  notifyListeners();
});

我们把这个放在哪里? - Christopher Kikoti
每当我们需要重建屏幕时,例如当我们调用notifyListeners或多次调用setState时,我们必须在重新构建之前等待build方法完成。 - Sanju Bhatt
这确实是在更复杂的情况下前进的方式,在单个小部件中可能会有几个侦听器和/或您正在以反应方式处理事情。谢谢! - Jason Simpson
这是来自小部件的回调,我认为它很有意义。谢谢! - Đốc.tc

4

有问题的代码:

if(condition)
{
  ...
  ...
  notifyListners(); // problem
}else{
  await Future....
  ...
  ...
  notifyListners();  //not getting problem here    
}

代码及解决方案: 我没有使用await,因此导致出现错误。因此,在使用notifyListners()之前,请在异步函数中使用await。

if(condition)
{
  ...
  ...
  await Future.delayed(Duration(milliseconds: 1)); // use await 
  notifyListners(); 
}else{
  await Future....
  ...
  ...
  notifyListners();      
}

延迟/休眠似乎是随意的。该值可能会根据客户端/服务器负载而改变。虽然在某些情况下这可能有效,但这似乎不是正确的解决方案。 - banerjk

3
当我收到这条消息时,发现在一个小部件中作为监听器的notifylisteners()被调用。我只需删除多余的notifyListeners()调用,一切都正常工作了。

1
如果有人在从initState()中调用该方法并调用了notifyListeners(),那么您也会收到此错误。解决方案如下:
  @override
  void initState() {
    
    
    Future.delayed(Duration.zero).then((value) {
     Provider.of<'//'>(context, listen: false).callMethod();
    });

    super.initState();
  }

1

之前

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;
  }

现在是通知模型类:
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:
问题:
///problem is here and notifylistner
userProfile.user = snapshot.data;

每当我们调用notifylistener()时,重新构建小部件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接