Flutter實現Android、iOS跨平臺經驗總結

當今程不識發表於2019-10-21

    Flutter跨平臺框架,有著較低的入門門檻,較好的開發效率和執行效率,在github上已有77.6k的star,故專案中有引用,以實現跨平臺的需求。
    整體而言,專案中主要遇到如下問題可供參考:

一、多語言類S例項為null的問題

    預設的i18n庫,使用語言環境類"S"來管理所有語言的字元,動態生成S,採用json配置檔案的形式,動態生成所有語言對應的變數。如圖所示:

Flutter實現Android、iOS跨平臺經驗總結
Flutter實現Android、iOS跨平臺經驗總結

而預設的i18n庫,呼叫
R.of(context)
將返回null。經檢視原始碼發現,可呼叫
S.delegate.load(Locale("en"));
來實現S的初始化。

故新建一個"R",繼承"S",在R的初始化方法,根據當前語言環境,先呼叫
S.delegate.load() 即可以解決,因S初始化失敗,導致的crash問題。

二、安卓各個語言包strings,轉成Flutter語言json檔案問題;

因安卓有比較充分的strings原始翻譯檔案包,但是Flutter只支援json,且格化必須為:

Flutter實現Android、iOS跨平臺經驗總結

安卓原生為xml檔案格式:

Flutter實現Android、iOS跨平臺經驗總結

故最好有一個自動將安卓strings.xml轉成符合Flutter格式json檔案的工具,以免一個人複製貼上,辛苦易錯又浪費時間。
小工具已發到外網:
strings.xml轉Flutter json工具

其中,由strings.xml解決出來的字串都進行了轉義,讀取出來插入到js物件時,不能再呼叫JSON.stringify再次轉義,不然復到到Flutter工程中,將導致編譯失敗。

此處採用的是字串拼接的方式:

      for (let key in obj) {
        json += "\n";
        json += JSON.stringify(key);
        json += ":";
        let v = obj[key];
        if (v.indexOf("\"") != -1 && v.indexOf("\\\"") == -1) {
          v = v.replace(/\"/g, "\\\"");
        }
        v = v.replace(/\\@/g, "@");
        json += "\"" + v + "\",";
      }
複製程式碼

還需要補充維吾爾語,多引數時,引數名相同的hotfix。

二、引用外部檔案時,需要統一採用'package:'。

在專案中,如果採用相對路徑,import外部檔案,而該類是單例的情況下,單例將失效,不能起到狀態儲存的作用;如:
import '../manager/VmDataManager.dart';
應統一使用:
import 'package:yh_flutter/manager/VmDataManager.dart';

三、iOS下使用FlutterViewController的self指標作為binaryMessenger變數,導致迴圈引用問題;

程式碼如下:

 FlutterMethodChannel * channel = [FlutterMethodChannel methodChannelWithName:@"com.xxx.client/plugin" binaryMessenger:self];
複製程式碼

應在在前viewcontrolelr出棧時,將FlutterMethodChannel成員變數、FlutterEventChannel成員變數置為nil:

- (void)didMoveToParentViewController:(UIViewController *)parent{
    if (parent == nil) {
        self.settingPresenter.methodChannel = nil;
        self.settingPresenter.eventChannel = nil;
        self.settingPresenter = nil;
    }
}
複製程式碼

四、Flutter不允許StatefulWidget繼承StatefulWidget元件,宜用擴充套件方式,實現類似java類重用的需求。

class _AboutPageState extends State<AboutPage> with BasePageMixin {}
複製程式碼

BasePageMixin實現了類似基類的效果,如"初始化navigationbar、返回上一頁"等。

class BasePageMixin {
  Widget getAppBar(BuildContext context, String title, List actions) {
    return widget;
  }

  void back(BuildContext context){
    if (VmDataManager.getInstance().isStackTop(context)){
      return;
    }
    debugPrint("can pop=" + context.toString());
    Navigator.pop(context);
  }
}
複製程式碼



五、可通過設定initialRoute的方式實現,native控制Flutter首頁及同步傳遞訊息給Flutter的效果;

iOS端設定路由程式碼:

[self setInitialRoute:self.routes];
複製程式碼

Android設定路由程式碼:

  public FlutterView createFlutterView(Context context) {
    WindowManager.LayoutParams matchParent = new WindowManager.LayoutParams(-1, -1);
    FlutterNativeView nativeView = this.createFlutterNativeView();
    FlutterView flutterView = new FlutterView(this, (AttributeSet) null, nativeView);
//    flutterView.setInitialRoute("xxx");

    flutterView.setLayoutParams(matchParent);
    this.setContentView(flutterView);
    return flutterView;
  }
複製程式碼

Flutter端讀取路由字串程式碼:

window.defaultRouteName
複製程式碼

可在setInitialRoute傳遞Flutter在初始化時,就需要獲取的資訊,如"語言環境、是否登入、登入方式、快取大小"等,可減少Flutter再非同步調native,native再非同步調Flutter的過程。

六、Flutter網路,防止https抓包,及自簽名根證照信任問題;

Flutter的網路請求,如果在專案中不事先程式碼宣告當前proxy代理的ip和port,則系統的代理設定將不生效。
所以只要釋出包沒有設定代理的方法,將不能被第三方應用抓包。程式碼如下;

 var client = HttpClient(context: context);
    client.findProxy = (uri) {
    return "PROXY 100.69.181.11:8888";
 };
複製程式碼

公司要求支援xxx.cer根證照,而此根證照是自簽名的,故新增根證照信任。程式碼如下:

ByteData data = await rootBundle.load('assets/b.crt');
SecurityContext context = SecurityContext.defaultContext;
context.setTrustedCertificatesBytes(data.buffer.asUint8List());
複製程式碼

需要先將cer證照轉成x509 crt格式。命令如下:

openssl x509 -inform DER -in 053-Actalis_root.cer -out b.crt
複製程式碼

相關文章