Search code examples
androidiosflutterdartflutter-test

Flutter pagination not work. I can not load more data


i tried to load more data when scroll. But loadMore function not work. When debugging it stop loadMore function and Scroll not working. Can anyone help me?. I did not find a soluton. Why loadMore not work? What I want to do is load more data when scroll end, page number increases

My api class

  static Future<List<Category>> fetchCategories(String path,int pageNumber, int pageSize) async {
    late List<Category> list;
    final http = InterceptedHttp.build(interceptors: [
      ApiInterceptor(),
    ]);
    final response =
    await http.get(Uri.parse(path + "?pageNumber=$pageNumber&pageSize=$pageSize"));
    print(response);
    var responseJson = json.decode(response.body);
    var rest = responseJson['data'] as List;

    if (response.statusCode == 200) {
      print(rest);
      list = rest.map<Category>((json) => Category.fromJson(json)).toList();
      print("List Size: ${list.length}");
      return list;
    } else {
      throw Exception(response.statusCode);
    }
  }
} 

My View class


import 'package:flutter/material.dart';
import 'package:healtie/core/models/category/category_api.dart';
import 'package:healtie/core/utility/constants.dart';

import '../../../core/config/app_config.dart';
import '../../../core/models/category/category.dart';
import 'categoryCard.dart';

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

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

class _BuildCategoriesState extends State<BuildCategories> {
  late final _baseUrl =
      AppConfig.of(context)!.baseURL + AppConfig.of(context)!.categoryPath;

  // At the beginning, we fetch the first 10 posts
  int _pageNumber = 0;
  int _pageSize = 12;

  // There is next page or not
  bool _hasNextPage = true;

  // Used to display loading indicators when _firstLoad function is running
  bool _isFirstLoadRunning = false;

  // Used to display loading indicators when _loadMore function is running
  bool _isLoadMoreRunning = false;

  // This holds the posts fetched from the server
  List<Category> _categories = [];
  late ScrollController _controller;

  void _firstLoad() async {
    setState(() {
      _isFirstLoadRunning = true;
    });
    try {
      var data =
          await CategoryApi.fetchCategories(_baseUrl, _pageNumber, _pageSize);
      setState(() {
        data.forEach((element) {
          _categories.add(element);
          print("saaa");
        });
      });
    } catch (err) {
      print(err);
    }
    setState(() {
      _isFirstLoadRunning = false;
      print("asaaa");
    });
  }

  void _loadMore() async {
    if (_hasNextPage == true &&
        _isFirstLoadRunning == false &&
        _isLoadMoreRunning == false &&
        _controller.position.extentAfter < 300) {
      print(_isFirstLoadRunning);
      setState(() {
        _isLoadMoreRunning = true; // Display a progress indicator at the bottom
      });
      _pageNumber += 1; // Increase _page by 1
      try {
        var fetchedCategories =
            await CategoryApi.fetchCategories(_baseUrl, _pageNumber, _pageSize);
        print("oldu");
        if (fetchedCategories.length > 0) {
          setState(() {
            _categories.addAll(fetchedCategories);
          });
        } else {
          setState(() {
            _hasNextPage = false;
          });
        }
        print("oldu");

      } catch (err) {
        print(err);
      }

      setState(() {
        _isLoadMoreRunning = false;
      });
    }
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance!.addPostFrameCallback((_) => _firstLoad());
    _controller = ScrollController()..addListener(_loadMore);
  }

  @override
  void dispose() {
    _controller.removeListener(_loadMore);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return _isFirstLoadRunning
        ? Center(
            child: CircularProgressIndicator(),
          )
        : Column(
            children: [
              GridView.builder(
                  controller: _controller,
                  itemCount: _categories.length,
                  scrollDirection: Axis.vertical,
                  physics: const BouncingScrollPhysics(),
                  shrinkWrap: true,
                  padding: const EdgeInsets.only(
                      left: padding, right: padding, top: 10, bottom: 10),
                  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 3,
                    crossAxisSpacing: 10,
                    mainAxisSpacing: 10,
                    mainAxisExtent: 120,
                  ),
                  itemBuilder: (_, index) {
                    var store = _categories[index];
                    return CategoryCard(
                      store: store,
                      index: index,
                    );
                  }),
              if (_isLoadMoreRunning == true)
                const Padding(
                  padding: EdgeInsets.only(top: 10, bottom: 40),
                  child: Center(
                    child: CircularProgressIndicator(),
                  ),
                ),
              // When nothing else to load
              if (_hasNextPage == false)
                Container(
                  padding: const EdgeInsets.only(top: 30, bottom: 40),
                  color: Colors.amber,
                  child: const Center(
                    child: Text('You have fetched all of the content'),
                  ),
                ),
            ],
          );
  }
} 

App Page

enter image description here


Solution

  • I use this method for pagination in my projects: First you get the total page and current page in your response like this:

    {
      "success": true,
      "code": 200,
      "result":[your response],
      "pageNumber": 0,
      "totalPage": 0
    }
    

    And in your code :

            class _BuildCategoriesState extends State<BuildCategories> {
    
      List items = []; //list of your items 
      bool loadMore = false; //load more data
      int page=1;
      int totalPage=1;
      void getItems(int page) {
        //fetch your data and set total page and current page here to handle your paignation
      }
    
      @override
      Widget build(BuildContext context) {
        return Column(
            children: [
              Expanded(child:
              NotificationListener(
                onNotification: (ScrollNotification notification) {
                  if (!loadMore && notification.metrics.extentAfter == 0 && page != totalpage) {
                    setState(() => loadMore = true);
                    page++;
                    //send request to server for more data
                    getitems();
                  }
                  return false;
                },
                child: GridView.count(
                  children: List.generate(items.length, (int index) {
                    return Your_Widget_Item()
                  }),
    
                ),
              )),
              Align(
                alignment: Alignment.center,
                child: Padding(
                  padding: EdgeInsets.only(bottom: 15),
                  child: Container(
                    height: loadMore ? 20.0 : 0,
                    width: 20,
                    child: CircularProgressIndicator(
                    ),
                  ),
                ),
              ),
            ]
        )
      }
    }
    

    I hope this code help you to get total idea about handling pagination