Виджет не может быть помечен как необходимый для сборки, потому что фреймворк уже находится в процессе создания виджетов - PullRequest
2 голосов
/ 25 марта 2020

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

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

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: 202: 21 *) 4 UserProfile.user = (пакет: 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 Ответ

2 голосов
/ 26 марта 2020

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

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

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

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(),
    );
  }
}
...