在日常的開發(fā)中經(jīng)常會遇到二次請求才能完成顯示的情況
舉一個比較典型的例子:listView中的數(shù)據(jù)是下方的格式
[{String imgId,String text},{String imgId,String text}...]
需要使用imgId獲取具體的圖片url,這里就會有同學(xué)舉手了:為啥不直接給我一個url
因為這里不是單純的獲取一個url而已,一般是一個圖片對象汉规,然后客戶端根據(jù)不同的情況使用不同的圖片(類似的還有訂單Id等)
在原生Android中這個問題根本就不是個問題哭尝,在Adapter中再次進(jìn)行網(wǎng)絡(luò)請求(一般不會直接在Adapter中做這種事情,這里只是舉個例子)并對ImageView進(jìn)行賦值即可依疼,
但是基于狀態(tài)的Flutter做不到這一點痰腮,Widget是不可變的,沒法對其進(jìn)行再次賦值律罢,而且build()方法是不接受Future類型返回值的膀值,不能異步返回
這個有幾個辦法用來解決這個問題:
-
在本地額外加一個參數(shù)用來保存返回后的數(shù)據(jù),在返回后調(diào)用setState()進(jìn)行數(shù)據(jù)的保存
這樣也不是說不行误辑,但是至少會存在問題:- 什么時候進(jìn)行setState的調(diào)用沧踏,如果每張圖片完成后都進(jìn)行setState,那么setState次數(shù)過多消耗就會很大巾钉,如果加載完成后setState那么就需要用戶等待所有圖片都下載成功才能看到圖片翘狱,而且還需要額外的操作判斷所有請求都完成
2.如果數(shù)據(jù)類型很多的話需要添加的參數(shù)就太多了,數(shù)據(jù)結(jié)構(gòu)過于冗余
2.僅使用FutureBuilder<T>
FutureBuilder是一個異步組件砰苍,顧名思義潦匈,可以通過一個feature來進(jìn)行組件的創(chuàng)建
/// Creates a widget that builds itself based on the latest snapshot of
/// interaction with a [Future].
///
/// The [builder] must not be null.
const FutureBuilder({
Key key,
this.future,
this.initialData,//與創(chuàng)建時的泛型相同
@required this.builder,//AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnapshot<T> snapshot);
}) : assert(builder != null),
super(key: key);
initialData是在feature未完成時該組件的值,這里不是必要的赚导,但默認(rèn)值為null茬缩,需要注意處理
builder中的 snapshot參數(shù)為feture返回時的數(shù)據(jù)包裝類,使用 snapshot.data 進(jìn)行數(shù)據(jù)的獲取吼旧,該類還具有withError等方法用來處理意外情況
由于項目中使用 MVP 進(jìn)行開發(fā)凰锡,邏輯部分需要在Presenter層中進(jìn)行處理,以一個圖片例如:
//presenter
@override
Future<Widget> requestImageDetail(String imageId) async{
var image = await _imModel.getImageDetail(await PublicParams.getParams()..["image_id"] = imageId);
var url = image!=null?image[0].imageMiddle:"";
return Future.value(ImageUtil.network(url));
}
//view
FutureBuilder(initialData:Image.asset(Constant.IMAGE_ICON_LIFE),
future:requestImageDetail(imageId),
builder: (BuildContext context, AsyncSnapshot<Widget> snapshot){
return snapshot.data;
},)
代碼很簡單,這個方法的返回值直接用作FutureBuilder的future即可寡夹,這個Future會直接完成处面。
這段代碼雖然可以完成需求,但是對于MVP產(chǎn)生了嚴(yán)重的破壞菩掏,
首先魂角,P層的方法不應(yīng)該有返回值,
其次智绸,在本項目中model層應(yīng)該返回的是 StreamSubscription野揪,用來監(jiān)聽Stream返回并可以斷開監(jiān)聽,而在這里返回Future的話導(dǎo)致這個方法跳出了“三界”而不受管控
所以比較好的辦法是使用Completer與FutureBuilder進(jìn)行配合
-
使用Completer<T>與FutureBuilder進(jìn)行配合
Completer<T>是與Future相關(guān)的一個類瞧栗,調(diào)用Completer.complete(T)進(jìn)行手動進(jìn)行Future的返回
需要對上面的代碼進(jìn)行一些修改
//view
Completer<Image> _image = Completer();
mPresenter.requestImageDetail(_image, imageId);
FutureBuilder(initialData:Image.asset(Constant.IMAGE_ICON_LIFE),
future:_image.future,
builder: (BuildContext context, AsyncSnapshot<Widget> snapshot){
return snapshot.data;
},)
//presenter
requestImageDetail(Completer completer, String imageId) async{
makeRequest(_imModel.getImageDetail(await PublicParams.getParams()..["image_id"] = imageId,ICallBack<List<ImageInfoEntity>>(
(image){
var url = image!=null?image[0].imageMiddle:"";
completer.complete(ImageUtil.network(url));
}
)));
}
以上