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

Joh*_*nal 8 dart flutter flutter-provider

我正在尝试实现 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 (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(),
            );
          }
        },
      )
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

这是 UserProfile 类:

class UserProfile with ChangeNotifier {
  User _user = User();

  get user {
    return _user;
  }

  set user(User user) {
    this._user = user;
    notifyListeners();
  }
}
Run Code Online (Sandbox Code Playgroud)

以及用于获取配置文件的 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

为什么我收到这个错误?我究竟做错了什么?任何人都可以帮我解决这个问题吗?

San*_*att 11

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

解决方案:-

WidgetsBinding.instance?.addPostFrameCallback((_) {
  notifyListeners();
});
Run Code Online (Sandbox Code Playgroud)


chu*_*han 7

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

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

解决方案:
在用户情况下,getUser()可以直接设置后UserProfile.user
Step 1: remove final userProfile = Provider.of<UserProfile>(context);
Step 2: move userProfile.user = snapshot.data;logic to futureBuilder'sfuture

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

完整代码

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(),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)