Flutter下拉重新整理,上拉載入更多資料

leyan95發表於2018-07-08

下拉重新整理

很簡單,直接使用 RefreshIndicator 元件, onRefresh 為重新獲取資料的方法

  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        padding: EdgeInsets.all(2.0),
        child: RefreshIndicator(
          onRefresh: _refresh,
          backgroundColor: Colors.blue,
          child: ListView.builder(
                  itemCount: _dataList.length,
                  itemBuilder: (context, index) {
                      return ListItem(_dataList[index]);
                  },
                ),
        ),
      ),
    );
  }
  
  Future<Null> _refresh() async {
    _dataList.clear();
    await _loadFirstListData();
    return;
  }
複製程式碼

上拉載入更多

我們先看一下效果

Flutter下拉重新整理,上拉載入更多資料
  • 讓我們先從最原始的十條的資料開始
class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<int> items = List.generate(10, (i) => i);

  @override
  void initState() {
    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(
        title: Text("Infinite ListView"),
      ),
      body: ListView.builder(
        itemCount: items.length,
        itemBuilder: (context, index) {
          return ListTile(title: new Text("Number $index"));
        },
      ),
    );
  }
}
複製程式碼

  • 現在我們要編寫一個載入更多資料的方法,用來模擬 http 請求
Future<List<int>> fakeRequest(int from, int to) async {
 return Future.delayed(Duration(seconds: 2), () {
   return List.generate(to - from, (i) => i + from);
 });
}
複製程式碼

  • 現在我們想要讓使用者將 ListView 滑動到最末端的觸發 fakeRequest 來載入更多資料,最簡單的實現方式就是使用 ScrollController 來完成,ScrollController 會監聽滾動事件,當 ListView 滾動到末端的時候他會發出一個請求。在這裡還有一件需要注意的事就是為了避免對伺服器不斷地請求,我們需要做一個標記 isPerformingRequest 只有當它為 false 的時候才允許對後臺進行請求。
class _MyHomePageState extends State<MyHomePage> {
  List<int> items = List.generate(10, (i) => i);
  ScrollController _scrollController = new ScrollController();
  bool isPerformingRequest = false;

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(() {
      if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
        _getMoreData();
      }
    });
  }

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

  _getMoreData() async {
    if (!isPerformingRequest) {
      setState(() => isPerformingRequest = true);
      List<int> newEntries = await fakeRequest(items.length, items.length + 10);
      setState(() {
        items.addAll(newEntries);
        isPerformingRequest = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(
        title: Text("Infinite ListView"),
      ),
      body: ListView.builder(
        itemCount: items.length,
        itemBuilder: (context, index) {
          return ListTile(title: new Text("Number $index"));
        },
        controller: _scrollController,
      ),
    );
  }
}
複製程式碼

如果你現在執行程式你將會看到我們的列表已經可以實現動態載入了,但是這距離我們的目標還很遠,我們需要新增一些標誌動作讓使用者這道請求已經開始。


  • 接下來我們要用到 CircularProgressIndicator 去完成這個載入標誌
Widget _buildProgressIndicator() {
  return new Padding(
    padding: const EdgeInsets.all(8.0),
    child: new Center(
      child: new Opacity(
        opacity: isPerformingRequest ? 1.0 : 0.0,
        child: new CircularProgressIndicator(),
      ),
    ),
  );
}
複製程式碼
  • 現在我們將這個載入標誌放到我們的 ListView 中去,注意這裡要給 itemCount 加出一塊空間來放置我們的 _buildProgressIndicator
@override
Widget build(BuildContext context) {
  return new Scaffold(
    appBar: AppBar(
      title: Text("Infinite ListView"),
    ),
    body: ListView.builder(
      itemCount: items.length + 1,
      itemBuilder: (context, index) {
        if (index == items.length) {
          return _buildProgressIndicator();
        } else {
          return ListTile(title: new Text("Number $index"));
        }
      },
      controller: _scrollController,
    ),
  );
}
複製程式碼
  • 到這裡載入更多資料的功能基本完成了,為了更加美觀我們還要處理當沒有請求到更多資料的時候動作,在這裡我們新增一個動畫沒有更多資料的時候 ListView 向下移動覆蓋正在載入更多資料的標誌
_getMoreData() async {
  if (!isPerformingRequest) {
    setState(() => isPerformingRequest = true);
    List<int> newEntries = await fakeRequest(items.length, items.length); //returns empty list
    if (newEntries.isEmpty) {
      double edge = 50.0;
      double offsetFromBottom = _scrollController.position.maxScrollExtent - _scrollController.position.pixels;
      if (offsetFromBottom < edge) {
        _scrollController.animateTo(
            _scrollController.offset - (edge -offsetFromBottom),
            duration: new Duration(milliseconds: 500),
            curve: Curves.easeOut);
      }
    }
    setState(() {
      items.addAll(newEntries);
      isPerformingRequest = false;
    });
  }
}
複製程式碼

相關文章