Create ListView with infinite scrolling by provider: setState() or markNeedsBuild() called during build

Issue

I am trying to create ListView where items are built while the screen is being scrolled. I am trying to add content by using provider.

I get an error "setState() or markNeedsBuild() called during build" when calling notifyListener() inside itemBuilder.

I know the reason is because notifyListener() is called during ListView is built.

My questions are:

  • is there a workaround to call notifyListener() after or before build?
  • if this is not even an appropriate approach, what is the best approach to add content to ListView for infinite scrolling by using Provider?

This is not a duplicate of this post because his ListView widget doesn’t have to be built over and over again as scroll happens.

my_app.dart

import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
import 'package:provider/provider.dart';
import 'suggestions.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (context) => Suggestions(),
        ),
      ],
      child: MaterialApp(
        title: 'Welcome to Flutter',
        theme: ThemeData(
          primaryColor: Colors.white,
        ),
        home: RandomWords(),
      ),
    );
  }
}

class RandomWords extends StatelessWidget {
  final _biggerFont = TextStyle(fontSize: 18.0);

  void _addSuggestions(BuildContext context) {
    Provider.of<Suggestions>(context, listen: false).addSuggestions();
  }

  @override
  Widget build(BuildContext context) {
    var suggestions = Provider.of<Suggestions>(context).getSuggestions;
    return Scaffold(
      appBar: AppBar(
        title: Text('Startup Name Generator'),
        actions: [
          IconButton(icon: Icon(Icons.list), onPressed: () {}),
        ],
      ),
      body: _buildSuggestions(suggestions),
    );
  }

  Widget _buildSuggestions(suggestions) {
    return ListView.builder(
        padding: EdgeInsets.all(16.0),
        itemBuilder: /*1*/ (context, i) {
          if (i.isOdd) {
            return Divider(); /*2*/
          }

          final index = i ~/ 2; /*3*/
          if (index >= suggestions.length) {
            _addSuggestions(context);
          }
          return _buildRow(suggestions[index]);
        });
  }

  Widget _buildRow(WordPair pair) {
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
      trailing: Icon(
        Icons.favorite_border,
        color: null,
      ),
    );
  }
}

suggestions.dart

import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

class Suggestions extends ChangeNotifier {
  var _suggestions = <WordPair>[];

  get getSuggestions {
    return _suggestions;
  }

  void addSuggestions() {
    _suggestions.addAll(generateWordPairs().take(10));
    notifyListeners();
  }
}

Solution

Remove notifyListeners() from addSuggestions();

It is because ListView is being built every time scroll happens. You do not need to use notifyListeners to rebuild the widget inside a widget that rebuilds by itself.

Answered By – Kouta Nakano

Answer Checked By – Mary Flores (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *