flutter 开发中遇到的常见问题(持续更新...,最后更新时间20211110)

Flutter与原生交互侧滑

原生跳转flutter,如果原生不做处理,flutter内部页面支持侧滑,但从flutter到原生不支持侧滑。

监听flutter首页,当首页出现时,原生ViewController打开侧滑代理事件;跳转到flutter二级页面关闭原生侧滑事件。

具体实现:

@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

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

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

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    ///注册监听器
    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判断

参考文章

第一种、通过断言识别

assert((){
     // Do something for debug
     print('这是asset下的输出内容');
     return true;
 }());

第二种、通过编译常数识别

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

解决Flutter真机debug拔线后闪退

Flutter - debug/release切换优化

iOS高版本,debug在断开连接时,进入flutter模块会闪退;使用flutter_boast在启动时就闪退。

可设置在debug也能运行release

image
image

Release问题怎么排查

有时debug正常,但release可能卡死、闪退。没有办法联调,只能通过日志观察,分析有问题的代码。

FlutterError.onError = (FlutterErrorDetails details) async {
    // 转发至 Zone 中
    Zone.current.handleUncaughtError(details.exception, details.stack);
  };

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

BuildContext问题导致退出到首页失败

统一封装了退到首页的方法:

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

如果在_MyAppState中收到消息,直接调用popRoot(context)会失效。
需要获取顶层的context。

  1. 定义navigatorKey
final GlobalKey navigatorKey = GlobalKey();
  1. 注册navigatorKey
MaterialApp(
navigatorKey:navigatorKey
           ...)
  1. 获取当前的context
Future.delayed(Duration(seconds: 0)).then((value) {
    BuildContext curContext = navigatorKey.currentState.overlay.context;
    popRoot(curContext);
});

FlutterViewController内存没有释放

FlutterMethodChannel强引用self,导致内存泄露

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联调时出现下面错误

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运行,flutter直接停止,再次启动会出现上述错误。
可以重新运行XCode工程,暴力解法直接关掉模拟器,重新attch。

打了断点却不走

  1. 检查下新加的代码,有可能在断点前面的代码抛异常了。try...cache前面的代码,看报错原因。或者找离的近的代码,一行一行代码调试,看最后在哪里突然消失。
  2. 重命名类大小写问题:如果文件仅修改了大小写,可能定位在老的文件。

跳转flutter页面,白屏问题

  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 
@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中是常驻线程。如果出现闪退不会立刻崩溃,而是界面卡死,过段时间后闪退。

有点时候debug调试时,并没注意控制台的报错信息,直接热更了。而到了release情况,会引起程序卡死、闪退。

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)

检查数据是否有越界,空的情况。有时候debug情况没有注意,或者测试的时候没有复现。到release会一直打印信息,直到闪退。
flutter 开启了4个常驻线程,崩溃时不会立刻崩溃,而是一直在疯狂跑CPU,界面卡住。然后闪退。

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

多次跳转flutter页面,出现白屏

快速点击时,小概率出现多次跳到同一个页面。其中有些页面没刷新出来,出现白屏。

模拟测试:

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

解决方案:
防止按钮快速点击。

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

有时候发现arb更改后,插件没有自动更新。主要原因有两个:

  1. 各个语言中的key没对上;比如中文中有hello word,而其他语言少了
  2. 不同语言相同key但取的变量名不一致,如:
    "doc_version_title":"第{v1}版"
    "doc_version_title" : "Edition: {v2}"

你可能感兴趣的:(flutter 开发中遇到的常见问题(持续更新...,最后更新时间20211110))