Flutter 入門與實戰(六):給列表增加下拉重新整理和上滑載入更多功能

島上碼農發表於2021-05-25

在實際的 App 中,下拉重新整理和上滑載入更多是非常常見的互動形式。在 Flutter 中,有 flutter_easyrefresh開源外掛用於實現下拉重新整理和上滑載入更多。本篇介紹了有狀態元件和 flutter_easyrefresh 的基本應用,同時使用模擬的方式完成了非同步資料載入。

有狀態元件

當 Flutter 的頁面需要動態更新資料的時候,就會涉及到 UI 元件需要根據資料變化更新,此時也就意味著元件有了“狀態”。這就類似 React 的類元件和函式元件(只是後續 React 使用了勾子函式實現了函式元件也可以有狀態)。在 Flutter 中,元件也分為無狀態元件(StatelessWidget)和有狀態元件(StatefulWidget),一般儘量使用無狀態元件。但是如果元件本身需要維護自身狀態的時候,就需要使用有狀態元件了。有狀態元件的定義形式如下:

//有狀態元件宣告
class DynamicPage extends StatefulWidget {
  DynamicPage({Key key}) : super(key: key);

  //建立元件狀態
  @override
  _DynamicPageState createState() => _DynamicPageState();
}

//元件狀態
class _DynamicPageState extends State<DynamicPage> {
  @override
  Widget build(BuildContext context) {
    //UI 構建
  }
}
複製程式碼

有狀態元件實際的業務邏輯均由對應的狀態實現,包括資料定義和 UI 構建。其核心在於有一個 setState 方法用於通知介面重新整理(這點也和 React 類似),一旦主動呼叫了 setState 方法,介面就會進行重新整理。當然,其中還有一些別的與狀態相關的方法,如狀態初始化方法 initialState。

非同步 async/await

在網路請求中,不可避免需要用到非同步請求,這個時候就需要使用到 async 和 await。標記為 async 的函式返回結果是一個 Future 包裹的物件,呼叫方可以使用 await 從中獲取實際返回的資料。async/await 這個配對就完成了一個非同步呼叫過程。在結果沒有返回的時候,主執行緒會執行其他任務。在這裡我們將列表資料獲取的方法改為 async 方式,以模擬網路請求,如下所示:

static Future<List<Map<String, Object>>> list(int page, int size) async {
    return List<Map<String, Object>>.generate(size, (index) {
      return {
        'title': '標題${index + (page - 1) * size + 1}:這是一個列表標題,最多兩行,多處部分將會被擷取',
        'imageUrl':
            'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3331308357,177638268&fm=26&gp=0.jpg',
        'viewCount': 180,
      };
    });
  }
複製程式碼

呼叫的時候,使用 await即可獲取實際結果資料,如下所示:

// _currentPage 為當前頁碼,PAGE_SIZE為分頁大小
List<Map<String, Object>> _newItems =
        await DynamicMockData.list(_currentPage, PAGE_SIZE);
複製程式碼

引入 flutter_easyrefresh

flutter 需要引入第三方外掛時,需要在 pubspec.yaml 檔案下的dependencies節點下增加依賴,撰寫本文時flutter_easyrefresh的最新版為2.2.1,因此指定版本如下:

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.2
  flutter_easyrefresh: ^2.2.1
複製程式碼

新增完依賴後,需要在專案目錄執行 flutter pub get 命令更新依賴(VSCode 在檢測到 pubspec.yaml變化時會自動拉取)。

使用 flutter_easyrefresh

對上一篇中的列表(Flutter 入門與實戰(五):來一個圖文並茂的列表)進行改造,分為如下三個步驟:

  • 將頁面修改為有狀態元件,方便支援管理資料和根據資料更新介面
  • 使用 EasyRefresh 包裹列表元件,並指定 onRefresh 和 onLoad 回撥方法,以響應下拉重新整理和上滑載入更多的動作。
  • 根據當前分頁獲取資料,並更新到列表資料中,再呼叫 setState 更新狀態資料重新整理介面。

整個新的 dynamic_page 的程式碼如下所示:

import 'package:flutter/material.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'dynamic_item.dart';
import 'dynamic_mock_data.dart';

class DynamicPage extends StatefulWidget {
  DynamicPage({Key key}) : super(key: key);

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

class _DynamicPageState extends State<DynamicPage> {
  List<Map<String, Object>> _listItems = [];
  int _currentPage = 1;
  static const int PAGE_SIZE = 20;

  void _refresh() async {
    _currentPage = 1;
    _requestNewItems();
  }

  void _load() async {
    _currentPage += 1;
    _requestNewItems();
  }

  void _requestNewItems() async {
    List<Map<String, Object>> _newItems =
        await DynamicMockData.list(_currentPage, PAGE_SIZE);
    this.setState(() {
      if (_currentPage > 1) {
        _listItems += _newItems;
      } else {
        _listItems = _newItems;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: EasyRefresh(
        onRefresh: () async {
          _refresh();
        },
        onLoad: () async {
          _load();
        },
        child: ListView.builder(
            itemCount: _listItems.length,
            itemBuilder: (context, index) {
              return DynamicItem(
                _listItems[index]['title'],
                _listItems[index]['imageUrl'],
                _listItems[index]['viewCount'],
              );
            }),
      ),
    );
  }
}

複製程式碼

執行結果

執行結果如下圖所示,由於上拉載入過快沒法直接截圖看到。需要注意的是,首次載入的時候,EasyRefresh 並不會自動載入,這個時候需要使用 EasyRefreshController 來控制,有興趣的同學可以參考 flutter_easyfresh 的文件實現。 refresh.jpg

結語

flutter_easyrefresh 可以實現豐富的載入效果,包括自定義載入元件,乃至使用有趣的動畫等等,基本上可以滿足各類下拉重新整理或上滑載入更多的需要。建議可以參考文件多嘗試其他效果,有興趣的也可以閱讀原始碼看看具體的實現方式。

相關文章