flutterflutter-layoutflutter-futurebuilderflutter-listviewflutter-gridview

Add infinite scroll to GridView.builder to flutter FutureBuilder


I'm having a hard time to wrap my head around infinite scrolling in flutter. I'm trying to load 10 recipes first, then with the help of ScrollController show the new recipes. and when there is no more recipes left, I want to show a message that shows there is no more posts.

here's my project's code.

import 'package:flutter/material.dart';
import 'package:test/model/recipe.dart';
import 'package:test/pages/recipe_details.dart';
import 'package:test/widgets/recipe_card.dart';
import 'package:http/http.dart' as http;

class SingleCategory extends StatefulWidget {
  final Recipe recipe;
  final int recipeCourseid;
  final String recipeCourseName;

  SingleCategory({
    this.recipe,
    @required this.recipeCourseid,
    @required this.recipeCourseName,
  });

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

class _SingleCategoryState extends State<SingleCategory> {
  ScrollController _scrollController = ScrollController();
  int pageNumber = 1;

  var myRecipe;

  Future<List<Recipe>> getRecipeList({int pageNumber}) async {
    // pageNum = 1;
    var response = await http.get(
        'https://test.com/wp-json/wp/v2/wprm_recipe?wprm_course=${widget.recipeCourseid}&per_page=10&$pageNumber');
    var body = response.body;
    final recipes = recipeFromJson(body)

    return recipes;
  }

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(() {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        pageNumber++;
        print(pageNumber);
        getRecipeList();
      }
    });
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Directionality(
      textDirection: TextDirection.rtl,
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.recipeCourseName),
          centerTitle: true,
          elevation: 5,
        ),
        body: Padding(
          padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 12.0),
          child: FutureBuilder(
            future: getRecipeList(),
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return GridView.builder(
                  controller: _scrollController,
                  itemCount: snapshot.data.length,
                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 2,
                    crossAxisSpacing: 12.0,
                    mainAxisSpacing: 12.0,
                  ),
                  itemBuilder: (BuildContext context, int index) {
                    myRecipe = snapshot.data[index].recipe;
                    return InkWell(
                      onTap: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) => RecipeDetails(
                              recipe: snapshot.data[index],
                            ),
                          ),
                        );
                      },
                      child: RecipeCard(myRecipe: myRecipe),
                    );
                  },
                );
              } else if (snapshot.hasError) {
                return Center(
                  child: Text('There was an error, Please try again'),
                );
              } else {
                return Center(
                  child: CircularProgressIndicator(),
                );
              }
            },
          ),
        ),
      ),
    );
  }
}

I would apprecite it if you could point me to the right direction.


Solution

  • Everything looks good but you have to make a small change.

    Your Code:

    _scrollController.addListener(() {
          if (_scrollController.position.pixels ==
              _scrollController.position.maxScrollExtent) {
            pageNumber++;
            print(pageNumber);
            getRecipeList();
          }
        });
    

    Should be:

    _scrollController.addListener(() {
          if (_scrollController.position.pixels ==
              _scrollController.position.maxScrollExtent) {
            pageNumber++;
            setState(() {}); // if add this, Reload your futurebuilder and load more data
     //        getRecipeList(); // It's not necessary because, FutureBuilder call automatically getRecipeList when you call setState 
          }
        });