Flutter移动端实战手册,Android程序员必会

String result;

result = await methodChannel.invokeMethod(‘PlayAlbumVideo’, params);

String playID = params[‘playID’];

String duration = params[‘duration’];

String name = params[‘name’];

showCupertinoDialog(context: context, builder: (BuildContext context){

return CupertinoAlertDialog(

title: Text(result),

content: Text(‘name: n a m e p l a y I D : name playID: nameplayID:playID duration:$duration’),

actions: [

FlatButton(

child: Text(‘确定’),

onPressed: (){

Navigator.pop(context);

},

)

],

);

});

}

NSString *channelName = @“flutterChannelName”;

FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:flutterVC];

[methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {

if ([call.method isEqualToString:@“PlayAlbumVideo”]) {

NSDictionary *params = call.arguments;

VideoPlayerModel *model = [[VideoPlayerModel alloc] init];

model.playID = [params stringForKey:@“playID”];

model.duration = [params stringForKey:@“duration”];

model.name = [params stringForKey:@“name”];

NSString *playStatus = [SVHistoryPlayUtil playVideoWithModel:model

showPlayerVC:self.flutterVC];

result([NSString stringWithFormat:@“播放状态 %@”, playStatus]);

}

}];

Native to Flutter

Native调用Flutter的代码和Flutter调用Native的基本类似,只是调用和设置回调的角色不同。同样的,Flutter由于要接收Native的消息回调,所以需要注册一个回调,由Native发起对Flutter的调用并传入参数。

Native和Flutter的相互调用都需要设置一个名字,每一个名字对应一个MethodChannel对象,每一个对象可以发起多次调用,不同调用以invokeMethod做区分。

import ‘package:flutter/services.dart’;

@override

void initState() {

super.initState();

MethodChannel methodChannel = MethodChannel(‘nativeChannelName’);

methodChannel.setMethodCallHandler(callbackHandler);

}

Future callbackHandler(MethodCall call) {

if(call.method == ‘requestHomeData’) {

String title = call.arguments[‘title’];

String content = call.arguments[‘content’];

showCupertinoDialog(context: context, builder: (BuildContext context){

return CupertinoAlertDialog(

title: Text(title),

content: Text(content),

actions: [

FlatButton(

child: Text(‘确定’),

onPressed: (){

Navigator.pop(context);

},

)

],

);

});

}

}

NSString *channelName = @“nativeChannelName”;

FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:flutterVC];

[RequestManager requestWithURL:url success:^(NSDictionary *result) {

[methodChannel invokeMethod:@“requestHomeData” arguments:result];

}];

调试工具集


在iOS和Android开发中,各自的编译器都提供了很好的调试工具集,方便进行内存、性能、视图等调试。Flutter也提供了调试工具和命令,下面基于VSCode编译器来讲一下Flutter调试,相对而言Android Studio提供的调试功能可能会更多一些。

性能调试


VSCode支持一些简单的命令行调试指令,在程序运行过程中,在Command Palette命令行面板中输入performance,并选择Toggle Performance Overlay命令即可。此命令有一个要求就是需要App在运行状态。

Flutter移动端实战手册,Android程序员必会_第1张图片

随后会在界面上出现一个性能面板,这个页面分为两部分,GPU线程和UI线程的帧率。每个部分分为三个横线,代表着不同的卡顿层级。如果是绿色则表示不会影响界面渲染,如果是红色则有可能会影响界面的流畅性。如果出现红色线条,则表示当前执行的代码需要优化。

Dart DevTools

VSCode为Flutter提供了一套调试工具集-Dart DevTools,这套工具集功能非常全,包含性能、UI、热更新、热重载、log日志等很多功能。

安装Dart DevTools后,在App运行状态下,可以在VSCode的右下角启动这个工具,工具会以网页的形式展现,并且可以控制App。

主界面

下面是Dart DevTools的主界面,我运行的是一个界面类似于微信的App。从Inspector中可以看到页面的视图结构,Android Studio也有类似的功能。页面整体是一个树形结构,并且选中某一个控件后,会在右侧展示出控件的变量值,例如frame、color等,这个功能非常实用。

Flutter移动端实战手册,Android程序员必会_第2张图片

我运行的设备是Xcode模拟器,如果想切换Android的Material Design,点击上面的iOS按钮即可直接切换设备。刚才上面说到的查看内存的性能面板,点击iOS按钮旁边的Performance Overlay即可出现。

Select Widget

如果想知道在Dart DevTools中选择的节点,具体对应哪个控件,可以选择Select Widget Mode使屏幕上被选中的控件高亮。

Flutter移动端实战手册,Android程序员必会_第3张图片

##Flutter移动端实战手册,Android程序员必会_第4张图片

Debug Paint

点击Debug Paint可以让每个控件都高亮,通过这个模式可以看到ListView的滑动方向,以及每个控件的大小及控件之间的距离。

除此之外,还可以选择Paint Baseline使所有控件的底线高亮,功能和Debug Paint类似,不做叙述。

Memory

Dart DevTools中提供的内存调试工具更加直观,可以实时显示内存使用情况。在刚开始运行时,我们发现一个内存峰值,把鼠标放上去可以看到具体的内存使用情况。内存会有具体分类,Used、GC等。

Flutter移动端实战手册,Android程序员必会_第5张图片

Dart DevTools的内存工具还是不够完美,Xcode可以选择某段内存,看到这块内存中涉及到主要堆栈调用,并且点击调用栈可以跳转到Xcode对应的代码中,而Dart DevTools还不具备这个功能,可能和Web的展示形式有关系。

内存管理Flutter使用的是GC,回收速度可能不是很快,iOS中的ARC则是基于引用计数立即回收的。还有很多其他的功能,这里就不一一详细叙述了,各位同学可以自己探索。

多实例


项目中是通过实例化FlutterViewController控制器来显示Flutter界面的,整个Flutter页面可以理解为一个画布,通过页面不断的变化,改变画布上的东西。所以,在单实例的情况下,Flutter页面中间不能插入原生页面。

这时候如果我们想在多个地方展示Flutter页面,而这些页面并不是Flutter -> Flutter的连贯跳转形式,那怎么来实现这个场景呢?Google的建议是创建Flutter的多实例,并通过传入不同的参数实例化不同的页面。但这样会造成很严重的内存问题,所以并不能这么做。

Router

如果不能真正创建多个实例对象,那就需要通过其他方式来实现多实例。Flutter页面显示其实并不是跟着FlutterVC走的,而是跟着FlutterEngine走的。所以在创建一次FlutterVC之后,就将FlutterEngine保存下来,在其他位置创建FlutterVC时直接通过FlutterEngine的方式创建,并且在创建后进行跳转操作。

在进行页面切换时,通过channelMethod调用Flutter侧的路由切换代码,并将切换后的新页面FlutterVC添加到Native上。这种实现方式,就是通过Flutter的Router的方式实现的,下面将会介绍Router的两种表现形式,静态路由和动态路由。

静态路由

静态路由是MaterialApp提供的一个API,routes本质上是一个Map对象,其组成结构是key是调用页面的唯一标识符,value就是对应页面的Widget。

在定义静态路由时,可以在创建Widget时传入参数,例如实例化ContactWidget时就可以传入对应的参数过去。

void main() {

runApp(

MaterialApp(

home: Page2(),

routes: {

‘page1’: (_) => Page1(),

‘page2’: (_) => Page2()

},

),

);

}

class Page1 extends StatelessWidget {

@override

Widget build(BuildContext context) {

return ContactWidget();

}

}

class Page2 extends StatelessWidget {

@override

Widget build(BuildContext context) {

return HomeScreen();

}

}

进行页面跳转时,通过Navigator进行调用,每次调用都会重新创建对应的Widget。进行调用时pushNamed函数会传入一个参数,这个参数就是定义Map时对应页面的key。

Navigator.of(context).pushNamed(‘page1’);

动态路由

静态路由的方式并不是很灵活,相对而言动态路由更加灵活。动态路由不需要预先设定routes,直接调用即可。和普通push不同的是,动态路由在push时通过PageRouteBuilder来构建push对象,在Builder的构建方法中执行对应的页面跳转操作即可。

结合之前说的channelMethod,就是在channelMethod对应的Callback回调中,执行Navigator的push函数,接收Native传递过来的参数并构建对应的Widget页面,将Widget返回给Builder即可完成页面跳转操作。所以说动态路由的方式非常灵活。

无论是通过静态路由还是动态路由的方式创建,都可以通过then函数接收新页面返回时的返回值。

Navigator.of(context).push(PageRouteBuilder(

pageBuilder: (BuildContext context, Animation animation, Animation secondaryAnimation) {

return ContactWidget(‘next page value’);

}
d,就是在channelMethod对应的Callback回调中,执行Navigator的push函数,接收Native传递过来的参数并构建对应的Widget页面,将Widget返回给Builder即可完成页面跳转操作。所以说动态路由的方式非常灵活。

无论是通过静态路由还是动态路由的方式创建,都可以通过then函数接收新页面返回时的返回值。

Navigator.of(context).push(PageRouteBuilder(

pageBuilder: (BuildContext context, Animation animation, Animation secondaryAnimation) {

return ContactWidget(‘next page value’);

}

你可能感兴趣的:(程序员,架构,移动开发,android)