Flutter知識點(diǎn)

    1. 級聯(lián)操作符

    Dart 中 級聯(lián)操作符 可以返回對象從而繼續(xù)執(zhí)行其他方法:

    new Column(
          children: [
            new Container(),
          ]
            ..addAll(List.generate(3, (index) {
              return Container();
            }))
            ..addAll([
              new HorizontalLine(height: 5),
            ]),
        );
    
    1. 一次性執(zhí)行匿名回調(diào)函數(shù)

    Dart 支持一次性執(zhí)行匿名回調(diào)函數(shù),如:

    void test() {
      () {
        print('hi');
      }();
    }
    

    也可在組件中運(yùn)用并接收參數(shù),如:

    @override
    Widget build(BuildContext context) {
      return new Scaffold(
        body: new Text((String text) {
          return text;
        }('我是文字')),
    }
    
    1. 可選方法參數(shù)

    Dart 方法可以設(shè)置 參數(shù)默認(rèn)值指定名稱 。

    比如: getDetail(Sting userName, reposName, {branch = "master"}){} 方法,這里 branch 不設(shè)置的話,默認(rèn)是 “master” 。參數(shù)類型 可以指定或者不指定。調(diào)用效果: getRepositoryDetailDao(“aaa", "bbbb", branch: "dev"); 。

    1. Assert (斷言)

    assert 只在檢查模式有效,在開發(fā)過程中,assert(unicorn == null); 只有條件為真才正常,否則直接拋出異常,一般用在開發(fā)過程中,某些地方不應(yīng)該出現(xiàn)什么狀態(tài)的判斷。

    1. 擴(kuò)展

    擴(kuò)展可以給指定類型的數(shù)值增加獲取方法,如( ScreenUtil 給 num 加的擴(kuò)展):

    extension SizeExtension on num {
      num get w => ScreenUtil().setWidth(this);
    
      num get h => ScreenUtil().setHeight(this);
    
      num get sp => ScreenUtil().setSp(this);
    
      num get ssp => ScreenUtil().setSp(this, allowFontScalingSelf: true);
    }
    

    比如我想獲取一個(gè) ScreenUtil 適配的50寬度值:

    Container(width: 50.w)
    

    等同于:

    Container(width: ScreenUtil().setWidth(50))
    

    從而簡化了代碼,提升了開發(fā)效率。

    1. runZoned

    可以在自己的區(qū)域中運(yùn)行指定代碼(根據(jù) zoneSpecification 使用 Zone.fork 創(chuàng)建的區(qū)域),如果代碼出現(xiàn)錯(cuò)誤將拋出全局錯(cuò)誤在 onError:

    runZoned(() {
        runApp(MyApp());
      }, onError: (Object obj, StackTrace stack) {
        print(obj);
        print(stack);
      });
    

    我們通用的錯(cuò)誤信息統(tǒng)計(jì)平臺如 sentry,一般都是放在 runZoned 的 onError 內(nèi)。

組件

1. ListView 自動(dòng)內(nèi)邊距

ListView 會在腳手架中未寫 AppBar 的情況下自動(dòng)添加 padding 內(nèi)邊距,內(nèi)邊距值是狀態(tài)欄高度

2. Draggable 重繪問題

Draggable 的 child 和 feedback 是同一個(gè)組件是每次拖動(dòng)都會重繪組件,如果不想被重繪,在組件的 key 給個(gè) GlobalKey即可。

3. 拿到數(shù)組模型索引

List.generate 可以獲得數(shù)組模型的索引 index,length 寫數(shù)組模型的長度即可。

4. 輸入框內(nèi)容被清空

如果出現(xiàn)輸入框內(nèi)容輸入的時(shí)候突然返回到桌面,回來的時(shí)候被清空了,可以嘗試使用 WidgetsBindingObserver 監(jiān)聽不可見生命周期,輸入框焦點(diǎn)自動(dòng)取消,從而輸入框會保存原有內(nèi)容。(這個(gè)問題在早期 flutter 版本出現(xiàn))

5. 刷新頁面的指定組件

如果只想刷新某個(gè)頁面的指定組件可使用 GlobalKey 包裹此組件的 State 類,調(diào)用此組件的時(shí)候傳過去,想刷新的時(shí)候就可以在外面調(diào)用這個(gè) GlobalKey 的 setState 或其他刷新方法了。

6. 拿特殊滑動(dòng)組件內(nèi)控制器

特殊滑動(dòng)組件如 NestedScrollView 是有內(nèi)外控制器的,普通的賦值 controller 只能拿到外部的滑動(dòng)值,想要拿到內(nèi)控制可以在 NestedScrollView 的 body 中類在上下文獲取,調(diào)用上下文的 ancestorWidgetOfExactType


class DataBody extends StatefulWidget {
  @override
  _DataBodyState createState() => _DataBodyState();
}

class _DataBodyState extends State<DataBody> {
  ScrollController pageScrollController;

  Type typeOf<T>() => T;

  @override
  void initState() {
    super.initState();

    PrimaryScrollController primaryScrollController =
        context.ancestorWidgetOfExactType(typeOf<PrimaryScrollController>());

    pageScrollController = primaryScrollController.controller;
  }
}

不過這個(gè)在 1.12 已被廢棄,新版 Flutter 使用 findAncestorWidgetOfExactType 代替。

功能

1. 無需上下文路由

自己定義個(gè) NavigatorState 的全局 key 然后賦值到 MaterialApp 的 navigatorKey 就能使用 NavigatorState 的所有功能并無需上下文。

2. Future 執(zhí)行完成錯(cuò)誤

如果出現(xiàn) Future 執(zhí)行完成錯(cuò)誤可嘗試使用 Completer 的 future,如:

Future<Null> _refreshData() {
  final Completer<Null> completer = new Completer<Null>();

  new Future.delayed(new Duration(seconds: 2), () {
    completer.complete(null);
  });

  return completer.future;
}

3. 調(diào)用方法出現(xiàn) null 錯(cuò)誤

dispose 銷毀某對象時(shí)在調(diào)用 .dispose 前加個(gè) ? 問號,可以避免前面的為空導(dǎo)致調(diào)用不到 dispose 方法報(bào)錯(cuò)。

4. 頁面狀態(tài)保存

Flutter 中可以通過 mixins AutomaticKeepAliveClientMixin ,然后重寫 wantKeepAlive 保持住頁面,記得在被保持住的頁面 build 中調(diào)用 super.build 。

5. 手勢識別范圍

使用 GestureDetector 范圍只有 child 的設(shè)定顏色或使用區(qū)域才可被觸發(fā),behavior 為 HitTestBehavior.translucent 時(shí)整個(gè) child 會被觸發(fā),InkWell 組件默認(rèn)整個(gè) child 會被觸發(fā)。
hitTest 得到一個(gè)包含所有待處理控件列表 HitTestResult
dispatchEvent 進(jìn)行事件分發(fā)并產(chǎn)生競爭,最終勝利者可以得到事件響應(yīng)權(quán)

如果同一個(gè)區(qū)域內(nèi)有多個(gè)控件都實(shí)現(xiàn)了 handleEvent,那么最后事件應(yīng)該交給誰處理呢?
事件競爭只有符合以下兩個(gè)條件之一的,才可以得到事件的處理權(quán)。

  1. 最后得到直接勝利的控件
  2. 活到最后的控件中排在列表第一位的控件

6. Flutter 手勢事件主要是通過競技判斷

主要有 hitTest 把所有需要處理的控件對應(yīng)的 RenderObject , 從 childparent 全部組合成列表,從最里面一直添加到最外層。

然后從隊(duì)列頭的 child 開始 for 循環(huán)執(zhí)行 handleEvent 方法,執(zhí)行 handleEvent 的過程不會被攔截打斷。

一般情況下 Down 事件不會決出勝利者,大部分時(shí)候是在 Move 或者 up 的時(shí)候才會決出勝利者。

競技場關(guān)閉時(shí)只有一個(gè)的就直接勝出響應(yīng),沒有勝利者就拿排在隊(duì)列第一個(gè)強(qiáng)制勝利響應(yīng)。

同時(shí)還有 didExceedDeadline 處理按住時(shí)的 Down 事件額外處理,同時(shí)手勢處理一般在 GestureRecognizer 的子類進(jìn)行。

插件開發(fā)

1. android 獲取 context 和 activity

可以在插件的 Plugin 類實(shí)現(xiàn) ActivityAware,然后在 onAttachedToActivity 和 onReattachedToActivityForConfigChanges 都可以拿到 ActivityPluginBinding 的 binding,binding 調(diào)用getActivity。


public class DemoPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {

    static Context ctx;
    static Activity activity;

    @Override
    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
        ctx = binding.getApplicationContext();
    }

    @Override
    public void onAttachedToActivity(ActivityPluginBinding binding) {
        activity = binding.getActivity();
        ctx = binding.getActivity();
    }

    @Override
    public void onDetachedFromActivityForConfigChanges() {

    }

    @Override
    public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
        ctx = binding.getActivity();
        activity = binding.getActivity();
    }
}

2. PlatformView

Flutter 中通過 PlatformView 可以嵌套原生 ViewFlutter UI 中,這里面其實(shí)是使用了 Presentation + VirtualDisplay + Surface 等實(shí)現(xiàn)的,大致原理就是:

使用了類似副屏顯示的技術(shù),VirtualDisplay 類代表一個(gè)虛擬顯示器,調(diào)用 DisplayManagercreateVirtualDisplay() 方法,將虛擬顯示器的內(nèi)容渲染在一個(gè) Surface 控件上,然后將 Surface 的 id 通知給 Dart,讓 engine 繪制時(shí),在內(nèi)存中找到對應(yīng)的 Surface 畫面內(nèi)存數(shù)據(jù),然后繪制出來。實(shí)時(shí)控件截圖渲染顯示技術(shù)。

3. Platform Channel

Flutter 中可以通過 Platform Channel 讓 Dart 代碼和原生代碼通信的:

  • BasicMessageChannel:用于傳遞字符串和半結(jié)構(gòu)化的信息。
  • MethodChannel:用于傳遞方法調(diào)用(method invocation)。
  • EventChannel: 用于數(shù)據(jù)流(event streams)的通信。

同時(shí) Platform Channel 并非是線程安全的
更多詳細(xì)可查閱閑魚技術(shù)的 《深入理解Flutter Platform Channel》

介紹

1. 運(yùn)行模式

Flutter 的 Debug 下是 JIT 模式,release 下是 AOT 模式。目前主流的語言大多數(shù)只支持其中一種編譯方式,如 C 僅支持 AOT,JavaScript 僅支持 JIT,一般來說靜態(tài)語言使用 AOT,動(dòng)態(tài)語言使用 JIT

AOT 編譯方式下,編譯器必須在執(zhí)行代碼前將代碼編譯成機(jī)器執(zhí)行的原生代碼,在程序運(yùn)行時(shí)就不需要做其他額外的操作直接快速執(zhí)行,但是編譯時(shí)需要區(qū)分用戶機(jī)器架構(gòu),生成不同架構(gòu)的二進(jìn)制代碼

JIT 程序運(yùn)行前不需要編譯代碼,而是在運(yùn)行時(shí)動(dòng)態(tài)編譯,不用考慮用戶機(jī)器是什么架構(gòu),雖然縮短了開發(fā)周期,但是可能導(dǎo)致程序執(zhí)行速度更慢

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容