Flutter學(xué)習(xí)筆記——用戶界面

以下為對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布局約束理解

  • 布局流程:
    1. widget從parent中獲取四個(gè)約束歹叮,分別是最小和最大寬度、最小和最大高度铆帽;
    2. widget將約束一個(gè)一個(gè)地傳遞給子widget咆耿,并讓子widget根據(jù)約束條件設(shè)定其自身的大小爹橱;
    3. widget根據(jù)子widget的大小一個(gè)一個(gè)進(jìn)行布局萨螺;
    4. widget將自身的大小上報(bào)給parent。
  • 布局流程會(huì)導(dǎo)致以下三個(gè)限制:
    1. 一個(gè)widget最終布局大小需要受到parent的約束限制愧驱,不是想要什么大小都可以慰技;
    2. 一個(gè)widget不能知道也不能決定其在屏幕中的位置,widget的布局由其parent決定组砚;
    3. 只有考慮整棵widget樹才能確定widget的大小和位置吻商,不能準(zhǔn)確地定義某個(gè)widget的位置和大小。
  • Container布局行為:
    1. 若沒有子widget糟红,沒有設(shè)置寬高艾帐,沒有約束,parent是無界約束盆偿,Container會(huì)填充parent柒爸,并希望讓自身盡量的小
    2. 若沒有子widget,沒有設(shè)置alignment事扭,設(shè)置了寬高或者有約束捎稚,Container會(huì)在滿足自身約束和parent約束的情況下盡量的小
    3. 若沒有子widget,沒有設(shè)置寬高求橄,沒有約束今野,沒有設(shè)置alignment,parent是有界約束罐农,那么Container會(huì)盡量的擴(kuò)大以滿足parent的約束
    4. 若設(shè)置了alignment条霜,parent無界約束,那么Container盡量縮小為子widget大小
    5. 若設(shè)置了alignment啃匿,parent有界約束蛔外,那么Container擴(kuò)大為parent約束大小蛆楞,并將子widget根據(jù)alignment設(shè)置來布局
    6. 若只有子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,
        );
      },
    );
  }
}

動(dòng)畫效果

  • 可以對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)畫是否完成括改。

學(xué)習(xí)資源

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市家坎,隨后出現(xiàn)的幾起案子嘱能,更是在濱河造成了極大的恐慌,老刑警劉巖虱疏,帶你破解...
    沈念sama閱讀 194,524評(píng)論 5 460
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惹骂,死亡現(xiàn)場離奇詭異,居然都是意外死亡做瞪,警方通過查閱死者的電腦和手機(jī)对粪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 81,869評(píng)論 2 371
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來装蓬,“玉大人著拭,你說我怎么就攤上這事‰怪悖” “怎么了儡遮?”我有些...
    開封第一講書人閱讀 141,813評(píng)論 0 320
  • 文/不壞的土叔 我叫張陵,是天一觀的道長暗赶。 經(jīng)常有香客問我鄙币,道長,這世上最難降的妖魔是什么蹂随? 我笑而不...
    開封第一講書人閱讀 52,210評(píng)論 1 263
  • 正文 為了忘掉前任十嘿,我火速辦了婚禮,結(jié)果婚禮上岳锁,老公的妹妹穿的比我還像新娘绩衷。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 61,085評(píng)論 4 355
  • 文/花漫 我一把揭開白布唇聘。 她就那樣靜靜地躺著版姑,像睡著了一般柱搜。 火紅的嫁衣襯著肌膚如雪迟郎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 46,117評(píng)論 1 272
  • 那天聪蘸,我揣著相機(jī)與錄音宪肖,去河邊找鬼。 笑死健爬,一個(gè)胖子當(dāng)著我的面吹牛控乾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播娜遵,決...
    沈念sama閱讀 36,533評(píng)論 3 381
  • 文/蒼蘭香墨 我猛地睜開眼蜕衡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了设拟?” 一聲冷哼從身側(cè)響起慨仿,我...
    開封第一講書人閱讀 35,219評(píng)論 0 253
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎纳胧,沒想到半個(gè)月后镰吆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 39,487評(píng)論 1 290
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡跑慕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 34,582評(píng)論 2 309
  • 正文 我和宋清朗相戀三年万皿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片核行。...
    茶點(diǎn)故事閱讀 36,362評(píng)論 1 326
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡牢硅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出芝雪,到底是詐尸還是另有隱情唤衫,我是刑警寧澤,帶...
    沈念sama閱讀 32,218評(píng)論 3 312
  • 正文 年R本政府宣布绵脯,位于F島的核電站佳励,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蛆挫。R本人自食惡果不足惜赃承,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 37,589評(píng)論 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悴侵。 院中可真熱鬧瞧剖,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 28,899評(píng)論 0 17
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至捉撮,卻和暖如春怕品,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背巾遭。 一陣腳步聲響...
    開封第一講書人閱讀 30,176評(píng)論 1 250
  • 我被黑心中介騙來泰國打工肉康, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人灼舍。 一個(gè)月前我還...
    沈念sama閱讀 41,503評(píng)論 2 341
  • 正文 我出身青樓吼和,卻偏偏與公主長得像,于是被迫代替她去往敵國和親骑素。 傳聞我的和親對象是個(gè)殘疾皇子炫乓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 40,707評(píng)論 2 335