Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Widget cannot be marked as needing to build because the framework is already in the process of building widgets

I'm trying to implement Provider and it seems it works fine but I get this message:

This _DefaultInheritedProviderScope widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase. The widget on which setState() or markNeedsBuild() was called was: _DefaultInheritedProviderScope value: Instance of 'UserProfile' listening to value The widget which was currently being built when the offending call was made was: FutureBuilder dirty state: _FutureBuilderState#bf6ec When the exception was thrown, this was the stack:

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) ... The UserProfile

sending notification was: Instance of 'UserProfile'

My code is the following:

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

And this is the UserProfile class:

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

  get user {
    return _user;
  }

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

and the part of the AuthService used to fetch the profile:

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

Why am I getting this error? What am I doing wrong? Can anyone help me with this, please?

like image 352
John Smith Optional Avatar asked Mar 25 '20 16:03

John Smith Optional


People also ask

How do you rebuild another widget in Flutter?

Using setState to rebuild widgets Flutter gives you access to setState() . In this case, we have to ensure setState() has the new values. When setState() is called, Flutter will know to get these new values and mark the widget that needs to be rebuilt.

What is a widget and mention its importance in Flutter how many types of widgets are there in Flutter?

Widgets describe what their view should look like given their current configuration and state. It includes a text widget, row widget, column widget, container widget, and many more. Widgets: Each element on a screen of the Flutter app is a widget.

Which method takes a widget and makes the widget as the root of widget tree in Flutter?

The runApp() function takes the given Widget and makes it the root of the widget tree. In this example, the widget tree consists of two widgets, the Center widget and its child, the Text widget.

How do you rebuild a stateful widget Flutter?

The solution is to pass a new Key to WidgetB every time we need it to be rebuilt: WidgetA will see that WidgetB has changed and will rebuild it when setState is called. In other words, whenever a stateful widget's Key property changes, calling setState on its parent will force a rebuild of the stateful widget.


2 Answers

code with problem:

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

code with solution: i am not using await thats why getting the error. so use await in async function, before using notifyListners()

if(condition)
{
  ...
  ...
  await Future.delayed(Duration(milliseconds: 1)); // use await 
  notifyListners(); 
}else{
  await Future....
  ...
  ...
  notifyListners();      
}
like image 199
Yash Zade Avatar answered Oct 22 '22 08:10

Yash Zade


You can copy paste run full code below, full code fix this issue
Reason:
This line userProfile.user = snapshot.data; cause error
FutureBuilder is build data, and receive notifyListeners()

From Flutter team's suggestion, https://github.com/flutter/flutter/issues/16218#issuecomment-403995076
The FutureBuilder's builder should only build widgets, it shouldn't have any logic. Builders can get called arbitrarily.

Solution:
In user case, after getUser() you can directly set UserProfile.user
Step 1: remove final userProfile = Provider.of<UserProfile>(context);
Step 2: move userProfile.user = snapshot.data; logic to futureBuilder's future

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

full code

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(),
    );
  }
}
like image 30
chunhunghan Avatar answered Oct 22 '22 06:10

chunhunghan