How To Lazy Load Large List From HTTP REST API With Pagination In Flutter

Lazy loading a large list with pagination from a REST API in Flutter is bit tricky because of the way ListView behaves in Flutter. As long as you are clear on how a ListView, it’s index and state behaves, then working with ListView in Flutter is a breeze. In this article let’s see how to lazy load a large list in Flutter without any additional third party ListView plugins.

从 Flutter 中的 REST API 分页来延迟加载一个大的列表有点棘手,因为 ListView 在 Flutter 的行为方式。只要你清楚 ListView 的索引和状态是如何运作的,那么在 Flutter 中使用 ListView 就是小菜一碟。在这篇文章中,让我们看看如何在没有任何额外的第三方 ListView 插件的情况下在 Flutter 懒惰地加载一个大列表。

The entire Activity can be split into multiple steps. Follow the bellow steps to create a lazy loading list. In this example , I using an API from Random UsersWebsite.

整个活动可以分成多个步骤。按照下面的步骤创建一个延迟加载列表。在这个例子中,我使用了一个来自随机用户网站的 API。

Create a StatefulWidget

创建一个 StatefulWidget

The first step is to create a stateful widget with a Scroll Controller to track the scroll position in the device. Based on the scroll position,
we would be loading the next set of data.

第一步是创建带有状态控制器的有状态小部件,以跟踪设备中的滚动位置。根据滚动位置,我们将加载下一组数据。

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

class Home extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new HomeState();
}

class HomeState extends State<Home> {
  static int page = 0;
  ScrollController _sc = new ScrollController();
  bool isLoading = false;
  List users = new List();
  final dio = new Dio();
  @override
  void initState() {
    this._getMoreData(page);
    super.initState();
    _sc.addListener(() {
      if (_sc.position.pixels ==
          _sc.position.maxScrollExtent) {
        _getMoreData(page);
      }
    });
  }

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

Lazy Load Large List

延迟加载大型列表

In this step we would be loading the data from REST API. I am using Dio for http requests, you can also use the the standard http plugin for this.

在这一步中,我们将从 restapi 加载数据。我使用 Dio 来处理 http 请求,你也可以使用标准的 http 插件。

....
....
final dio = new Dio();
....
....
void _getMoreData(int index) async {
    if (!isLoading) {
      setState(() {
        isLoading = true;
      });
      var url = "https://randomuser.me/api/?page=" +
          index.toString() +
          "&results=20&seed=abc";
      print(url);
      final response = await dio.get(url);
      List tList = new List();
      for (int i = 0; i < response.data['results'].length; i++) {
        tList.add(response.data['results'][i]);
      }

      setState(() {
        isLoading = false;
        users.addAll(tList);
        page++;
      });
    }
  }

Creating The List

创建列表

The next step is to create the List and bind the list with data.

下一步是创建 List 并用数据绑定列表。

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Lazy Load Large List"),
      ),
      body: Container(
        child: _buildList(),
      ),
      resizeToAvoidBottomPadding: false,
    );
  }

  Widget _buildList() {
    return ListView.builder(
      itemCount: users.length + 1, // Add one more item for progress indicator
      padding: EdgeInsets.symmetric(vertical: 8.0),
      itemBuilder: (BuildContext context, int index) {
        if (index == users.length) {
          return _buildProgressIndicator();
        } else {
          return new ListTile(
            leading: CircleAvatar(
              radius: 30.0,
              backgroundImage: NetworkImage(
                users[index]['picture']['large'],
              ),
            ),
            title: Text((users[index]['name']['first'])),
            subtitle: Text((users[index]['email'])),
          );
        }
      },
      controller: _sc,
    );
  }

Other Items To Note

其他须注意事项

Along with the text data, an avatar image is also added to list item. Whenever the scroll position reaches the bottom, the next set of data is requested by increasing the page index by one. In addition to that, a loading indicator is shown while the data request to REST API is performed.
Also ensure that the scroll controller is disposed properly.

除了文本数据之外,一个化身图像也被添加到列表项中。每当滚动位置到达底部时,通过将页面索引增加一个来请求下一组数据。除此之外,在执行对 restapi 的数据请求时,还会显示一个加载指示器。还要确保滚动控制器被正确配置。

The bellow is the entire code that is been is been explained in this article. In addition to this code, you may have to hook this to the main.dart or to your main function to load this class.

以下是本文中解释的全部代码。除了这个代码,你可能需要把它挂到 main.dart 或者你的 main 函数上来加载这个类。

Full Code

完整代码

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

class Home extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new HomeState();
}

class HomeState extends State<Home> {
  static int page = 0;
  ScrollController _sc = new ScrollController();
  bool isLoading = false;
  List users = new List();
  final dio = new Dio();
  @override
  void initState() {
    this._getMoreData(page);
    super.initState();
    _sc.addListener(() {
      if (_sc.position.pixels ==
          _sc.position.maxScrollExtent) {
        _getMoreData(page);
      }
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Lazy Load Large List"),
      ),
      body: Container(
        child: _buildList(),
      ),
      resizeToAvoidBottomPadding: false,
    );
  }

  Widget _buildList() {
    return ListView.builder(
      itemCount: users.length + 1, // Add one more item for progress indicator
      padding: EdgeInsets.symmetric(vertical: 8.0),
      itemBuilder: (BuildContext context, int index) {
        if (index == users.length) {
          return _buildProgressIndicator();
        } else {
          return new ListTile(
            leading: CircleAvatar(
              radius: 30.0,
              backgroundImage: NetworkImage(
                users[index]['picture']['large'],
              ),
            ),
            title: Text((users[index]['name']['first'])),
            subtitle: Text((users[index]['email'])),
          );
        }
      },
      controller: _sc,
    );
  }
  
  void _getMoreData(int index) async {
    if (!isLoading) {
      setState(() {
        isLoading = true;
      });
      var url = "https://randomuser.me/api/?page=" +
          index.toString() +
          "&results=20&seed=abc";
      print(url);
      final response = await dio.get(url);
      List tList = new List();
      for (int i = 0; i < response.data['results'].length; i++) {
        tList.add(response.data['results'][i]);
      }

      setState(() {
        isLoading = false;
        users.addAll(tList);
        page++;
      });
    }
  }

  Widget _buildProgressIndicator() {
    return new Padding(
      padding: const EdgeInsets.all(8.0),
      child: new Center(
        child: new Opacity(
          opacity: isLoading ? 1.0 : 00,
          child: new CircularProgressIndicator(),
        ),
      ),
    );
  }

}

The bellow is the screenshot from simulator based on the above code.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值