Виджет не может быть помечен как требующий сборки, потому что фреймворк уже находится в процессе создания виджетов.

Я пытаюсь реализовать Provider, и кажется, что он работает нормально, но я получаю следующее сообщение:

Этот виджет _DefaultInheritedProviderScope нельзя пометить как требующий сборки, поскольку структура уже находится в процессе создания виджетов. Виджет может быть помечен как требующий создания на этапе сборки, только если один из его предков в настоящее время строит. Это исключение разрешено, потому что фреймворк строит родительские виджеты раньше дочерних, что означает, что всегда будет строиться грязный потомок. В противном случае платформа может не посещать этот виджет на этапе сборки. Виджет, для которого был вызван setState () или markNeedsBuild (): значение _DefaultInheritedProviderScope: экземпляр 'UserProfile', прослушивающий значение Виджет, который в настоящее время создавался, когда был сделан оскорбительный вызов, был: FutureBuilder грязное состояние: _FutureBuilderState # bf6ec Когда было сгенерировано исключение, это был стек:

0 Element.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 (пакет: flutter / src / foundation / change_notifier.dart: 206: 21)

4 UserProfile.user = (package: mdd / core / services / user_info.dart: 13: 5) ... Профиль пользователя

отправка уведомления была: Экземпляр 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;
  }
}

Почему я получаю эту ошибку? Что я делаю неправильно? Кто-нибудь может мне помочь с этим, пожалуйста?


person John Smith Optional    schedule 25.03.2020    source источник


Ответы (2)


Вы можете скопировать и вставить полный код ниже, полный код устранит эту проблему
Причина:
Эта строка userProfile.user = snapshot.data; вызывает ошибку
FutureBuilder - это данные сборки и получение notifyListeners()

По предложению команды Flutter, https://github.com/flutter/flutter/issues/16218#issuecomment-403995076
FutureBuilder builder должен создавать только виджеты, в нем не должно быть никакой логики. Строителей можно вызывать произвольно.

Решение:
В случае пользователя, после getUser() вы можете напрямую установить UserProfile.user
Шаг 1: удалить final userProfile = Provider.of<UserProfile>(context);
Шаг 2: переместить логику userProfile.user = snapshot.data; в future future futureBuilder

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(),
    );
  }
}
person chunhunghan    schedule 26.03.2020
comment
Спасибо! Так что нет проблем с сохранением окончательного authService = Provider.of ‹AuthService› (context); и использовать его, чтобы иметь возможность использовать функцию authService.getUser в будущем параметре, я полагаю. Я имею в виду, что я так и сделал, и это работает, но просто для уверенности, что это глупая практика. - person John Smith Optional; 26.03.2020
comment
Если вы столкнетесь с ненужными перестройками FutureBuilder, вы можете сослаться на этот github.com/flutter/flutter / issues / 11426 # issuecomment-414047398 - person chunhunghan; 27.03.2020

код с проблемой:

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

код с решением: я не использую await, поэтому получаю ошибку. поэтому используйте ожидание в функции async перед использованием notifyListners ()

if(condition)
{
  ...
  ...
  await Future.delayed(Duration(milliseconds: 1)); // use await 
  notifyListners(); 
}else{
  await Future....
  ...
  ...
  notifyListners();      
}
person Yash Zade    schedule 29.06.2021