skip to Main Content

I get the following error while using Provider in Flutter :

The following assertion was thrown while dispatching notifications for CampaignProvider: setState() or markNeedsBuild() called during build. This _InheritedProviderScope<CampaignProvider?> 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<CampaignProvider?> The widget which was currently being built when the offending call was made was: KeyedSubtree-[GlobalKey#a8154]

Here is my widget :

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

  @override
  State<CampaignsScreen> createState() => _CampaignsScreenState();
}

class _CampaignsScreenState extends State<CampaignsScreen> {
  @override
  void initState() {
    super.initState();
    context.read<CampaignProvider>().getCampaigns();
  }

  @override
  Widget build(BuildContext context) {
    List<Campaign> campaigns = context.watch<CampaignProvider>().campaigns;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Campagnes'),
      ),
      body: Center(
        child: ListView.builder(
          itemCount: campaigns.length,
          itemBuilder: (BuildContext context, int index) {
            return ListTile(
              title: Text(campaigns[index].title),
              subtitle: Text(campaigns[index].description),
            );
          },
        ),
      ),
    );
  }
}

And my provider:

class CampaignProvider with ChangeNotifier {
  final CampaignService _campaignService = CampaignService();

  List<Campaign> _campaigns = [];

  List<Campaign> get campaigns {
    return _campaigns;
  }

  void getCampaigns() {
    _campaigns = _campaignService.getCampaigns();
    notifyListeners();
  }
}

It seems that I cannot use context.read() in initState(). But how can I call the method getCampaigns from my Provider class when the widget is building and listen for value changes so my screen can be updated ?

I tried to replace context.watch() by Consumer but I got the same error.

2

Answers


  1. That’s because you’re refreshing the widget when this was not built yet.

    You’ll need to wait until the first frame is loaded, like this:

    
      @override
      void initState() {
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (mounted) {
            context.read<CampaignProvider>().getCampaigns();
          }
        });
    }
    

    Adding more context, as jraufeisen mentioned, it seems like your code is not using any async method, getCampaigns() is not returning a Future, so in this case you can omit the notifyListeners and it will work.

    If your method getCampaigns returns a Future, then notifyListeners will work (or the solution I proposed first).

    Login or Signup to reply.
  2. You can use Future.delayed

    @override
     void initState() {
       Future.delayed(Duration.zero, () {
        if (mounted) 
         {
         context.read<CampaignProvider>().getCampaigns();
         }
        });
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search