做者:騰訊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觸發的動做。具體流程以下圖所示:
針對這個場景,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)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愈來愈好!