skip to Main Content

I’m trying to use FutureBuilder inside a Stateless widget. The data is rendered properly, however I do face an issue "setState() or markNeedsBuild() called during build.". I’ve tried converting it to the Stateful widget and initializing future inside onInit() method but I acutally get the same error. Please find my code for both stateless and statefull widgets.

Stateless Widget

class DoctorsListWidget extends StatelessWidget {
  const DoctorsListWidget({Key? key}) : super(key: key);

  Future<void> _refreshDoctors(BuildContext context) async {
    await Provider.of<DoctorsProvider>(context, listen: false).getDoctors();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _refreshDoctors(context),
      builder: (context, snapshot) =>
          snapshot.connectionState == ConnectionState.waiting
              ? Center(
                  child: CircularProgressIndicator(),
                )
              : RefreshIndicator(
                  onRefresh: () => _refreshDoctors(context),
                  child: Consumer<DoctorsProvider>(
                    builder: (context, doctorsProvider, _) => Container(
                      height: 400,
                      child: Padding(
                        padding: EdgeInsets.all(10),
                        child: ListView.builder(
                          itemBuilder: (_, i) => Column(
                            children: <Widget>[
                              DoctorItemWidget(
                                  doctorsProvider.doctors[i].name,
                                  doctorsProvider.doctors[i].surname,
                                  doctorsProvider.doctors[i].spec,
                                  true),
                            ],
                          ),
                          itemCount: doctorsProvider.doctors.length,
                        ),
                      ),
                    ),
                  ),
                ),
    );
  }
}type here

Stateful widget

class DoctorsStatefulListWidget extends StatefulWidget {
  const DoctorsStatefulListWidget({Key? key}) : super(key: key);

  @override
  _DoctorsStatefulListWidgetState createState() =>
      _DoctorsStatefulListWidgetState();
}

class _DoctorsStatefulListWidgetState extends State<DoctorsStatefulListWidget> {
  Future? _future;

  @override
  void initState() {
    super.initState();
    _future = Provider.of<DoctorsProvider>(context, listen: false).getDoctors();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _future,
      builder: (context, snapshot) =>
          snapshot.connectionState == ConnectionState.waiting
              ? Center(
                  child: CircularProgressIndicator(),
                )
              : RefreshIndicator(
                  onRefresh: () => _refreshDoctors(),
                  child: Consumer<DoctorsProvider>(
                    builder: (context, doctorsProvider, _) => Container(
                      height: 400,
                      child: Padding(
                        padding: EdgeInsets.all(10),
                        child: ListView.builder(
                          itemBuilder: (_, i) => Column(
                            children: <Widget>[
                              DoctorItemWidget(
                                  doctorsProvider.doctors[i].name,
                                  doctorsProvider.doctors[i].surname,
                                  doctorsProvider.doctors[i].spec,
                                  true),
                            ],
                          ),
                          itemCount: doctorsProvider.doctors.length,
                        ),
                      ),
                    ),
                  ),
                ),
    );
  }

  Future<void> _refreshDoctors() async {
    await Provider.of<DoctorsProvider>(context, listen: false).getDoctors();
  }
}

Doctors Provider

class DoctorsProvider with ChangeNotifier {
  List<DoctorProvider> _doctors = [];

  DoctorsProvider(this._doctors);

  List<DoctorProvider> get doctors {
    return [..._doctors];
  }

  Future<void> getDoctors() async {
    _doctors = MockedDoctorsProvider().doctors;
    notifyListeners();
  }
}```

Both of the widgets throw the same error when running.


======== Exception caught by foundation library ====================================================
The following assertion was thrown while dispatching notifications for DoctorsProvider:
setState() or markNeedsBuild() called during build.

This _InheritedProviderScope<DoctorsProvider?> 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: _InheritedProviderScope<DoctorsProvider?>“`

Do I get the infinite loop somewhere although using Consumer?

Looking forward for your help!

I’ve tried converting the stateless widget to stateful one but the same error is thrown.

2

Answers


  1. Chosen as BEST ANSWER

    I'm not sure why but it looks that when using FutureBuilder, the getDoctors() method inside a provider should not call the notifyListeners() method. The view is rendered correctly without calling the notifyListeners() and the error no longer occurs.


  2. Try the following:

    SchedulerBinding.instance.addPostFrameCallback((_) {
        _future = Provider.of<DoctorsProvider>(context, listen: false).getDoctors();
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search