Flutter 入門與實戰(二十七):使用 GetIt 同步不一樣頁面間數據

本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰!前端

前言

以前幾篇都是關於 Dio 網絡請求相關的內容,咱們的動態模塊也就差詳情頁了,可是每次添加和編輯成功後,返回到列表頁仍是須要手動刷新,沒有達到「所見即所得」的效果。本篇將介紹使用 GetIt 容器插件完成頁面間的數據同步。 本篇涉及到的知識點以下:數據庫

  • 詳情頁面界面構建;
  • 更新詳情查看次數接口實現;
  • GetIt 簡介;
  • 使用 GetIt 註冊全局對象;
  • 使用 GetIt 實現頁面間的數據同步。

詳情頁面

詳情頁面咱們顯示動態的標題、查看次數、圖片和內容。最簡單的方式是使用 Column 組件將全部內容依次包裹。可是,考慮內容實際會很長(也多是富文本),所以使用滾動組件包裹更合適,這裏仍是使用 CustomerScrollView 來,相比普通的 ScrollView 來講,CustomerScrollView 使用 Sliver 子組件,滑動性能會更高也更順暢,就如同某巧克力廣告說的同樣——縱享絲滑。編程

image.png

頁面自己比較簡單,就很少介紹了,具體的頁面層級以下所示。關於 CustomerScrollView能夠參考以前的文章:Flutter 入門與實戰(十二):利用CustomScrollView實現更有趣的滑動效果後端

  • CustomScrollView
    • slivers
      • 標題:使用 Container 包裹以便調整佈局。
      • 查看次數:和列表的查看次數相似。
      • 圖片:爲了不圖片佔據過高的高度,將圖片高度限制在240。
      • 內容:和標題相似,只是字體調小了2號

以上的 slivers 的子組件的內容都使用SliverToBoxAdapter轉換爲 Sliver。最終界面看後面的動圖便可。api

詳情查看次數更新

當咱們進入詳情頁面時,須要向後端提交更新查看次數的接口。注意,有些應用處理是後端直接在獲取詳情接口時往數據庫增長查看數。雖然這樣能夠減小請求次數,可是後端處理存在缺陷是會把調用詳情接口直接當作查看詳情頁面進行統計,結果在其餘地方獲取詳情時(好比編輯接口)可能致使多統計,行業黑話稱之爲「刷流量」。markdown

image.png

使用前端更新查看次數能夠作到更精準的控制,好比咱們能夠設置在頁面停留多長時間纔是有效查看,或者滑動到頁面底部纔算有效查看等等。網絡

更新查看次數的接口地址爲:http://localhost:3900/api/dynamics/view/:id,記得拉取最新的代碼運行。咱們在獲取詳情接口成功後再進行查看次數更新。更新查看次數不是很重要的邏輯,出現錯誤時無需給予提醒(避免給用戶形成困惑),這裏只是打印異常信息,用於開發過程當中排查問題。實際生產過程能夠將異常信息上傳到異常監控後臺。async

if (response.statusCode == 200) {
  setState(() {
    _dynamicEntity = DynamicEntity.fromJson(response.data);
  });
  _updateViewCount();
}
//...

void _updateViewCount() async {
  try {
    var response = await DynamicService.updateViewCount(_dynamicEntity.id);
    if (response.statusCode == 200) {
      setState(() {
        _dynamicEntity.viewCount = response.data['viewCount'];
        GetIt.instance.get<DynamicListener>().dynamicUpdated(
              _dynamicEntity.id,
              _dynamicEntity,
            );
      });
    }
  } catch (e) {
    print(e.toString());
  }
}
複製代碼

GetIt 簡介

GetIt 自己是一個容器管理插件,其最初的設計是用於完成依賴注入DI 和 IOC 容器的功能,有點相似Java Spring 的Bean容器。因爲容器中的對象是全局的,所以能夠用來作數據同步,也是 Flutter 官方推薦的狀態管理容器之一。另外一個經常使用的狀態管理插件是 Provider,後面咱們涉及到狀態管理的時候再來說述。ide

所謂的容器,本質上就是一個全局Map對象,能夠往裏面存入對象後,在須要用的時候直接取出,而不須要每一個使用者都本身建立對象,也實現了對象之間的解耦。oop

GetIt 的基礎用法很簡單,以下所示。若是考慮啓動時避免佔用太多資源,也可使用 lazy懶加載的方式,懶加載時傳入的是一個構建對象的方法,在取出對象的時候,若是容器中沒有該對象,則使用構建對象的方法建立一個,若是已經有了就直接返回。

注意,GetIt 有不少個版本,對 Flutter 的 最低SDK 版本有要求,咱們當前使用的 SDK 版本是2.0.6,所以最高只能選擇4.0.3版本(最新版本是7.1.2,須要2.12.x 以上版本)。

// 註冊對象:通常是單例
GetIt.instance.registerSingleton<T>(T object);
// 懶加載方式註冊
GetIt.instance.registerLazySingleton<T>(FactoryFunc<T> func)
// 獲取容器中的對象
GetIt.instance.get<T>();
複製代碼

註冊動態改變監聽對象

當動態新增,或者動態內容發生改變時,咱們須要更新列表。最簡單的方式是通知列表刷新,可是那樣的增長了網絡請求。咱們能夠直接修改列表的數據來完成列表的更新。考慮到不只僅是動態列表頁須要更新(好比動態嵌入到其餘頁面中),咱們把動態更新的方法抽象爲接口,只要是實現了對應接口的對象均可以在動態發生變化時調用對應的接口更新——即所謂的面向接口編程。

image.png

新增一個 dynamic_listener.dart 文件,定義一個接口抽象類DynamicListener。在列表頁面的_DynamicPageState中實現對應的接口。

import 'package:home_framework/models/dynamic_entity.dart';

abstract class DynamicListener {
  void dynamicUpdated(String id, DynamicEntity updatedDynamic);

  void dynamicAdded(DynamicEntity newDynamic);
}
複製代碼

_DynamicPageState使用 implements 關鍵字(也可使用 with 關鍵字)實現DynamicListener接口的兩個方法:

  • 新增響應方法:當有新增動態時,將新增動態插入到開頭處;
  • 更新方法:使用新的動態替換舊的動態數據。

同時,在initState方法中註冊自身到 GetIt 容器。

class _DynamicPageState extends State<DynamicPage> implements DynamicListener {
	// ...
	
  @override
  void initState() {
    super.initState();
    // 註冊到 GetIt容器
    GetIt.instance.registerSingleton<DynamicListener>(this);
  }
  
  void dynamicUpdated(String id, DynamicEntity updatedDynamic) {
    int index = _listItems.indexWhere((element) => element.id == id);
    if (index != -1) {
      setState(() {
        _listItems[index] = updatedDynamic;
      });
    }
  }

  void dynamicAdded(DynamicEntity newDynamic) {
    setState(() {
      _listItems.insert(0, newDynamic);
    });
  }
  
  // ...
}
複製代碼

頁面間數據更新

有了 GetIt 容器,由於能夠直接從容器中獲取動態列表狀態管理對象,在其餘頁面處理就比較簡單了,邏輯分別以下:

  • 新增頁面:新增成功後調用dynamicAdded方法更新列表頁面;
  • 編輯頁面:編輯成功後調用dynamicUpdated方法更新列表頁面;
  • 詳情頁面:更新查看次數後調用dynamicUpdated方法更新列表頁面。

三個頁面的代碼分別以下:

//新增頁面
var response = await DynamicService.post(newFormData);
if (response.statusCode == 200) {
  Dialogs.showInfo(context, '添加成功');
  GetIt.instance
      .get<DynamicListener>()
      .dynamicAdded(DynamicEntity.fromJson(response.data));
  Navigator.of(context).pop();
}
//-------------------------------------
//編輯頁面
if (response.statusCode == 200) {
  Dialogs.showInfo(context, '保存成功');
  //處理成功更新後的業務
  _handleUpdated(newFormData);
  Navigator.of(context).pop();
}

// 處理更新,若是圖片更新了才更新動態圖片內容
void _handleUpdated(Map<String, String> newFormData) {
  _dynamicEntity.title = newFormData['title'];
  _dynamicEntity.content = newFormData['content'];
  if (newFormData.containsKey('imageUrl')) {
    _dynamicEntity.imageUrl = newFormData['imageUrl'];
  }
  GetIt.instance.get<DynamicListener>().dynamicUpdated(
      _dynamicEntity.id,
      _dynamicEntity,
  );
}

//-------------------------------------
//詳情頁面
void _updateViewCount() async {
  try {
    var response = await DynamicService.updateViewCount(_dynamicEntity.id);
    if (response.statusCode == 200) {
      setState(() {
        _dynamicEntity.viewCount = response.data['viewCount'];
        GetIt.instance.get<DynamicListener>().dynamicUpdated(
              _dynamicEntity.id,
              _dynamicEntity,
            );
      });
    }
  } catch (e) {
    print(e.toString());
  }
}
複製代碼

運行效果

屏幕錄製2021-07-12 下午11.52.47.gif

總結

本篇完成了整個動態管理的業務邏輯,包括了新增、刪除、編輯、查看次數等功能。經過 GetIt 容器管理插件及接口定義,能夠很簡單快速地完成頁面之間的數據同步。從整個系列也能夠看到,咱們在網絡請求這塊的代碼存在以下問題:

  • 重複代碼不少:好比 try...catch 代碼塊;
  • 暴露了 Dio 的細節;
  • 界面參與了業務對象的構建,沒有與業務邏輯分離。

接下來的篇章咱們將逐步完成對 Dio的封裝和網絡請求部分代碼的重構。

相關文章
相關標籤/搜索