1.動(dòng)畫原理:在一段時(shí)間內(nèi)快速的多次改變UI外觀,由于人眼會(huì)產(chǎn)生視覺暫留所以最終看到的就是一個(gè)連續(xù)的動(dòng)畫。

成都創(chuàng)新互聯(lián)公司是一家專業(yè)從事成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、網(wǎng)頁設(shè)計(jì)的品牌網(wǎng)絡(luò)公司。如今是成都地區(qū)具影響力的網(wǎng)站設(shè)計(jì)公司,作為專業(yè)的成都網(wǎng)站建設(shè)公司,成都創(chuàng)新互聯(lián)公司依托強(qiáng)大的技術(shù)實(shí)力、以及多年的網(wǎng)站運(yùn)營經(jīng)驗(yàn),為您提供專業(yè)的成都網(wǎng)站建設(shè)、營銷型網(wǎng)站建設(shè)及網(wǎng)站設(shè)計(jì)開發(fā)服務(wù)!
UI的一次改變稱為一個(gè)動(dòng)畫幀,對(duì)應(yīng)一次屏幕刷新。
FPS:幀率,每秒的動(dòng)畫幀數(shù)。
flutter動(dòng)畫分為兩類:
常見動(dòng)畫模式:
是一個(gè)抽象類,主要的功能是保存動(dòng)畫的值和狀態(tài)。常用的一個(gè)Animation類是Animation double ,是一個(gè)在一段時(shí)間內(nèi)依次生成一個(gè)區(qū)間之間的值的類,可以是線性或者曲線或者其他。
可以生成除double之外的其他類型值,如:Animation Color 或 Animation Size 。
是一個(gè)動(dòng)畫控制器,控制動(dòng)畫的播放狀態(tài),在屏幕刷新的每一幀,就會(huì)生成一個(gè)新的值。
包含動(dòng)畫的啟動(dòng)forward()、停止stop() 、反向播放 reverse()等方法,在給定的時(shí)間段內(nèi)線性的生成從0.0到1.0(默認(rèn)區(qū)間)的數(shù)字。
curve:描述動(dòng)畫的曲線過程。
curvedAnimation:指定動(dòng)畫的曲線。
常用Curve:
繼承自Animatable T ,表示的就是一個(gè) Animation 對(duì)象的取值范圍,只需要設(shè)置開始和結(jié)束的邊界值(值也支持泛型)。 它唯一的工作就是定義輸入范圍到輸出范圍的映射。
例如,Tween可能會(huì)生成從紅到藍(lán)之間的色值,或者從0到255。
Tween.animate:返回一個(gè)Animation。
映射過程:
1). Tween.animation通過傳入 aniamtionController 獲得一個(gè)_AnimatedEvaluation 類型的 animation 對(duì)象(基類為 Animation), 并且將 aniamtionController 和 Tween 對(duì)象傳入了 _AnimatedEvaluation 對(duì)象。
2). animation.value方法即是調(diào)用 _evaluatable.evaluate(parent)方法, 而 _evaluatable 和 parent 分別為 Tween 對(duì)象和 AnimationController 對(duì)象。
3). 這里的 animation 其實(shí)就是前面的 AnimationController 對(duì)象, transform 方法里面的 animation.value則就是 AnimationController 線性生成的 0.0~1.0 直接的值。 在 lerp 方法里面我們可以看到這個(gè) 0.0~1.0 的值被映射到了 begin 和 end 范圍內(nèi)了。
接收一個(gè)TickerProvider類型的對(duì)象,它的主要職責(zé)是創(chuàng)建Ticker。
防止屏幕外動(dòng)畫消耗資源。
[圖片上傳失敗...(image-115b94-1636441483468)]
過程:
回調(diào):
不使用addListener()和setState()來給widget添加動(dòng)畫。
使用AnimatedWidget,將widget分離出來,創(chuàng)建一個(gè)可重用動(dòng)畫的widget,AnimatedWidget中會(huì)自動(dòng)調(diào)用addListener()和setState()
AnimatedModalBarrier、DecoratedBoxTransition、FadeTransition、PositionedTransition、RelativePositionedTransition、RotationTransition、ScaleTransition、SizeTransition、SlideTransition
如何渲染過渡,把渲染過程也抽象出來:
AnimatedBuilder的示例包括: BottomSheet、 PopupMenu、ProgressIndicator、RefreshIndicator、Scaffold、SnackBar、TabBar。
MaterialPageRoute:平臺(tái)風(fēng)格一致的路由切換動(dòng)畫
CupertinoPageRoute:左右切換風(fēng)格
自定義:PageRouteBuilder
1.要?jiǎng)?chuàng)建交織動(dòng)畫,需要使用多個(gè)動(dòng)畫對(duì)象(Animation)。
2.一個(gè)AnimationController控制所有的動(dòng)畫對(duì)象。
3.給每一個(gè)動(dòng)畫對(duì)象指定時(shí)間間隔(Interval)
可以同時(shí)對(duì)其新、舊子元素添加顯示、隱藏動(dòng)畫.
當(dāng)AnimatedSwitcher的child發(fā)生變化時(shí)(類型或Key不同),舊child會(huì)執(zhí)行隱藏動(dòng)畫,新child會(huì)執(zhí)行執(zhí)行顯示動(dòng)畫。
希望大家支持一下,感謝
Flutter 里的 BuildContext 相信大家都不會(huì)陌生,雖然它叫 Context,但是它實(shí)際是 Element 的抽象對(duì)象,而在 Flutter 里,它主要來自于 ComponentElement 。
關(guān)于 ComponentElement 可以簡(jiǎn)單介紹一下,在 Flutter 里根據(jù) Element 可以簡(jiǎn)單地被歸納為兩類:
所以一般情況下,我們?cè)? build 方法或者 State 里獲取到的 BuildContext 其實(shí)就是 ComponentElement 。
那使用 BuildContext 有什么需要注意的問題 ?
首先如下代碼所示,在該例子里當(dāng)用戶點(diǎn)擊 FloatingActionButton 的時(shí)候,代碼里做了一個(gè) 2秒的延遲,然后才調(diào)用 pop 退出當(dāng)前頁面。
正常情況下是不會(huì)有什么問題,但是當(dāng)用戶在點(diǎn)擊了 FloatingActionButton 之后,又馬上點(diǎn)擊了 AppBar 返回退出應(yīng)用,這時(shí)候就會(huì)出現(xiàn)以下的錯(cuò)誤提示。
可以看到此時(shí) log 說,Widget 對(duì)應(yīng)的 Element 已經(jīng)不在了,因?yàn)樵? Navigator.of(context) 被調(diào)用時(shí), context 對(duì)應(yīng)的 Element 已經(jīng)隨著我們的退出銷毀。
一般情況下處理這個(gè)問題也很簡(jiǎn)單, 那就是增加 mounted 判斷,通過 mounted 判斷就可以避免上述的錯(cuò)誤 。
上面代碼里的 mounted 標(biāo)識(shí)位來自于 State , 因?yàn)? State 是依附于 Element 創(chuàng)建,所以它可以感知 Element 的生命周期 ,例如 mounted 就是判斷 _element != null; 。
那么到這里我們收獲了一個(gè)小技巧: 使用 BuildContext 時(shí),在必須時(shí)我們需要通過 mounted 來保證它的有效性 。
那么單純使用 mounted 就可以滿足 context 優(yōu)化的要求了嗎 ?
如下代碼所示,在這個(gè)例子里:
由于在 5 秒之內(nèi),Item 被劃出了屏幕,所以對(duì)應(yīng)的 Elment 其實(shí)是被釋放了,從而由于 mounted 判斷, SnackBar 不會(huì)被彈出。
那如果假設(shè)需要在開發(fā)時(shí)展示點(diǎn)擊數(shù)據(jù)上報(bào)的結(jié)果,也就是 Item 被釋放了還需要彈出,這時(shí)候需要如何處理 ?
我們知道不管是 ScaffoldMessenger.of(context) 還是 Navigator.of(context) ,它本質(zhì)還是通過 context 去往上查找對(duì)應(yīng)的 InheritedWidget 泛型,所以其實(shí)我們可以提前獲取。
所以,如下代碼所示,在 Future.delayed 之前我們就通過 ScaffoldMessenger.of(context); 獲取到 sm 對(duì)象,之后就算你直接退出當(dāng)前的列表頁面,5秒過后 SnackBar 也能正常彈出。
為什么頁面銷毀了,但是 SnackBar 還能正常彈出 ?
因?yàn)榇藭r(shí)通過 of(context); 獲取到的 ScaffoldMessenger 是存在 MaterialApp 里,所以就算頁面銷毀了也不影響 SnackBar 的執(zhí)行。
但是如果我們修改例子,如下代碼所示,在 Scaffold 上面多嵌套一個(gè) ScaffoldMessenger ,這時(shí)候在 Item 里通過 ScaffoldMessenger.of(context) 獲取到的就會(huì)是當(dāng)前頁面下的 ScaffoldMessenger 。
這種情況下我們只能保證Item 不可見的時(shí)候 SnackBar 還能正常彈出, 而如果這時(shí)候我們直接退出頁面,還是會(huì)出現(xiàn)以下的錯(cuò)誤提示,因?yàn)? ScaffoldMessenger 也被銷毀了 。
所以到這里我們收獲第二個(gè)小技巧: 在異步操作里使用 of(context) ,可以提前獲取,之后再做異步操作,這樣可以盡量保證流程可以完整執(zhí)行 。
既然我們說到通過 of(context) 去獲取上層共享往下共享的 InheritedWidget ,那在哪里獲取就比較好 ?
還記得前面的 log 嗎?在第一個(gè)例子出錯(cuò)時(shí),log 里就提示了一個(gè)方法,也就是 State 的 didChangeDependencies 方法。
為什么是官方會(huì)建議在這個(gè)方法里去調(diào)用 of(context) ?
首先前面我們一直說,通過 of(context) 獲取到的是 InheritedWidget ,而 當(dāng) InheritedWidget 發(fā)生改變時(shí),就是通過觸發(fā)綁定過的 Element 里 State 的 didChangeDependencies 來觸發(fā)更新, 所以在 didChangeDependencies 里調(diào)用 of(context) 有較好的因果關(guān)系 。
那我能在 initState 里提前調(diào)用嗎 ?
當(dāng)然不行,首先如果在 initState 直接調(diào)用如 ScaffoldMessenger.of(context).showSnackBar 方法,就會(huì)看到以下的錯(cuò)誤提示。
這是因?yàn)?Element 里會(huì)判斷此時(shí)的 _StateLifecycle 狀態(tài),如果此時(shí)是 _StateLifecycle.created 或者 _StateLifecycle.defunct ,也就是在 initState 和 dispose ,是不允許執(zhí)行 of(context) 操作。
當(dāng)然,如果你硬是想在 initState 下調(diào)用也行,增加一個(gè) Future 執(zhí)行就可以成功執(zhí)行
那我在 build 里直接調(diào)用不行嗎 ?
直接在 build 里調(diào)用肯定可以,雖然 build 會(huì)被比較頻繁執(zhí)行,但是 of(context) 操作其實(shí)就是在一個(gè) map 里通過 key - value 獲取泛型對(duì)象,所以對(duì)性能不會(huì)有太大的影響。
真正對(duì)性能有影響的是 of(context) 的綁定數(shù)量和獲取到對(duì)象之后的自定義邏輯 ,例如你通過 MediaQuery.of(context).size 獲取到屏幕大小之后,通過一系列復(fù)雜計(jì)算來定位你的控件。
例如上面這段代碼,可能會(huì)導(dǎo)致鍵盤在彈出的時(shí)候,雖然當(dāng)前頁面并沒有完全展示,但是也會(huì)導(dǎo)致你的控件不斷重新計(jì)算從而出現(xiàn)卡頓。
所以到這里我們又收獲了一個(gè)小技巧: 對(duì)于 of(context) 的相關(guān)操作邏輯,可以盡量放到 didChangeDependencies 里去處理 。
首先我們知道 GetX 組件里面 obs 狀態(tài)管理有三種創(chuàng)建屬性的方式,我們這里以 List 為例
視頻講解鏈接
我們聲明了一個(gè)類ListController 繼承自 GetxController ,用于屬性創(chuàng)建以及狀態(tài)通知的方法,首先我們用三種方式來創(chuàng)建屬性并且通過 convertToUpperCase 方法進(jìn)行對(duì)值的改變,然后我們通過調(diào)用 update()`方法來進(jìn)行數(shù)據(jù)更新,最后我們使用該屬性狀態(tài)的值,接下來我們看一下三種使用方式的對(duì)比。
import 'dart:convert';
import 'package:get/get.dart';
class ListController extends GetxController {
// 第一種
final listOne = RxListMap([
{
"name": "Jimi",
"age": 18
}
]);
// 第二種
final listTwo = RxList([
{
"name": "Jimi",
"age": 18
}
]);
// 第三種
final listThree = [{
"name": "Jimi",
"age": 18
}].obs;
void convertToUpperCase() {
listOne.value[0]["name"] = listOne.value.first["name"].toUpperCase();
listTwo.toList().first["name"] = listTwo.toList().first["name"].toString().toUpperCase();
listThree.toList().first["name"] = listTwo.toList().first["name"].toString().toUpperCase();
update();
}
}
我們?cè)陧撁嬷蝎@取狀態(tài)更新的值
import 'package:flutter/material.dart';
import 'package:flutter_getx_dvanced_example/ListController.dart';
import 'package:get/get.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
ListController listController = Get.put(ListController());
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: "GetX",
home: Scaffold(
appBar: AppBar(
title: Text("GetX"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
GetBuilderListController(
init: listController,
builder: (controller) {
return Text(
"我的名字是 {controller.listTwo.first['name']}",
style: TextStyle(color: Colors.green, fontSize: 30),
);
},
),
SizedBox(height: 20,),
GetBuilderListController(
init: listController,
builder: (controller) {
return Text(
"我的名字是 ${controller.listThree.first['name']}",
style: TextStyle(color: Colors.green, fontSize: 30),
);
},
),
SizedBox(height: 20,),
ElevatedButton(
onPressed: () {
listController.convertToUpperCase();
},
child: Text("轉(zhuǎn)換為大寫"))
],
),
),
),
);
}
}
/pre
|`
效果展示
RxT 繼承自 _RxImplT , _RxImplT 又繼承 RxNotifierT 并混合 RxObjectMixinT 類
RxImplT 它主要的作用是管理泛型的所有邏輯的。
RxObjectMixinT 它主要的作用是管理注冊(cè)到 GetX 和 Obx 的全局對(duì)象,比如 Widget 的 Rx 值
RxT 它主要的作用是將自定義模型類用Rx`來進(jìn)行包裝,
class RxT extends _RxImplT {
Rx(T initial) : super(initial);
@override
dynamic toJson() {
try {
return (value as dynamic)?.toJson();
} on Exception catch (_) {
throw '$T has not method [toJson]';
}
}
}
abstract class _RxImplT extends RxNotifierT with RxObjectMixinT {
_RxImpl(T initial) {
_value = initial;
}
void addError(Object error, [StackTrace? stackTrace]) {
subject.addError(error, stackTrace);
}
StreamR mapR(R mapper(T? data)) = stream.map(mapper);
void update(void fn(T? val)) {
fn(_value);
subject.add(_value);
}
void trigger(T v) {
var firstRebuild = this.firstRebuild;
value = v;
if (!firstRebuild) {
subject.add(v);
}
}
}
/pre
|`
RxListE 繼承自 ListMixinE 實(shí)現(xiàn)了 RxInterfaceListE 并混合了 NotifyManagerListE, RxObjectMixinListE
RxListE 它的主要作用是創(chuàng)建一個(gè)類似于 ListT 的一個(gè)列表
class RxListE extends ListMixinE
with NotifyManagerListE, RxObjectMixinListE
implements RxInterfaceListE {
RxList([ListE initial = const []]) {
_value = List.from(initial);
}
factory RxList.filled(int length, E fill, {bool growable = false}) {
return RxList(List.filled(length, fill, growable: growable));
}
factory RxList.empty({bool growable = false}) {
return RxList(List.empty(growable: growable));
}
/// Creates a list containing all [elements].
factory RxList.from(Iterable elements, {bool growable = true}) {
return RxList(List.from(elements, growable: growable));
}
/// Creates a list from [elements].
factory RxList.of(IterableE elements, {bool growable = true}) {
return RxList(List.of(elements, growable: growable));
}
/// Generates a list of values.
factory RxList.generate(int length, E generator(int index),
{bool growable = true}) {
return RxList(List.generate(length, generator, growable: growable));
}
/// Creates an unmodifiable list containing all [elements].
factory RxList.unmodifiable(Iterable elements) {
return RxList(List.unmodifiable(elements));
}
@override
IteratorE get iterator = value.iterator;
@override
void operator []=(int index, E val) {
_value[index] = val;
refresh();
}
/// Special override to push() element(s) in a reactive way
/// inside the List,
@override
RxListE operator +(IterableE val) {
addAll(val);
refresh();
return this;
}
@override
E operator [](int index) {
return value[index];
}
@override
void add(E item) {
_value.add(item);
refresh();
}
@override
void addAll(IterableE item) {
_value.addAll(item);
refresh();
}
@override
int get length = value.length;
@override
@protected
ListE get value {
RxInterface.proxy?.addListener(subject);
return _value;
}
@override
set length(int newLength) {
_value.length = newLength;
refresh();
}
@override
void insertAll(int index, IterableE iterable) {
_value.insertAll(index, iterable);
refresh();
}
@override
IterableE get reversed = value.reversed;
@override
IterableE where(bool Function(E) test) {
return value.where(test);
}
@override
IterableT whereTypeT() {
return value.whereTypeT();
}
@override
void sort([int compare(E a, E b)?]) {
_value.sort(compare);
refresh();
}
}
/pre
|`
當(dāng)我們?cè)谡{(diào)用 .obs 的時(shí)候其實(shí)內(nèi)部的實(shí)現(xiàn)源碼還是通過 RxListe(this) 進(jìn)行了一層包裝,設(shè)計(jì)這個(gè)主要的目的就是為了方便開發(fā)者進(jìn)行使用
ListExtensionE on ListE {
RxListE get obs = RxListE(this);
/// Add [item] to [ListE] only if [item] is not null.
void addNonNull(E item) {
if (item != null) add(item);
}
// /// Add [IterableE] to [ListE] only if [IterableE] is not null.
// void addAllNonNull(IterableE item) {
// if (item != null) addAll(item);
// }
/// Add [item] to ListE only if [condition] is true.
void addIf(dynamic condition, E item) {
if (condition is Condition) condition = condition();
if (condition is bool condition) add(item);
}
/// Adds [IterableE] to [ListE] only if [condition] is true.
void addAllIf(dynamic condition, IterableE items) {
if (condition is Condition) condition = condition();
if (condition is bool condition) addAll(items);
}
/// Replaces all existing items of this list with [item]
void assign(E item) {
// if (this is RxList) {
// (this as RxList)._value;
// }
}
/// Replaces all existing items of this list with [items]
void assignAll(IterableE items) {
// if (this is RxList) {
// (this as RxList)._value;
// }
clear();
addAll(items);
}
}
/pre
|`
我們對(duì) RxT([]) 、 RxListE 、 .obs 進(jìn)行了一個(gè)總結(jié),在我們平時(shí)的開發(fā)過程中建議大家使用 .obs 即可,因?yàn)檫@是最簡(jiǎn)單的方式。
使用注解@JSONField
其中name: "list"的list就是后臺(tái)返回字段名稱,deserialize(默認(rèn)true)是否參與fromJson解析,serialize(默認(rèn)true)是否參與tojson,
比如包含如下json
可以解析出來list中map的所有字段,并且每個(gè)list的map字段不同或者為null問題有會(huì)做出處理
helper文件內(nèi)容
直接傳遞上面生成的entity就可以自動(dòng)根據(jù)map解析出對(duì)應(yīng)實(shí)例,并自動(dòng)賦值
網(wǎng)絡(luò)請(qǐng)求實(shí)例
dio請(qǐng)求部分
這些操作完成后自動(dòng)生成如下文件
@JSONField作用在Field時(shí),其name不僅定義了輸入key的名稱,為了防止后臺(tái)返回?cái)?shù)據(jù)不規(guī)范,但是flutter端需要按照駝峰命名
a_b_c_entity_helper.dart類提供了eitity類的tojson和fromjson代理方法
json_convert_content.dart提供了json_convert_content.dart.fromJsonAsT方法 根據(jù)泛型來解析json成對(duì)象
本文首發(fā)在公眾號(hào) Flutter那些事 ,歡迎大家多多關(guān)注。
工具安裝:
Flutter基礎(chǔ)篇:
Flutter進(jìn)階篇:
Dart語法基礎(chǔ)篇:
Dart語法進(jìn)階篇:
說明:本文中的所有函數(shù)的引用在 main 函數(shù)中:
這里的執(zhí)行結(jié)果是:
Futue直接new就可以了。我這里沒有具體的返回?cái)?shù)據(jù),所以就用匿名函數(shù)代替了, Future future = new Future(() = null); 相當(dāng)于 FutureNull future = new Future(() = null); 泛型如果為null可以省略不寫,為了便于維護(hù)和管理,開發(fā)中建議加上泛型。
輸出結(jié)果是:
future里面有幾個(gè)函數(shù):
then :異步操作邏輯在這里寫。
whenComplete :異步完成時(shí)的回調(diào)。
catchError :捕獲異常或者異步出錯(cuò)時(shí)的回調(diào)。
因?yàn)檫@里面的異步操作過程中沒有遇到什么錯(cuò)誤,所以catchError回調(diào)不會(huì)調(diào)用。
我們可以看到執(zhí)行結(jié)果是:
我們可以看到輸出結(jié)果是: 2 1 3 和我們創(chuàng)建Future對(duì)象的先后順序完全一致。
我們可以看到結(jié)果為 1 2 3 ,和我們調(diào)用then的先后順序無關(guān)。:
當(dāng)then回調(diào)函數(shù)里面還有then回調(diào)的時(shí)候,這時(shí)候的流程跟前面就不太一樣了,也是一個(gè)大坑,也是面試經(jīng)常會(huì)被問到的一個(gè)知識(shí)點(diǎn)。
我們可以看到執(zhí)行結(jié)果如下:
結(jié)果還是一樣的:
運(yùn)行結(jié)果是:
這里再次證明了上面我的猜想: 執(zhí)行順序和和創(chuàng)建Future的先后順序有關(guān),如果有多個(gè)then嵌套執(zhí)行,先執(zhí)行外面的then,然后執(zhí)行里面的then。
執(zhí)行結(jié)果如下,我們可以看到then內(nèi)部創(chuàng)建的Future要等到then執(zhí)行完了,最后再去執(zhí)行的:
根據(jù)上文總結(jié)的特點(diǎn),我們可以不用運(yùn)行也能推斷出輸出結(jié)果:
為了驗(yàn)證我們的猜想,我們打印一下輸出結(jié)果,果然我們的證明是正確的。
我們重點(diǎn)看看 then函數(shù)的文檔說明:
then 注冊(cè)在 Future 完成時(shí)調(diào)用的回調(diào)。
當(dāng)這個(gè) Future 用一個(gè) value 完成時(shí),將使用該值調(diào)用 onValue 回調(diào)。
如果 Future 已經(jīng)完成,則不會(huì)立即調(diào)用回調(diào),而是將在稍后的 microtask(微任務(wù)) 中調(diào)度。
如果回調(diào)返回 Future ,那么 then 返回的 future 將與 callback 返回的 future 結(jié)果相同。
onError 回調(diào)必須接受一個(gè)參數(shù)或兩個(gè)參數(shù),后者是[StackTrace]。
如果 onError 接受兩個(gè)參數(shù),則使用錯(cuò)誤和堆棧跟蹤時(shí)調(diào)用它,否則僅使用錯(cuò)誤對(duì)象時(shí)候調(diào)用它。
onError 回調(diào)必須返回一個(gè)可用于完成返回的future的值或future,因此它必須是可賦值給 FutureOr R 的東西。
返回一個(gè)新的 Future ,該 Future 是通過調(diào)用 onValue (如果這個(gè)Future是通過一個(gè)value完成的)或' onError (如果這個(gè)Future是通過一個(gè)error完成的)的結(jié)果完成的。
如果調(diào)用的回調(diào)拋出異常,返回的 future 將使用拋出的錯(cuò)誤和錯(cuò)誤的堆棧跟蹤完成。在 onError 的情況下,如果拋出的異常與 onError 的錯(cuò)誤參數(shù)“相同(identical)”,則視為重新拋出,并使用原始堆棧跟蹤替代
如果回調(diào)返回 Future ,則 then 返回的 Future 將以與回調(diào)返回的 Future 相同的結(jié)果完成。
如果未給出 onError ,并且后續(xù)程序走了剛出現(xiàn)了錯(cuò)誤,則錯(cuò)誤將直接轉(zhuǎn)發(fā)給返回的 Future 。
在大多數(shù)情況下,單獨(dú)使用 catchError 更可讀,可能使用 test 參數(shù),而不是在單個(gè) then 調(diào)用中同時(shí)處理 value 和 error 。
請(qǐng)注意,在添加監(jiān)聽器(listener)之前, future 不會(huì)延遲報(bào)告錯(cuò)誤。如果第一個(gè) then 或 catchError 調(diào)用在 future 完成后發(fā)生 error ,那么 error 將報(bào)告為未處理的錯(cuò)誤。
將枚舉類型更改為常規(guī)類或?qū)⒊R?guī)類更改為枚舉類型時(shí),熱重載(r)不起作用。 需要hot restart(cmd + shift + r)
修改泛型類型聲明后,熱重裝將無法工作。 例如,以下操作將無效:
Widget 快速替換 、 包裝 、 移動(dòng) 、 刪除 、 抽取成變量 、 抽取成方法
焦點(diǎn)放到相應(yīng)的widget上, 然后 cmd + . 如果提示沒有相關(guān)操作,多試幾次
網(wǎng)站欄目:flutter泛型,float型
新聞來源:http://chinadenli.net/article10/dsggjdo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護(hù)、外貿(mào)網(wǎng)站建設(shè)、網(wǎng)站策劃、網(wǎng)站內(nèi)鏈、小程序開發(fā)、網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)