Add infinite scroll to GridView.builder to flutter FutureBuilder

Solution for Add infinite scroll to GridView.builder to flutter FutureBuilder
is Given Below:

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.

Try to add in your GridView

physics: const AlwaysScrollableScrollPhysics(),

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

Declare a new variable in your class

 List<Recipe> recipesList;

And inside the getRecipeList() method change as like below

Future<List<Recipe>> getRecipeList({int pageNumber}) async {
    // pageNum = 1;
If(page number ==1)
  recipesList.clear;

    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)
    recipesList.addAll(recipes);
    return recipesList;
  }

And in the scrollListener try to pass pageNumber. Since you have declare the same name for pageNumber both inside and outside getRecipesList method.

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