做者:腾讯NOW直播 - narutosun (孙帅)html
Flutter是Google使用Dart语言开发的移动应用开发框架,使用一套Dart代码就能构建高性能、高保真的iOS和Android应用程序,而且在排版、图标、滚动、点击等方面实现零差别。腾讯Now直播App中使用Flutter实现了动态搜索列表页。本文主要介绍动态搜索列表页实现相关步骤,整体来看主要分为UI,数据解析和数据通讯三个部分。bash
Now直播动态搜索列表页在Native代码实现的UI以下图所示。闭包
从iOS开发的角度看,能够将这个页面元素进行拆解。页面的父视图就是普通的一个UITableview。每一行的元素就是一个cell,cell内部头像是一个UIImageview,昵称是UILabel,时间也是UILabel,动态的内容经过UILabel展现,动态图片经过UIImageview显示,右上角更多的按钮是一个UIButton。框架
从Flutter开发的角度看,咱们能够将界面元素拆解成Row,Column,ListView,itemWidget这些UI元素。下面看下具体这些元素如何定义及使用。async
因为Container属性有不少,下面一样只展现下Now动态搜索列表页用到的属性。函数
从上面来看,咱们能分析出,经过Flutter实现这个UI,用的无非就是Row,Column,Container,listView这些基础widget。动态搜索页基础UI咱们就已经搭建出来了。工具
Now App在请求数据与解析数据,都是经过谷歌的protobuf来实现的,因此Flutter页面的数据依旧经过protobuf来实现。Flutter怎么集成进来protobuf的插件呢,这里我使用的是AndroidStudio的IDE,安装了Dart环境后,打开Flutter工程,会有一个pubspec.yaml的配置文件,在这里能够像Xcode的cocopods同样,将protobuf的版本号输入这里,而后update一下Dart,就会自动将protobuf集成进来。具体的protobuf是什么以及如何使用,能够参考这篇文章布局
在上面咱们也看到了搜索页有不少的视图元素,还有一些点击跳转的事件也须要传参,因此这里设计数据格式的话,给当前类建立了这些属性。性能
class AnchorInfo {
String anchorHeadUrl; //头像
String anchorName; //昵称
String uin; //uin
String content; //内容
FEED_TYPE feedType; //Feed类型
String feedsId; //FeedID
String coverImageUrl; //封面url
double imgWidth = 200.0; //图片宽度
double imgHeight = 200.0; //图片高度
String jumpUrl; //跳转url
}
复制代码
从这个数据格式上来看,已经包含了动态搜索列表页面Cell的基本数据。下面就是如何给这些数据初始化,以及怎么拿到这些数据。ui
数据通讯主要有两种状况,一种是有Flutter页面主动触发的,另外一种是由Native主动触发调用到Flutter的。咱们分别来看这两种状况在动态搜索页中的应用。
这种状况应用于动态搜索列表页feeds拉取的场景。因为Now工程项目的特殊性,客户端在发包的时候使用的是WNS平台,因此这块发包不能经过Flutter页面来实现,只能经过客户端来发包,客户端回包会取到一个二进制流的数据,会将这个二进制流的数据塞给Flutter,由于发包一样是遵守protobuf的格式。因此这里Flutter拿到二进制流的数据后,就能够经过protobuf来解包,实现了客户端发包,Flutter解包的一个过程。具体流程以下图所示。
针对这个场景,Flutter提供了MethodChannel来实现。具体步骤以下:
1)Flutter内部注册MethodChannel
class DynamicListViewState extends State<DynamicState> {
...
static platform = const MethodChannel('now.qq.com/flutter');
...
}
复制代码
在类初始化的时候,就将platform赋值给了一个MethodChannel,咱们看到传了一个字符串的参数进去。那这个参数其实就是一个调用iOS的标识。
2)Native代码注册相同名称的MethodChannel iOS客户端一样会注册一个带有相同标识的channel。
self.methodChannel = [FlutterMethodChannel methodChannelWithName:
@"now.qq.com/flutter" binaryMessenger:self];
复制代码
3)Flutter调用MethodChannel 上面完成的步骤,能够理解为在iOS上已经注入了一个插件,那接下来就须要经过插件来调用iOS的相关的API,如何来调用具体的iOS的API呢。看下咱们这边加载动态数据的代码块。
void moreAnchorInfoDataRequest() async {
...
List<int> data = await platform.invokeMethod("anchorInfoDataRequest", pageIndex);
...
}
复制代码
主要看platform.invokeMethod的方法,发现又传入了一个字符串,不一样的是还传入了一个int型的pageIndex。字符串一样是一个标识,是Flutter调用客户端的一个约定。客户端在取到这个标识后,会去调用相应的API。至于pageIndex,是客户端在须要调用这个方法时,所需的参数。来看下客户端的代码是如何响应并执行这个操做的。
4)Native接收到调用处理完成后回包
[self.methodChannel setMethodCallHandler:^(FlutterMethodCall* call,
FlutterResult result) {
...
if([call.method isEqualToString:@"anchorInfoDataRequest"]){
wself.result = result;
[wself anchorInfoRequest:((NSNumber*)call.arguments).unsignedIntegerValue];
}
...
}];
复制代码
从代码块中能够看出,客户端在闭包里,能够取到call的实例,经过method的属性来判断出须要调用哪一种API。这样就已经完成了Flutter调用客户端的步骤。那如今还有一步是怎么经过客户端调用到Flutter呢。
这种状况应用于Native动态详情页进行删除的场景。在Flutter页面点击头像->进入主人态我的中心->删除了动态->告知Flutter动态页数据更新。Native主动触发事件去告诉Flutter,Flutter须要去响应Native触发的动做。具体流程以下图所示:
1)Flutter注册EventChannel
static const EventChannel eventChannel = const EventChannel("now.qq.com/event");
复制代码
重写当前类的initState方法,在这个方法里,注册一个监听。用来监遵从客户端调用来的事件,在_onEvent方法里执行监听到以后所须要作的操做。
void initState() {
super.initState();
...
eventChannel.receiveBroadcastStream().listen(_onEvent,onError: _onEventError);
...
}
复制代码
在Now里这个事件是个删除动态的操做,因此调用了删除的操做后,告知Flutter页面的数据改变,UI须要刷新。
void _onEvent(Object event) {
...
if(deleteData != null){
setState(() {
dynamicDataList.remove(deleteData);
if(dynamicDataList.length > 0) {
showType = show_normalView;
}else{
showType = show_notingView;
}
});
}
...
}
复制代码
2)Native代码注册相同名称的EventChannel
看下在Now工程中,这个方法是在什么时机调用的。代码块以下:
self.eventChannel = [FlutterEventChannel eventChannelWithName:@"now.qq.com/event" binaryMessenger:self];
[self.eventChannel setStreamHandler:self];
复制代码
一样客户端也建立了一个FlutterEventChannel的实例,经过类方法来建立的。setStreamHandler的方法就是注入一个工具类,做用将须要调用的事件流通知给Flutter。设置了这个方法后,咱们须要实现它提供的代理方法,在实现代理方法前,咱们还须要一个FlutterEventSink的闭包函数。咱们把这个闭包函数声明成了一个属性。
@property (nonatomic, strong) FlutterEventSink eventSink;
复制代码
3)Native调用到Flutter
下面看怎么使用这个属性以及怎么回调到Flutter页面上。首先须要实现FlutterStreamHandler的代理方法,在onListenWithArguments代理方法里,咱们保存了FlutterEventSink类型的闭包函数。方便后续在调用的地方去使用。
- (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments
eventSink:(FlutterEventSink)events {
self.eventSink = events;
return nil;
}
复制代码
调用的时机以下,在咱们删除动态成功后,咱们会回调给Flutter。传入一个feedsId的参数
- (void)onDeleteFeedsRequestSucceed:(NSArray *)feedsArray {
...
if(self.eventSink){
self.eventSink(model.feedsId);
}
...
}
复制代码
以上就是Now直播使用Flutter实现动态搜索列表页的一些步骤细节,欢迎你们探讨。Now直播终端团队致力于为Flutter生态做出一点本身的贡献,期待Flutter愈来愈好!