作者:腾讯NOW直播 - narutosun (孙帅)

前言

Flutter是Google使用Dart语言开发的移动应用开发框架,使用一套Dart代码就能构建高性能、高保真的iOS和Android应用程序,并且在排版、图标、滚动、点击等方面实现零差异。腾讯Now直播App中使用Flutter实现了动态搜索列表页。本文主要介绍动态搜索列表页实现相关步骤,总体来看主要分为UI,数据解析和数据通信三个部分。

1. 动态搜索列表页UI

Now直播动态搜索列表页在Native代码实现的UI如下图所示。

从iOS开发的角度看,可以将这个页面元素进行拆解。页面的父视图就是普通的一个UITableview。每一行的元素就是一个cell,cell内部头像是一个UIImageview,昵称是UILabel,时间也是UILabel,动态的内容通过UILabel展示,动态图片通过UIImageview显示,右上角更多的按钮是一个UIButton。

从Flutter开发的角度看,我们可以将界面元素拆解成Row,Column,ListView,itemWidget这些UI元素。下面看下具体这些元素如何定义及使用。

  1. Row (行布局)
    行布局,顾名思义,布局就是以行为基准,将子视图,以行的形式去排列,它可以拥有多个子widget,在这里也说一下,Flutter秉承的原则就是“一切皆为控件”,每个元素都是一个widget。那这里的子widget可以理解为子视图,也就是说Row可以包含多个子视图。由于Row的属性网上有好多介绍的,这里就不一一去介绍了,主要介绍下在Now动态搜索列表页里用到Row几个属性:
  • mainAxisAlignment: 在水平方向上,子widget的对齐方式(有这几种方式spaceEvenly,center,left,right)。
  • verticalDirection: 表示垂直方向上,从哪个方向为起始位置(比如从上向下布局还是从下向上布局)
  • textDirector:表示在横轴上,布局从哪个方向开始(比如从左右向还是从右向左布局)
  1. Column (列布局)
    和行布局不一样的,列布局就是在纵轴上对子widget进行排列。同样也是可以包含多个子视图。同样介绍下Now动态搜索列表页里涉及到的简单的属性:
  • mainAxisAlignment:和Row不一样的是,这里指的变成了在垂直方向上,子widget的对齐方式(有这几种方式spaceEvenly,center,left,right)。
  • verticalDirection: 同样和Row有相同的效果
  1. Container (容器)
    Container在Flutter中太常见了。官方给出的简介,是一个结合了绘制(painting)、定位(positioning)以及尺寸(sizing)widget的widget。可以得出几个信息,它是一个组合的widget,内部有绘制widget、定位widget、尺寸widget。后续看到的不少widget,都是通过一些更基础的widget组合而成的。只包含一个子widget,container的组成可以由下面这张官方的图体现出来。

由于Container属性有很多,下面同样只展示下Now动态搜索列表页用到的属性。

  • padding: decoration内部的空白区域,如果有child的话,child位于padding内部。可以通过设置padding,来改变content在container的位置,同样也能改变container的大小。
  • alignment: 设置child在Container的对齐方式(有topLeft,topCenter,topRight,centerLeft,center,centerRight,bottomLeft,bottomCenter,bottomRight这几种系统提供的类型)。可以通过Alignment的方法来自定义child的位置
  • margin:从上图能够看出,margin其实表示的是decoration外面的区域,不是Container的内容区域。可以设置与父widget的间隔
  1. ListView
    ListView控件是APP开发中最为常见的控件之一,类似iOS中的UITableView,可以用来展示列表式的信息。它的内容对于其渲染框太长时会自动提供滚动。可以拥有多个child,可以水平或者垂直放置。在APP中扮演着重要的角色,listview属性详细介绍可以看下listView文档,Now这里用的是Flutter第三库的组件。Now动态搜索列表页用到的基本属性简单介绍一下:
  • scrollDirection:默认child是垂直方向上进行布局(Axis.vertical),可滑动方向对应的也是垂直方向上,horizontal表示的是水平方向。
  • reverse:是个bool类型,默认是false,在水平方向则child是从左向右排列,垂直方向上child是从上向下排列。反之ture的话,水平方向的child是从右向左排列,垂直方向是从下向上排列。

从上面来看,我们能分析出,通过Flutter实现这个UI,用的无非就是Row,Column,Container,listView这些基础widget。动态搜索页基础UI我们就已经搭建出来了。

2. 数据格式及解析

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;                  //uinString content;              //内容FEED_TYPE feedType;          //Feed类型String feedsId;              //FeedIDString coverImageUrl;        //封面urldouble imgWidth = 200.0;     //图片宽度double imgHeight = 200.0;    //图片高度String jumpUrl;              //跳转url
}
复制代码

从这个数据格式上来看,已经包含了动态搜索列表页面Cell的基本数据。下面就是如何给这些数据初始化,以及怎么拿到这些数据。

3. 数据通信

数据通信主要有两种情况,一种是有Flutter页面主动触发的,另一种是由Native主动触发调用到Flutter的。我们分别来看这两种情况在动态搜索页中的应用。

3.1 Flutter调用Native

这种情况应用于动态搜索列表页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呢。

3.2 Native调用Flutter

这种情况应用于Native动态详情页进行删除的场景。在Flutter页面点击头像->进入主人态个人中心->删除了动态->告知Flutter动态页数据更新。Native主动触发事件去告诉Flutter,Flutter需要去响应Native触发的动作。具体流程如下图所示:

针对这个场景,Flutter提供了EventChannel来实现。具体步骤如下:

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)argumentseventSink:(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越来越好!

NOW直播Flutter动态搜索列表页实现相关推荐

  1. uni-app实战之社区交友APP(8)搜索列表页和文章详情页开发

    文章目录 前言 一.搜索列表页开发 1.搜索类型传递和占位符设置 2.搜索功能实现 二.帖子详情页开发 1.pages.json配置和页面通信 2.公共列表组件功能优化 3.详情页关注和顶踩功能完善 ...

  2. php商品状态精品 热销,ecshop商品列表,商品详细页,热销,精品,搜索列表页调用商品销售量(已销售数量)...

    ecshop各个页面调用商品销售量方法(原创可用)ECSHOP模板 首页的推荐商品包括热销推荐和促销三个文件 只对热销商品为例 第一步:打开根目录/includes/lib_goods.php文件.在 ...

  3. php商品状态精品 热销,ecshop商品列表,商品页,热销,精品,搜索列表页调用商品销售量(已销售数量)-ECSHOP教程网...

    ecshop各个页面调用商品销售量方法(原创可用)ECSHOP模板 首页的推荐商品包括热销推荐和促销三个文件 只对热销商品为例 第一步:打开根目录/includes/lib_goods.php文件.在 ...

  4. 智联招聘数据爬取准备(1)-智联招聘搜索列表源码解析

    网页源码解析 - 智联招聘搜索列表 一开始必须要解析智联招聘搜索列表页,从这里更方便实现各种深层级数据抓取. 网页地址是: http://sou.zhaopin.com/jobs/searchresu ...

  5. Django项目实战——14—(列表页热销排行、商品搜索、Haystack建立数据索引、渲染商品搜索结果、商品详情页)

    1.列表页热销排行 根据路径参数category_id查询出该类型商品销量前二的商品. 使用Ajax实现局部刷新的效果. 查询列表页热销排行数据 请求方式 请求参数:路径参数 响应结果:JSON {& ...

  6. dede php 调用自定义字段,在dedecms搜索结果列表页调用自定义字段的方法(绝对可用)...

    关于在dedecms搜索结果列表页调用自定义字段的方法比较多有些教程都写得比较含糊,经过织梦者的测试今天推荐一个比较有效的方法给大家,方法是从网络中搜到的,但是绝对可用 打开 include/exte ...

  7. Axure通用web端元件库rplib文件格式+移动端app通用元件库rplib文件格式+电脑端动态可视化图表元件库+数据展示+操作反馈+通用模板+数据录入+列表页+表单页+详情页+通用版布局

    作品介绍:Axure通用web端元件库rplib文件格式+移动端app通用元件库rplib文件格式+电脑端动态可视化图表元件库+数据展示+操作反馈+通用模板+数据录入+列表页+表单页+详情页+通用版布 ...

  8. Flutter ListView (动态)列表组件、水平列表组件、图标组件详解

    Flutter ListView (动态)列表组件.水平列表组件.图标组件 Flutter ListView 基础列表组件.水平列表组件.图标组件 列表常见的几种情况: 垂直列表 垂直图文列表 横向列 ...

  9. 零基础也能学会的微信小程序制作动态搜索页

    零基础也能学会的微信小程序制作动态搜索页 准备工作: 微信开发者工具 APPID或测试号 创建一个JavaScript基本框架(如下图) 最终效果: 动手做起来吧~ index.wxml <vi ...

最新文章

  1. TensorFlow 深度学习笔记 TensorFlow实现与优化深度神经网络
  2. git 提交的时候报错:error: 'flutter_app/' does not have a commit checked out
  3. Hello World!团队第四次会议
  4. spring boot实战(第七篇)内嵌容器tomcat配置
  5. java map存储对象_JAVA:查找存储在hashMap中的对象的最佳性能方法
  6. 进击的数据中台,企业数字化转型的新引擎
  7. cdt规约报文用程序解析_用 Python 撸一个 Web 服务器第3章:使用 MVC 构建程序
  8. centos mysql无法启动 sock_linux下mysql无法启动的解决方法
  9. linux查看设备在哪个cpu上,如何在linux中查看cpu信息、机器硬件型号
  10. 7、监控和管理Linux进程
  11. C语言:运行中获取宏名字的技巧
  12. 火力发电厂卸料车系统无线改造
  13. 深入浅出SCSI子系统(八)SCSI错误恢复
  14. 微信小程序获取用户信息(附代码、流程图)
  15. 百年工业,名词满天飞
  16. android 桌面动画,Android 如何在Launcher的桌面滑动时添加动画效果? M
  17. 基于JAVA博物馆交流平台计算机毕业设计源码+系统+lw文档+部署
  18. 基于ACCESS的教师工作量计算系统的设计与实现(含源文件)
  19. 工作室课题—质因数2
  20. 如何将“\”替换为任意字符

热门文章

  1. 宝鲲财经:外汇实盘操作小技巧
  2. 真正可以在线上编辑的PDF免费工具
  3. Broadcom SDK编译
  4. 纯代码实现的分割线____简单而有效的html画线代码hr实例,纯代码实现的分割线____简单而有效的html画线代码“hr”实例...
  5. hive当前日期超前_hive日期函数
  6. linux strsep字符串分割
  7. torch.nn里的损失函数:MSE、BCE、BCEWithLogits、NLLLoss、CrossEntropyLoss的用法
  8. 菜鸟教程C语言-17
  9. 2023年全国最新安全员精选真题及答案13
  10. 第五届CCPC中国大学生程序设计竞赛落幕 清华夺冠! 南大、浙大分获二、三名