flutter 開發(fā)中遇到的常見問題(持續(xù)更新...,最后更新時(shí)間20211110)

Flutter與原生交互側(cè)滑

原生跳轉(zhuǎn)flutter,如果原生不做處理,flutter內(nèi)部頁(yè)面支持側(cè)滑,但從flutter到原生不支持側(cè)滑。

監(jiān)聽flutter首頁(yè),當(dāng)首頁(yè)出現(xiàn)時(shí),原生ViewController打開側(cè)滑代理事件;跳轉(zhuǎn)到flutter二級(jí)頁(yè)面關(guān)閉原生側(cè)滑事件。

具體實(shí)現(xiàn):

@interface BLFlutterViewController : FBFlutterViewContainer
@end

@implementation BLFlutterViewController
- (void)setIsSideslip:(BOOL)isSideslip{
    _isSideslip = isSideslip;

    RTRootNavigationController * navi = self.rt_navigationController;

    if (isSideslip) {
        navi.interactivePopGestureRecognizer.delaysTouchesBegan = YES;
        navi.interactivePopGestureRecognizer.delegate = self;
        navi.interactivePopGestureRecognizer.enabled = YES;
    } else {
        navi.interactivePopGestureRecognizer.delegate = nil;
        navi.interactivePopGestureRecognizer.enabled = NO;
    }
}

[self.methodChannel setMethodCallHandler:^(FlutterMethodCall* call,
                                         FlutterResult result) {
    if([call.method isEqualToString:@"supportedSideSlip"]){//左滑
        BOOL side = [call.arguments boolValue];
        self.isSideslip = side;
    }
}];

@end

//主頁(yè)
class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  void dispose() {
    super.dispose();
    PageVisibilityBinding.instance.removeObserver(this);
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    ///注冊(cè)監(jiān)聽器
    PageVisibilityBinding.instance.addObserver(this, ModalRoute.of(context));
  }

  @override
  void onPageHide() {
    super.onPageHide();
    print("LifecycleTestPage - onPageHide");
    methodChannel.invokeMethod(channel_supportedSideSlip, false);
  }

  @override
  void onPageShow() {
    super.onPageShow();
    print("LifecycleTestPage - onPageShow");
    methodChannel.invokeMethod(channel_supportedSideSlip, true);
  }
}

Flutter 編譯模式debug和release判斷

參考文章

第一種、通過斷言識(shí)別

assert((){
     // Do something for debug
     print('這是asset下的輸出內(nèi)容');
     return true;
 }());

第二種、通過編譯常數(shù)識(shí)別

if (kReleaseMode){ // 
      //release
}else {
     //debug
}

解決Flutter真機(jī)debug拔線后閃退

Flutter - debug/release切換優(yōu)化

iOS高版本,debug在斷開連接時(shí),進(jìn)入flutter模塊會(huì)閃退;使用flutter_boast在啟動(dòng)時(shí)就閃退。

可設(shè)置在debug也能運(yùn)行release

image
image

Release問題怎么排查

有時(shí)debug正常,但release可能卡死、閃退。沒有辦法聯(lián)調(diào),只能通過日志觀察,分析有問題的代碼。

FlutterError.onError = (FlutterErrorDetails details) async {
    // 轉(zhuǎn)發(fā)至 Zone 中
    Zone.current.handleUncaughtError(details.exception, details.stack);
  };

  runZoned<Future<Null>>(() async {
    runApp(MyApp());
  }, onError: (error, stackTrace) async {
    //Do sth for error
    String message =
        "error = ${error.toString()}" + "\nstackTrace ${stackTrace.toString()}";
    debugPrint(message);
  });

BuildContext問題導(dǎo)致退出到首頁(yè)失敗

統(tǒng)一封裝了退到首頁(yè)的方法:

void popRoot(BuildContext context) {
  // ignore: unnecessary_statements
  Navigator.popUntil(context, (route) {
    //跳到根目錄
    String name = route.settings.name;
    if (name != "/") {
      return false;
    }
    return true;
  });
}

如果在_MyAppState中收到消息,直接調(diào)用popRoot(context)會(huì)失效。
需要獲取頂層的context。

  1. 定義navigatorKey
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
  1. 注冊(cè)navigatorKey
MaterialApp(
navigatorKey:navigatorKey
           ...)
  1. 獲取當(dāng)前的context
Future.delayed(Duration(seconds: 0)).then((value) {
    BuildContext curContext = navigatorKey.currentState.overlay.context;
    popRoot(curContext);
});

FlutterViewController內(nèi)存沒有釋放

FlutterMethodChannel強(qiáng)引用self,導(dǎo)致內(nèi)存泄露

self.methodChannel = [FlutterMethodChannel
          methodChannelWithName:@"cn.percent.online_document"
                binaryMessenger:self];

解決方案

self.methodChannel = [FlutterMethodChannel
          methodChannelWithName:@"cn.percent.online_document"
                binaryMessenger:[BLWeakProxy proxyWithTarget:self]];

@interface BLWeakProxy : NSObject
@property (weak,nonatomic,readonly)id target;

+ (instancetype)proxyWithTarget:(id)target;
- (instancetype)initWithTarget:(id)target;
@end

@implementation BLWeakProxy

- (instancetype)initWithTarget:(id)target{
    _target = target;
    return self;
}

+ (instancetype)proxyWithTarget:(id)target{
    return [[self alloc] initWithTarget:target];
}

- (void)forwardInvocation:(NSInvocation *)invocation{
    SEL sel = [invocation selector];

    if ([self.target respondsToSelector:sel]) {
        [invocation invokeWithTarget:self.target];
    }
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    return [self.target methodSignatureForSelector:aSelector];
}

- (BOOL)respondsToSelector:(SEL)aSelector{
    return [self.target respondsToSelector:aSelector];
}

@end

attach聯(lián)調(diào)時(shí)出現(xiàn)下面錯(cuò)誤

There are multiple observatory ports available.
Rerun this command with one of the following passed in as the appId:

  flutter attach --app-id bundleId
  flutter attach --app-id bundleId (2)

Exited (1)

XCode運(yùn)行,flutter直接停止,再次啟動(dòng)會(huì)出現(xiàn)上述錯(cuò)誤。
可以重新運(yùn)行XCode工程,暴力解法直接關(guān)掉模擬器,重新attch。

打了斷點(diǎn)卻不走

  1. 檢查下新加的代碼,有可能在斷點(diǎn)前面的代碼拋異常了。try...cache前面的代碼,看報(bào)錯(cuò)原因?;蛘哒译x的近的代碼,一行一行代碼調(diào)試,看最后在哪里突然消失。
  2. 重命名類大小寫問題:如果文件僅修改了大小寫,可能定位在老的文件。

跳轉(zhuǎn)flutter頁(yè)面,白屏問題

  1. 增加白屏的加載效果
UIView * splashScreenView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
UIActivityIndicatorView * loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[loadingView startAnimating];
loadingView.center = splashScreenView.center;
loadingView.size = CGSizeMake(40, 40);
[splashScreenView addSubview:loadingView];
  1. 增加緩存
@import Flutter;
@interface AppDelegate : FlutterAppDelegate <UIApplicationDelegate>
@property (nonatomic,strong) FlutterEngine *flutterEngine;
@end

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.flutterEngine = [[FlutterEngine alloc] initWithName:@"my flutter engine"];
    [self.flutterEngine run];
    [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
}
@end

//初始化viewController
FlutterEngine *flutterEngine =
                ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine;
BLFlutterViewController *viewController = [[BLFlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
viewController.splashScreenView = splashScreenView;
[self.navigationController pushViewController:viewController animated:YES];

flutter 卡死閃退

flutter的UI線程在iOS中是常駐線程。如果出現(xiàn)閃退不會(huì)立刻崩潰,而是界面卡死,過段時(shí)間后閃退。

有點(diǎn)時(shí)候debug調(diào)試時(shí),并沒注意控制臺(tái)的報(bào)錯(cuò)信息,直接熱更了。而到了release情況,會(huì)引起程序卡死、閃退。

flutter: error = Null check operator used on a null value
stackTrace #0 State.setState (package:flutter/src/widgets/framework.dart:1108)
1 _MySpaceState.loadFiltrate (package:online_document/Section/myspace/MySpace.dart:126)

檢查數(shù)據(jù)是否有越界,空的情況。有時(shí)候debug情況沒有注意,或者測(cè)試的時(shí)候沒有復(fù)現(xiàn)。到release會(huì)一直打印信息,直到閃退。
flutter 開啟了4個(gè)常駐線程,崩潰時(shí)不會(huì)立刻崩潰,而是一直在瘋狂跑CPU,界面卡住。然后閃退。

Widget body() {
    ...
    return list.pullCustomListViewHeader(headerWidget,
        (BuildContext context, int index) {
      if (index >= list.datas.length) {
        return SizedBox();
      }
      return cell(context, list.datas[index]);
    });
  }

多次跳轉(zhuǎn)flutter頁(yè)面,出現(xiàn)白屏

快速點(diǎn)擊時(shí),小概率出現(xiàn)多次跳到同一個(gè)頁(yè)面。其中有些頁(yè)面沒刷新出來,出現(xiàn)白屏。

模擬測(cè)試:

for (int i = 0; i<3; i++) {
   [self.navigationController pushViewController:[BLFlutterViewController flutterVC] animated:YES];
} 

解決方案:
防止按鈕快速點(diǎn)擊。

Flutter Intl插件不生效

flutter_intl:
  enabled: true # Required. Must be set to true to activate the plugin. Default: false
  arb_dir: lib/l10n # Optional. Sets the directory of your ARB resource files. Provided value should be a valid path on your system. Default: lib/l10n
  output_dir: lib/generated # Optional. Sets the directory of generated localization files. Provided value should be a valid path on your system. Default: lib/generated
  use_deferred_loading: false

有時(shí)候發(fā)現(xiàn)arb更改后,插件沒有自動(dòng)更新。主要原因有兩個(gè):

  1. 各個(gè)語(yǔ)言中的key沒對(duì)上;比如中文中有hello word,而其他語(yǔ)言少了
  2. 不同語(yǔ)言相同key但取的變量名不一致,如:
    "doc_version_title":"第{v1}版"
    "doc_version_title" : "Edition: {v2}"
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 本文不是講技術(shù)的本文不是講技術(shù)的本文不是講技術(shù)的重要的事情說三遍。。。 之所以發(fā)這篇文章,只是為了記錄自己在學(xué)習(xí) ...
    yanftch閱讀 3,131評(píng)論 2 8
  • 1、搭建環(huán)境時(shí) 解決:給報(bào)錯(cuò)的開發(fā)工具 安裝flutter和dart插件即可。 第一個(gè)?:按照終端提示,順序運(yùn)行給...
    wg剛閱讀 8,656評(píng)論 0 10
  • # Flutter Go代碼開發(fā)規(guī)范 0.1.0版 ##代碼風(fēng)格###標(biāo)識(shí)符三種類型####大駝峰 類、枚舉、ty...
    哥哥是歐巴Vitory閱讀 495評(píng)論 0 3
  • 國(guó)際化下光標(biāo)無法對(duì)齊 初始化數(shù)據(jù)存儲(chǔ)插件 不進(jìn)行初始化操作 會(huì)在頁(yè)面渲染時(shí)無法獲取到存儲(chǔ)的數(shù)據(jù) dialog在頁(yè)面...
    顏色不一樣的炮仗閱讀 2,075評(píng)論 0 6
  • 一. 調(diào)用原生功能 1.1. Camera 某些應(yīng)用程序可能需要使用移動(dòng)設(shè)備進(jìn)行拍照或者選擇相冊(cè)中的照片,F(xiàn)lut...
    5e4c664cb3ba閱讀 1,134評(píng)論 1 0

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