以下為對Flutter官網(wǎng)的學(xué)習(xí)總結(jié)鳖敷,如果你想快速掌握知識(shí)點(diǎn),或者想復(fù)習(xí)一下官網(wǎng)學(xué)習(xí)的內(nèi)容绍些,那么值得看看膘滨。
轉(zhuǎn)載請注明出處:Lawrence_Shen
用戶界面
widgets介紹
- Flutter一切都是widget,包括設(shè)置padding的container。
- 幾乎所有widget都通過build方法聲明其UI
- StatelessWidget用于固定樣式的widget握截,StatefulWidget用于根據(jù)數(shù)據(jù)變化的widget飞崖。
- StatefulWidget通過createState關(guān)聯(lián)私有的State對象,并通過setState()方法更新數(shù)據(jù)并通知UI變化谨胞。
- 更新UI時(shí)Flutter會(huì)通過比較前后widget樹來計(jì)算差異固歪,widget只是保存了樣式信息,它的重建可以考慮是輕量級(jí)的胯努。widget樹會(huì)對應(yīng)到element樹牢裳,并通過element樹創(chuàng)建Render樹。相同類型widget會(huì)重用element和render對象叶沛。
- State對象的生命周期跨越其對應(yīng)的widget對象build方法蒲讯,比widget本身生命周期要長
- State調(diào)用流程大致為initState -> build -> dispose,可以在initState做初始化操作灰署,在dispose中做清理操作
- didChangeDependencies會(huì)在initState和build之間調(diào)用判帮,當(dāng)父widget有InheritedWidget變化時(shí)也會(huì)被調(diào)用
- InheritedWidget可用于在widget樹中給子widget共享數(shù)據(jù),通常通過of方法調(diào)用context.inheritFromWidgetOfExactType返回?fù)碛泄蚕頂?shù)據(jù)的InheritedWidget對象
- key控制widget重建時(shí)與哪些其他widget進(jìn)行匹配溉箕,從而保持正確的state狀態(tài)晦墙,一般用在widget的添加刪除或者重排序中控制widget重用
- key分為Local key(value key表示根據(jù)某個(gè)值判斷、Object key表示根據(jù)某個(gè)對象判斷约巷、Unique key表示每個(gè)widget都不一樣) 和Global key(表示不同頁面的widget共享)偎痛,
構(gòu)建layouts
Flutter中的layouts
- 可以通過Row和Column構(gòu)建復(fù)雜頁面
- mainAxisAlignment控制主軸對齊方式旱捧,crossAxisAlignment控制次軸對齊方式
- 使用Expanded widget來fit window独郎,flex來指定比例
- 將布局widget賦值給變量,通過變量組合布局減少層級(jí)嵌套
- 使用Container設(shè)置margin枚赡、border氓癌、pandding和背景
- GridView.extend中maxCrossAxisExtent設(shè)置每個(gè)item的最大寬度,mainAxisSpacing設(shè)置主軸item之間的間隔贫橙,crossAxisSpacing設(shè)置次軸item之間的間隔贪婉,childAspectRatio設(shè)置item寬高比例
- GridView.builder用于數(shù)量較多的item展示,僅加載當(dāng)前可見的部分卢肃,GridView.count用于加載少量固定數(shù)目的item并指定每行item格式疲迂,GridView.extend用于加載少量固定item并指定每行item最大寬度
- GridView中通過SliverGridDelegate控制子widget如何布局,通過SliverChildDelegate來獲取子widget莫湘,可以通過自定義Delegate來實(shí)現(xiàn)自由或者疊加布局尤蒿。
- GridView和ListView都繼承自BoxScrollView
- 大量數(shù)據(jù)需使用ListView.builder并在itemBuilder回調(diào)中創(chuàng)建并提供widget;如果列表的item樣式可以提前構(gòu)建則可以直接使用new ListView幅垮;ListView.separated除了itemBuilder之外還有個(gè)separatorBuilder用來定義分隔線樣式腰池;ListView.custom通過提供自定義的SliverChildDelegate來實(shí)現(xiàn)自定義的列表加載和緩存邏輯。
- Stack用于widget的堆疊,可以做漸變的圖片陰影
- Card內(nèi)部內(nèi)容不能夠滾動(dòng)示弓,可以自定義圓角和陰影大小
- ListTitle是方便構(gòu)建至多三行文字加上前后圖標(biāo)的列表item widget
layout使用例子
- 使用Expanded widget占滿剩余空間讳侨,子widget設(shè)置CrossAxisAlignment.start表示從前開始
- Text softwrap控制是否需要自動(dòng)換行
- 修改pubspec.yaml設(shè)置assets目錄,例如:flutter: assets: [images/]
- Image.asset中設(shè)置fit:BoxFit.cover 表示圖片應(yīng)該以最小的大小占滿box空間
- 使用ListView代替Column保證小屏幕手機(jī)中空間可以滾動(dòng)
創(chuàng)建自適應(yīng)UI應(yīng)用
- 使用LayoutBuilder的BoxConstraints獲取當(dāng)前widget的寬高比例從而調(diào)整子widget布局
- 使用MediaQuery.of()獲取屏幕寬高和旋轉(zhuǎn)方向等設(shè)備信息從而控制整體布局樣式
- AspectRatio控制子widget的寬高比例
- CustomSingleChildLayout奏属、CustomMultiChildLayout將子widget的布局委托給ChildLayoutDelegate進(jìn)行控制
- FittedBox:當(dāng)子widget比父widget大時(shí)跨跨,通過FittedBox可以設(shè)置子widget的縮放方式
- FractionallySizedBox可以設(shè)置子widget占據(jù)其空間的寬高百分比
- MediaQueryData中padding指代周邊有多少不能繪制的區(qū)域不計(jì)算被鍵盤等遮擋的區(qū)域,viewPadding指的是周邊有多少不能被繪制的區(qū)域不受鍵盤等遮擋影響拍皮,viewInsert表示周邊有多少區(qū)域被鍵盤等遮擋了
- OrientationBuilder獲取屏幕是否旋轉(zhuǎn)
Constraints布局約束理解
- 布局流程:
- widget從parent中獲取四個(gè)約束歹叮,分別是最小和最大寬度、最小和最大高度铆帽;
- widget將約束一個(gè)一個(gè)地傳遞給子widget咆耿,并讓子widget根據(jù)約束條件設(shè)定其自身的大小爹橱;
- widget根據(jù)子widget的大小一個(gè)一個(gè)進(jìn)行布局萨螺;
- widget將自身的大小上報(bào)給parent。
- 布局流程會(huì)導(dǎo)致以下三個(gè)限制:
- 一個(gè)widget最終布局大小需要受到parent的約束限制愧驱,不是想要什么大小都可以慰技;
- 一個(gè)widget不能知道也不能決定其在屏幕中的位置,widget的布局由其parent決定组砚;
- 只有考慮整棵widget樹才能確定widget的大小和位置吻商,不能準(zhǔn)確地定義某個(gè)widget的位置和大小。
- Container布局行為:
- 若沒有子widget糟红,沒有設(shè)置寬高艾帐,沒有約束,parent是無界約束盆偿,Container會(huì)填充parent柒爸,并希望讓自身盡量的小
- 若沒有子widget,沒有設(shè)置alignment事扭,設(shè)置了寬高或者有約束捎稚,Container會(huì)在滿足自身約束和parent約束的情況下盡量的小
- 若沒有子widget,沒有設(shè)置寬高求橄,沒有約束今野,沒有設(shè)置alignment,parent是有界約束罐农,那么Container會(huì)盡量的擴(kuò)大以滿足parent的約束
- 若設(shè)置了alignment条霜,parent無界約束,那么Container盡量縮小為子widget大小
- 若設(shè)置了alignment啃匿,parent有界約束蛔外,那么Container擴(kuò)大為parent約束大小蛆楞,并將子widget根據(jù)alignment設(shè)置來布局
- 若只有子widget,沒有設(shè)置寬高夹厌,沒有約束豹爹,沒有alignment,Container會(huì)將parent的約束傳遞給子widget矛纹,并盡量縮小為子widget大小
- 布局中FittedBox可以控制子widget在約束空間中的布局臂聋,例如設(shè)置自動(dòng)縮小文字或者縮放圖片
- tight約束表示固定寬高約束,loose約束表示在設(shè)置最大寬高基礎(chǔ)上盡量的縮小
Box constraints邊界約束
- 有三種box或南,分別是無限擴(kuò)展例如Center或者ListView孩等、子widget決定例如Trnasform和Opacity、固定大小例如Image和Text
- 類似于當(dāng)一個(gè)豎向的ListView嵌套進(jìn)了一個(gè)橫向的ListView采够,會(huì)造成無界約束狀態(tài)(Unbounded constraints)肄方,這種狀態(tài)會(huì)使得子widget可以在兩個(gè)方向無限擴(kuò)展導(dǎo)致錯(cuò)誤
- Flex boxs指的是Row和Column,表示當(dāng)其處于一個(gè)有界的區(qū)域會(huì)不斷擴(kuò)展至給定大小蹬癌,當(dāng)其處于一個(gè)無界區(qū)域會(huì)適應(yīng)他的子widget大小权她。
- 如果將Flex box放置于類似于ListView的widget中,那么flex box中不能有類似于Expanded的widget逝薪,這會(huì)導(dǎo)致類似于Expanded的widget無限擴(kuò)大造成錯(cuò)誤
- Column的寬度和Row的高度不能設(shè)置為無界的隅要,否則他們的子widget將無法布局
加入互動(dòng)邏輯
- 可交互的widget有三點(diǎn),一是有兩個(gè)類董济,分別繼承StatefulWidget和State步清,二是State類中擁有可變的狀態(tài)和build方法,三是當(dāng)狀態(tài)變化虏肾,調(diào)用setState()方法對widget進(jìn)行重繪廓啊。
- 將Text放在SizedBox中可以防止當(dāng)文字變化時(shí)由于寬度變化帶來的位置抖動(dòng)
- 當(dāng)調(diào)用setState({})方法時(shí),會(huì)先執(zhí)行l(wèi)ambda邏輯询微,然后調(diào)用_element.markNeedsBuild()標(biāo)記當(dāng)前element為dirty狀態(tài)并在下一幀根據(jù)修改后的狀態(tài)進(jìn)行重繪
- 有三種常見的管理狀態(tài)方法崖瞭,分別是:widget自己管理自己的狀態(tài)狂巢、父widget管理狀態(tài)撑毛、混合前兩種方式
- 如果狀態(tài)是用戶數(shù)據(jù),那么最好在父widget管理唧领。如果狀態(tài)是與界面效果有關(guān)的例如動(dòng)畫藻雌,那么最好在widget自身內(nèi)管理狀態(tài)。如果不確定最好先在父widget中管理斩个,因?yàn)榇蠖鄶?shù)情況外層需要對狀態(tài)數(shù)據(jù)進(jìn)行處理并更新子widget胯杭,外層處理狀態(tài)也有利于子widget保持整潔。當(dāng)widget既包含用戶狀態(tài)又包含外部不關(guān)注的自身界面效果狀態(tài)則使用混合狀態(tài)管理模式受啥。
- 對于必須傳入的參數(shù)使用@require注解
添加assets和圖片
- 在pubspec.yaml中聲明assets文件夾的路徑聲明做个,如果需要添加子文件夾的話需要單獨(dú)列出
- 聲明assets時(shí)會(huì)同時(shí)查找其定義的子文件夾是否有同名的文件鸽心,如果有的話會(huì)把同名的文件同時(shí)引入,這是為了方便引入不同分辨率的圖片資源
- 使用DefaultAssetBundle.of(context).load()或loadString()方法加載asset文本資源居暖,其中context最好使用當(dāng)前widget的BuildContext顽频,這有利于父widget在測試或者本地化時(shí)在運(yùn)行時(shí)替換不同的AssetBundle。
- 當(dāng)不能獲取widget context的地方太闺,可以使用rootBundle來加載文本資源
- 對于圖片資源糯景,可以使用相同的圖片命名并放在2.0x和3.0x文件夾中,不同dp/px比例的手機(jī)會(huì)自動(dòng)選用合適大小的資源
- 使用AssetImage加載圖片會(huì)自動(dòng)選擇對應(yīng)分辨率的圖片省骂,如果需要加載不同package的圖片蟀淮,需要在AssetImange中指定package
- 對于不在同一個(gè)package的圖片資源,也需要在pubspec.yaml文件中定義钞澳,例如需要引用package為fancy_backgrounds的圖資源怠惶,需要在當(dāng)前的pubspec.yaml中定義assets路徑為packages/fancy_backgrounds/xxx(圖片在fancy_backgrounds中l(wèi)ibs目錄下的相對位置)
- 在Android中使用flutter的asset資源,使用PluginRegistry.Registrar.lookupKeyForAsset()方法獲取key轧粟,并使用AssetManager.openFd(key)方法獲取AssetFileDescriptor
- 在iOS中使用flutter的asset資源甚疟,可以使用registrar lookupKeyForAsset或者key,然后通過mainBundle pathForResource:key ofType獲取asset路徑逃延。如果使用了ios_platform_images插件览妖,那么可以直接使用OC中的UIImage flutterImageWithName或者Swift中的UIImage.flutterImageNamed獲取。
- flutter中使用iOS的圖片可以使用ios_platform_images插件中的IosPlatformImages.load方法
- 啟動(dòng)頁會(huì)在Flutter繪制第一幀的時(shí)候被替換揽祥,如果在main方法中不調(diào)用runApp方法讽膏,那么啟動(dòng)頁將一直展示。
- 加入啟動(dòng)頁的方式需要使用Android和iOS的本身的方式加入拄丰。
頁面導(dǎo)航
導(dǎo)航至新頁面并返回
- route在安卓中相當(dāng)于Activity府树,在iOS中相當(dāng)于ViewController,在Flutter中料按,route表示的只是一個(gè)widget
- 頁面導(dǎo)航的步驟:創(chuàng)建兩個(gè)route奄侠,使用Navigator.push()導(dǎo)航到第二個(gè)route,使用Navigator.pop()返回到上一個(gè)route
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
)
- 通過創(chuàng)建MaterialPageRoute適配安卓和iOS頁面跳轉(zhuǎn)的動(dòng)效载矿,通過設(shè)置maintainState釋放上一個(gè)頁面的內(nèi)存垄潮,通過fullscreenDialog設(shè)置是否全屏dialog樣式
使用具名路由跳轉(zhuǎn)
- 當(dāng)頁面之間跳轉(zhuǎn)較多時(shí),在MaterialApp中聲明路由關(guān)系闷盔,然后使用具名路由導(dǎo)航Navigator.pushNamed()可以減少代碼重復(fù)
MaterialApp(
// Start the app with the "/" named route. In this case, the app starts
// on the FirstScreen widget.
initialRoute: '/',
routes: {
// When navigating to the "/" route, build the FirstScreen widget.
'/': (context) => FirstScreen(),
// When navigating to the "/second" route, build the SecondScreen widget.
'/second': (context) => SecondScreen(),
},
);
Navigator.pushNamed(context, '/second');
非具名路由之間傳遞數(shù)據(jù)
- 使用非具名路由跳轉(zhuǎn)有兩種頁面間傳遞數(shù)據(jù)的做法弯洗,一種是跳轉(zhuǎn)新頁面時(shí)在Widget的構(gòu)造函數(shù)中傳入數(shù)據(jù);第二種是通過設(shè)置MaterialPageRoute的RouteSettings中的arguments逢勾,并在跳轉(zhuǎn)頁面中使用ModalRoute.of(context).settings.arguments獲取
// 第一種方法
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(todo: todos[index]),
),
);
// 第二種方法—設(shè)置參數(shù)
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(),
// Pass the arguments as part of the RouteSettings. The
// DetailScreen reads the arguments from these settings.
settings: RouteSettings(
arguments: todos[index],
),
),
);
// 第二種方法—獲取參數(shù)
final Todo todo = ModalRoute.of(context).settings.arguments;
具名路由之間傳遞數(shù)據(jù)
- 使用具名路由跳轉(zhuǎn)有兩種頁面間傳輸?shù)淖龇嫡环N是使用Navigator.pushNamed并設(shè)置arguments,然后在跳轉(zhuǎn)頁面使用ModalRoute.of(context).settings.arguments獲饶绻啊逃贝;第二種是是使用Navigator.pushNamed并設(shè)置arguments谣辞,然后在MaterialApp的onGenerateRoute方法中獲取settings.arguments并在返回的MaterialPageRoute中通過構(gòu)造函數(shù)設(shè)置給跳轉(zhuǎn)頁面
// 第一種方法—設(shè)置
Navigator.pushNamed(
context,
ExtractArgumentsScreen.routeName,
arguments: ScreenArguments(
'Extract Arguments Screen',
'This message is extracted in the build method.',
),
);
// 第一種方法—獲取
final ScreenArguments args = ModalRoute.of(context).settings.arguments;
// 第二種方法—通過onGenerateRoute方法構(gòu)造目標(biāo)頁面并傳遞參數(shù)
MaterialApp(
// Provide a function to handle named routes. Use this function to
// identify the named route being pushed, and create the correct
// screen.
onGenerateRoute: (settings) {
// If you push the PassArguments route
if (settings.name == PassArgumentsScreen.routeName) {
// Cast the arguments to the correct type: ScreenArguments.
final ScreenArguments args = settings.arguments;
// Then, extract the required data from the arguments and
// pass the data to the correct screen.
return MaterialPageRoute(
builder: (context) {
return PassArgumentsScreen(
title: args.title,
message: args.message,
);
},
);
}
},
);
從目標(biāo)頁面返回?cái)?shù)據(jù)
- 使用Navigator.pop設(shè)置數(shù)據(jù),并使用await獲取Navigator.push返回結(jié)果
// 設(shè)置數(shù)據(jù)
Navigator.pop(context, 'Yep!');
// 獲取數(shù)據(jù)
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => nextScreen()),
);
動(dòng)畫
Implicit動(dòng)畫
- 對于普通的修改大小和形狀等的屬性動(dòng)畫可以使用Implicit動(dòng)畫沐扳,設(shè)置動(dòng)畫時(shí)間duration潦闲、動(dòng)畫效果curve。常用的Implicit動(dòng)畫有以下這些:
- Align -> AnimatedAlign
- Container -> AnimatedContainer
- DefaulTextStyle -> AnimatedDefaulTextStyle
- Opacity -> AnimatedOpacity
- Padding -> AnimatedPadding
- PhysicalModel -> AnimatedPhysicalModel
- Positioned -> AnimatedPositioned
- PositionedDirectional -> AnimatedPositionedDirectional
- Theme -> AnimatedThemeSize -> AnimatedSize
- 若沒有能滿足需求的Implicit動(dòng)畫widget迫皱,那么可以嘗試使用TweenAnimationBuilder來實(shí)現(xiàn)自定義屬性動(dòng)畫
Explicit動(dòng)畫
- 如果想要對動(dòng)畫進(jìn)行播放控制歉闰,那么需要使用Explicit動(dòng)畫,并在turns中指定AnimationController卓起。常用的Explicit動(dòng)畫有以下這些:
- SizeTransition
- FadeTransition
- AlignTransition
- ScaleTransition
- SlideTransition
- RotationTransition
- PositionedTransition
- DecoratedBoxTransition
- DefaultTextStyleTransition
- RelativePositionedTransition
- StatusTransitionWidget
- Explicit動(dòng)畫的幾個(gè)概念:
-
Animaion<double>
:CurvedAnimation和AnimationController都繼承自Animaion<double>
和敬,通過Animaion可以獲取動(dòng)畫的狀態(tài)目前的插值,但是Animaion不會(huì)參與動(dòng)畫的繪制 - CurvedAnimation用于定義動(dòng)畫的非線性過程戏阅;AnimationController用于控制動(dòng)畫播放進(jìn)度昼弟,需要傳入TickerProvider來減少處于屏幕外的動(dòng)畫資源消耗;Tween用于對Animation的范圍進(jìn)行轉(zhuǎn)化奕筐;Animation可以通過設(shè)置Listners和StatusListeners來監(jiān)聽動(dòng)畫狀態(tài)舱痘。
-
- SingleTickerProviderStateMixin是TickerProvider的實(shí)現(xiàn);mixin是線性疊加的代碼繼承离赫,最后的類會(huì)覆蓋前面類方法芭逝,mixin是類的一層一層疊加,類型判斷可以為每一層的類渊胸,mixin更多強(qiáng)調(diào)的是代碼的復(fù)用而不是類繼承關(guān)系旬盯,mixin是一種類型不能實(shí)例化。參考:When to use mixins and when to use interfaces in Dart?
Mixins is all about how a class does what it does, it's inheriting and sharing concrete implementation. Interfaces is all about what a class is, it is the abstract signature and promises that the class must satisfy.
- 如果想對動(dòng)畫進(jìn)行播放控制翎猛,但是沒有現(xiàn)成的Explicit動(dòng)畫胖翰,那么可以使用AnimatedBuilder或者AnimatedWidget
- AnimatedWidget需要傳入一個(gè)listenable,一般是Animation切厘,也可以是 ChangeNotifier 或者 ValueNotifier萨咳,AnimatedWidget會(huì)在listenable變化的時(shí)候調(diào)用setState重新build從而產(chǎn)生動(dòng)畫效果。
// 不使用AnimatedWidget animation = Tween<double>().animate(controller) ..addListener(() { setState((){}); }; ... Widget build(BuildContext context) => GeneralWidget(animation); // 使用AnimatedWidget animation = Tween<double>().animate(controller); ... Widget build(BuildContext context) => AnimatedWidget(animation);
- 繼承自AnimatedWidget一般命名為FooTransition,而繼承自ImplicitlyAnimatedWidget一般命名為AnimatedFoo疫稿,這里Foo指的是沒有加入動(dòng)畫的widget名字培他。AnimatedWidget與ImplicitlyAnimatedWidget的最大區(qū)別在于前者需要使用者自己維護(hù)一個(gè)Animation,可以對動(dòng)畫進(jìn)行控制而克;后者自身會(huì)帶一個(gè)Animation并維護(hù)自身的動(dòng)畫狀態(tài)靶壮,不需要使用者參與管理怔毛。
- 如果構(gòu)造復(fù)雜的動(dòng)畫员萍,可以使用AnimatedBuilder。為了性能考慮拣度,可以在AnimatedBuilder中指定動(dòng)畫元素child碎绎,并在builder中將child傳入動(dòng)畫中螃壤,避免每次動(dòng)畫tick回調(diào)都會(huì)重新build child。
class _SpinnerState extends State with SingleTickerProviderStateMixin {
AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 10),
vsync: this,
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
child: Container(
width: 200.0,
height: 200.0,
color: Colors.green,
child: const Center(
child: Text('Wee'),
),
),
builder: (BuildContext context, Widget child) {
return Transform.rotate(
angle: _controller.value * 2.0 * math.pi,
child: child,
);
},
);
}
}
- 可以對Animation使用addStatusListener添加動(dòng)畫狀態(tài)監(jiān)聽筋帖。
- 使用SpringSimulation可以產(chǎn)生物理的彈性效果奸晴。
Hero動(dòng)畫
- 頁面間共享元素:使用Hero包裹頁面間的共享widget,并設(shè)置一個(gè)相同的tag日麸。當(dāng)路由push和pop的時(shí)候都會(huì)觸發(fā)動(dòng)畫寄啼。
- 共享元素能在兩個(gè)頁面之間過渡,其原理是使用了application overlay widget代箭,并使用RecTween生成過渡動(dòng)畫墩划。
- debug過程中可以使用
scheduler.dart
中的timeDilation減慢動(dòng)畫效果 - 在Hero中聲明createRectTween為MaterialRectCenterArcTween可以使得動(dòng)畫產(chǎn)生中心變化的效果,默認(rèn)的情況是使用Hero的四個(gè)角嗡综,如果形狀變化的動(dòng)畫可以會(huì)產(chǎn)生變形乙帮。
- 如果想要更自然的形狀變化效果,可以根據(jù)Hero動(dòng)畫過程中的大小變化動(dòng)態(tài)設(shè)置目標(biāo)位置大小极景,例子見radial_hero_animation_animate_rectclip
交替動(dòng)畫
- 通過指定Animation的curve為Interval來設(shè)置動(dòng)畫在Controller0到1之間出現(xiàn)的時(shí)機(jī)
borderRadius = BorderRadiusTween( begin: BorderRadius.circular(4.0), end: BorderRadius.circular(75.0), ).animate( CurvedAnimation( parent: controller, curve: Interval( 0.375, 0.500, curve: Curves.ease, ), ), ),
高級(jí)UI
Silvers
- Silver表示一部分可以滑動(dòng)的區(qū)域察净。
- ListView和GridView中分別用到了SliverList和SliverGrid,他們都繼承自SliverMultiBoxAdaptorWidget盼樟,而SliverChildDelegate則是這兩個(gè)類的構(gòu)造函數(shù)的主要參數(shù)氢卡。SliverChildDelegate有SliverChildBuilderDelegate和SliverChildListDelegate兩個(gè)實(shí)現(xiàn)類。這里delegate的意思是負(fù)責(zé)創(chuàng)建列表的item晨缴,以實(shí)現(xiàn)根據(jù)當(dāng)前可視窗口來構(gòu)建item异吻,避免一次性大量構(gòu)建沒必要的item的問題。
- 在SliverWidget對應(yīng)的render對象是RenderSliver喜庞【骼耍可滾動(dòng)widget外部是由一個(gè)Viewport包裹。RenderSliver就是布局在Viewport之中的子節(jié)點(diǎn)延都,可以接受viewport的可視窗口信息雷猪,以實(shí)現(xiàn)各種滾動(dòng)效果,例如可折疊標(biāo)題和parallax視差效果晰房。
Gestures
- 有兩種方式可以監(jiān)聽手勢事件求摇,分別是Listener中監(jiān)聽Point的Down,Move,Up,Cancel操作,還有GestureDetector中監(jiān)聽更為直觀的手勢操作殊者,例如onTap与境,onLongPress,onScaleUpdate等猖吴。前者可以用于獲取點(diǎn)擊的坐標(biāo)以及手勢的詳細(xì)信息摔刁,后者可以用于快速實(shí)現(xiàn)手勢監(jiān)聽。
- 當(dāng)出現(xiàn)手勢監(jiān)聽沖突時(shí)海蔽,會(huì)在手勢動(dòng)作后延遲一段時(shí)間來對監(jiān)聽進(jìn)行優(yōu)勝競爭共屈,勝利的會(huì)得到監(jiān)聽回調(diào)
- 可以使用CustomScrollView+Sliver的方式實(shí)現(xiàn)多個(gè)樣式的滑動(dòng)組合绑谣,解決滑動(dòng)沖突
閃屏
iOS
- 通過Xcode storyboard進(jìn)行設(shè)置
Android
- 設(shè)置應(yīng)用打開閃屏的設(shè)置與原生方式一樣,都是給第一個(gè)打開的activity設(shè)置主題
- 設(shè)置name為io.flutter.embedding.android.NormalTheme的meta-data來定義正常主題拗引,這樣就會(huì)使得頁面從啟動(dòng)的主題轉(zhuǎn)變?yōu)檎5闹黝}借宵。
- 在Android Activity啟動(dòng)后,還需要初始化Dart isolate矾削,這段時(shí)間可以再設(shè)置閃屏
- 設(shè)置Flutter的閃屏有兩種方法壤玫,一種是設(shè)置展示一個(gè)drawable,可以在Activity的Manifest中設(shè)置name為
io.flutter.embedding.android.SplashScreenDrawable
的meta-data并指定drawable資源哼凯,或在Fragment中重寫provideSplashScreen方法返回一個(gè)DrawableSplashScreen對象垦细。第二種方法是實(shí)現(xiàn)SplashScreen接口,通過createSplashView提供自定義閃屏view挡逼,并通過transitionToFlutter方法標(biāo)記閃屏view動(dòng)畫是否完成括改。