Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to show loading indicator when scrolled to end of the list in flutter

I am loading the data from the server using future and displaying the data in list view by using listview builder and adding the new list when we scroll to the end of the list but I am unable to show loading indicator while the new list of items are being loaded. I have attached my code below. I have tried the package loadmore but no luck.

Main.dart

class Herbs extends StatefulWidget {
  final String title;
  Herbs(this.title);

  @override
  _HerbsState createState() => new _HerbsState();
}

class _HerbsState extends State<Herbs> {
  ScrollController _scrollController = new ScrollController();
  var data;
  var cname;
  String gcm = '';
  List pages = [];

  @override
  void initState() {
    super.initState();
    fetchPost(gcm);
    _scrollController.addListener(() {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        print('Page reached end of page');
        setState(() {
          Text('Loading');
        });
        fetchPost(gcm);
      }
    });
  }

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

  @override
  Widget build(BuildContext context) {
    cname = widget.title;
    var hgt = MediaQuery.of(context).size.width * 0.65;
    var wid = MediaQuery.of(context).size.height * 0.58;
    return new Scaffold(
      appBar: AppBar(
        title: Align(
          alignment: Alignment(-0.2, 0.3),
          child: Text(
            cname,
          ),
        ),
      ),
      body: Center(
        child: FutureBuilder<Herbslist>(
          future: fetchPost(gcm),
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              return Scrollbar(
                child: ListView.builder(
                  controller: _scrollController,
                  shrinkWrap: true,
                  itemCount: pages == null ? 0 : pages.length,
                  itemBuilder: (BuildContext context, int index) {
                    gcm = snapshot.data.herbslistContinue.gcmcontinue;
                    var img = pages[index].thumbnail.source;
                    return GestureDetector(
                        onTap: () {
                          Navigator.push(
                              context,
                              MaterialPageRoute(
                                builder: (context) => Detailpage(
                                  pages[index].title,
                                ),
                              ));
                        },
                        child: Card(
                            child: Column(
                          children: <Widget>[
                            CachedNetworkImage(
                              placeholder: (context, url) => Image.asset(
                                'images/image.png',
                                height: hgt,
                                width: wid,
                                fit: BoxFit.fill,
                              ),
                              imageUrl: img,
                              height: hgt,
                              width: wid,
                              fit: BoxFit.fill,
                            ),
                            ListTile(
                              title: Text(pages[index].title, style: TextStyle(fontWeight: FontWeight.bold),),
                            )
                          ],
                        )));
                  },
                ),
              );
            } else {
              return Center(
                child: CircularProgressIndicator(),
              );
            }
          },
        ),
      ),
    );
  }

  Future<Herbslist> fetchPost(gcm) async {
    String url =
        'https://eample.org/api.php?action=query&gcmtitle=Category:$cname&pilimit=max&prop=pageimages&pithumbsize=200&generator=categorymembers&format=json&gcmcontinue=$gcm';
    final response = await http.get(url);
    print(url);
    if (response.statusCode == 200) {
      data = Herbslist.fromJson(json.decode(response.body));
      pages.addAll(data.query.pages.values);
      return data;
    } else {
      throw (e) {
        print("Exception thrown: $e");
        Exception(e);
      };
    }
  }
}
like image 604
Mahant Avatar asked Mar 12 '20 08:03

Mahant


2 Answers

Following is the way which worked for me(Cases maybe different for others)

class _HerbsState extends State<Herbs> {
  bool _loading = false;

@override
  void initState() {
    super.initState();
    fetchPost(gcm);
    _scrollController.addListener(() {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        print('Page reached end of page');
        setState(() {});
        fetchPost(gcm);
      }
    });
  }

Widget build(BuildContext context){
return Scaffold(
         ListView.builder(
           controller: _scrollController,
           shrinkWrap: true,
           itemCount: pages == null ? 0 : pages.length+1,  //add +1 here
           itemBuilder: (BuildContext context, int index) {
           if(index == pages.length){
                _loading=true;  // declare the boolean and return loading indicator
                return Center(
                     child: Container(
                         child: SpinKitThreeBounce(
                                  color: Colors.green,
                                  size: 30,
                                 ),
                            ));
                          }
)
}
}
like image 136
Mahant Avatar answered Sep 28 '22 20:09

Mahant


You can do this by using the Stack widget and overlapping the CircularProgressIndicator with the ListView. Check the code below for a simplified example:

class LoadingOnScroll60619794 extends StatefulWidget {
  @override
  _LoadingOnScroll60619794State createState() => _LoadingOnScroll60619794State();
}

class _LoadingOnScroll60619794State extends State<LoadingOnScroll60619794> {
  bool _loading = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.refresh),
            onPressed: runLoading
          ),
        ],
      ),
      body: Stack(
        children: <Widget>[
          ListView(
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text('item 1'),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text('item 1'),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text('item 1'),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text('item 1'),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text('item 1'),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text('item 1'),
              ),
            ],
          ),
          _loading
            ? Center(
              child: CircularProgressIndicator(),
            )
            : SizedBox(width: 0, height: 0,)
        ],
      ),
    );
  }

  void runLoading(){
    setState(() {
      _loading = true;
    });
    Timer(Duration(milliseconds: 1500), (){
      setState(() {
        _loading = false;
      });
    });
  }
}

If you modify your code to change the _loading variable whenever your are fetching the data, you will get the same effect. Like the example below:

Future<Herbslist> fetchPost(gcm) async {
  setState(() {
    _loading = true;
  });
  String url =
    'https://eample.org/api.php?action=query&gcmtitle=Category:$cname&pilimit=max&prop=pageimages&pithumbsize=200&generator=categorymembers&format=json&gcmcontinue=$gcm';
  final response = await http.get(url);
  print(url);
  if (response.statusCode == 200) {
    data = Herbslist.fromJson(json.decode(response.body));
    pages.addAll(data.query.pages.values);
    setState(() {
      _loading = false;
    });
    return data;
  } else {
    setState(() {
      _loading = false;
    });
    throw (e) {
      print("Exception thrown: $e");
      Exception(e);
    };
  }
}
like image 30
João Soares Avatar answered Sep 28 '22 20:09

João Soares