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<HomePage> {
@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拔线后闪退
iOS高版本,debug在断开连接时,进入flutter模块会闪退;使用flutter_boast在启动时就闪退。
可设置在debug也能运行release
Release问题怎么排查
有时debug正常,但release可能卡死、闪退。没有办法联调,只能通过日志观察,分析有问题的代码。
FlutterError.onError = (FlutterErrorDetails details) async {
// 转发至 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问题导致退出到首页失败
统一封装了退到首页的方法:
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。
- 定义navigatorKey
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
- 注册navigatorKey
MaterialApp(
navigatorKey:navigatorKey
...)
- 获取当前的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。
打了断点却不走
- 检查下新加的代码,有可能在断点前面的代码抛异常了。try...cache前面的代码,看报错原因。或者找离的近的代码,一行一行代码调试,看最后在哪里突然消失。
- 重命名类大小写问题:如果文件仅修改了大小写,可能定位在老的文件。
跳转flutter页面,白屏问题
- 增加白屏的加载效果
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];
- 增加缓存
@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中是常驻线程。如果出现闪退不会立刻崩溃,而是界面卡死,过段时间后闪退。
有点时候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更改后,插件没有自动更新。主要原因有两个:
- 各个语言中的key没对上;比如中文中有hello word,而其他语言少了
- 不同语言相同key但取的变量名不一致,如:
"doc_version_title":"第{v1}版"
"doc_version_title" : "Edition: {v2}"