Author: Tencent NOW Live – Narutosun

preface

Flutter is Google’s mobile application development framework that uses Dart to build high-performance, high-fidelity iOS and Android applications with zero differences in typography, ICONS, scrolling, and clicks. Flutter is used in Tencent’s Now live streaming App to implement dynamic search list pages. This paper mainly introduces the implementation of dynamic search list page related steps, generally divided into UI, data analysis and data communication three parts.

1. Dynamic search list page UI

The UI of the Now live dynamic search list page implemented in Native code is shown in the figure below.

From an iOS development perspective, this page element can be taken apart. The superview of the page is just a normal UITableview. Each row of elements is a cell, the head inside the cell is a UIImageview, the nickname is a UILabel, the time is a UILabel, dynamic content is displayed through a UILabel, dynamic images are displayed through a UIImageview, and the more buttons in the upper right corner are a UIButton.

From the Flutter development point of view, our interface elements can be disassembled into the Row, Column, ListView, itemWidget these UI elements. Let’s look at how these elements are defined and used.

  1. Row layout, as the name suggests, is the arrangement of subviews in the form of rows based on behavior. It can have multiple subwidgets. Here, too, the principle of Flutter is that “everything is a control”, each element is a widget. The child widget here can be understood as a child view, meaning that a Row can contain multiple child views. Now dynamic search list page using Row attributes:
  • MainAxisAlignment: The alignment of child widgets in the horizontal direction (there are several ways to spaceinstituted, center, left, right).
  • VerticalDirection: indicates which direction to start from vertically (e.g. top down or bottom up)
  • TextDirector: Indicates which direction the layout starts on the horizontal axis (e.g. from left to right or from right to left)
  1. A Column layout, unlike a row layout, aligns the child widgets on the vertical axis. It can also contain multiple subviews. Again, the simple properties involved in the Now dynamic search list page:
  • MainAxisAlignment: Instead of a Row, this now refers to the alignment of child widgets in a vertical direction (there are several ways a spaceinstituted, center, left, and right).
  • VerticalDirection: Has the same effect as Row
  1. Container Container is too common in a Flutter. The official description is a widget that combines painting, positioning, and sizing widgets. It is a composite widget with drawing widgets, positioning widgets, and sizing widgets inside. Many of the widgets you’ll see later are made up of more basic widgets. Contains only one widget, and the composition of the Container can be represented in the official figure below.

Since there are many Container attributes, the following shows only the attributes used by the Now dynamic search list page.

  • Padding: The empty area inside the decoration. If there is a child, the child is inside the padding. You can change the position of the content in the container by setting the padding. You can also change the size of the container.
  • Alignment: Set the alignment of the child in the Container (topLeft, topCenter, topRight, centerLeft, Center, centerRight, bottomLeft, bottomCenter, BottomRight is the type provided by these systems). The position of the child can be customized using the Alignment method
  • Margin: As can be seen from the figure above, margin actually refers to the area outside the decoration, not the content area of the Container. You can set the interval with the parent widget
  1. ListView is one of the most common controls in APP development. It is similar to UITableView in iOS and can be used to display list-like information. Its content automatically provides scrolling when its render box is too long. You can have multiple children, which can be placed horizontally or vertically. The listView property plays an important role in the APP. See the ListView documentation for more details. Now uses the component of the third library of Flutter. The basic properties of the Now dynamic search list page are described briefly:
  • ScrollDirection: By default, child is positioned vertically (Axis. Vertical), horizontal (horizontal), and horizontal (horizontal).
  • Reverse: is a bool (false by default) that is sorted horizontally from left to right and vertically from top to bottom. If true, horizontal children are aligned from right to left and vertical children are aligned from bottom to top.

From the above, we can see that the UI can be implemented with Flutter using the basic widgets of Row, Column, Container, and listView. We’ve built the basic UI for dynamic search pages.

2. Data format and analysis

Now App requests and parses data through Google’s Protobuf, so the data of Flutter pages is still implemented through Protobuf. Install the Dart environment and open the Flutter project. There is a Pubspec. yaml configuration file that can be used here just like Xcode’s Cocopods. Enter the protobuf version number here and update Dart to automatically integrate the Protobuf. What exactly is a protobuf and how to use it

We also saw above that the search page has a lot of view elements, and there are also some click events that need to be passed, so the data format here is to create these attributes for the current class.

class AnchorInfo { String anchorHeadUrl; // avatar String anchorName; // nickname String uin; //uin String content; // content FEED_TYPE feedType; //Feed type String feedsId; //FeedID String coverImageUrl; // Cover url double imgWidth = 200.0; // image width double imgHeight = 200.0; // Image height String jumpUrl; // redirect url}Copy the code

This data format already contains the basic data for the dynamic search list page Cell. Here’s how to initialize the data and how to get it.

3. Data communication

There are two main situations of data communication: one is that a Flutter page is actively triggered, the other is that a Flutter is actively triggered and invoked by a Native. Let’s look at the application of these two situations to dynamic search pages.

3.1 Flutter calls Native

This applies to dynamic search for list page feeds pull scenarios. Due to the particularity of Now project, the client uses WNS platform when sending packets. Therefore, this packet cannot be sent through the Flutter page, but can only be sent through the client. The client will fetch the data of a binary stream and feed the data of this binary stream to Flutter. Because sending packets also follows the protobuf format. Therefore, when the Flutter gets the binary stream data, it can unpack the packet through protobuf, which realizes the process of client sending and unpacking the packet. The specific process is shown in the figure below.

For this scenario, Flutter provides a MethodChannel to implement. The specific steps are as follows:

1) Method Channels are registered internally within Flutter

class DynamicListViewState extends State<DynamicState> {
  ...
  static platform = const MethodChannel('now.qq.com/flutter'); . }Copy the code

When the class is initialized, platform is assigned to a MethodChannel, and we see that a string argument is passed in. So this parameter is essentially an identifier to call iOS.

IOS clients with the same name will also register a channel with the same identity.

self.methodChannel = [FlutterMethodChannel methodChannelWithName:
                        @"now.qq.com/flutter" binaryMessenger:self];
Copy the code

3) The steps of the Flutter call to the MethodChannel can be seen as follows: iOS has injected a plugin into the Flutter. Then you need to call the iOS API through the plugin. How to call the specific iOS API? Take a look at our block of code that loads dynamic data.

void moreAnchorInfoDataRequest() async {
    ...
    List<int> data = await platform.invokeMethod("anchorInfoDataRequest", pageIndex); . }Copy the code

Platform. invokeMethod (); platform.invokeMethod (); pageIndex (); The string is also an identifier, a convention for the Flutter calling client. The client will call the corresponding API after retrieving the identity. PageIndex is the argument that the client needs to call this method. Let’s see how the client code responds and performs this operation.

4) After Native receives the call processing, the package is returned

[self.methodChannel setMethodCallHandler:^(FlutterMethodCall* call,
                                               FlutterResult result) {
        ...
         if([call.method isEqualToString:@"anchorInfoDataRequest"]){ wself.result = result; [wself anchorInfoRequest:((NSNumber*)call.arguments).unsignedIntegerValue]; }... }];Copy the code

As you can see from the code block, the client can take the call instance in the closure and use the method property to determine which API needs to be called. This completes the steps of the Flutter call client. Now there is one more step to call Flutter from the client.

3.2 Native calls Flutter

This situation applies to the scenario where the Native dynamic detail page is deleted. On the Flutter page, click the profile picture -> Enter the master personal center -> Delete dynamic -> inform the Flutter dynamic page of data updates. Native actively triggers events to tell the Flutter that the Flutter needs to respond to the actions that Native triggers. The specific process is shown in the figure below:

1) Flutter registers EventChannel

 static const EventChannel eventChannel = const EventChannel("now.qq.com/event");
Copy the code

Override the initState method of the current class, in which a listener is registered. The _onEvent method is used to listen for events called from the client, and performs any actions that need to be done after the event is heard.

void initState() { super.initState(); . eventChannel.receiveBroadcastStream().listen(_onEvent,onError: _onEventError); . }Copy the code

In Now, this event is a dynamic delete operation, so when the delete operation is invoked, it tells the Flutter page that the data has changed and the UI needs to be refreshed.

void _onEvent(Object event) {
    ...
    if(deleteData ! = null){setState(() {
        dynamicDataList.remove(deleteData);
        if(dynamicDataList.length > 0) {
          showType = show_normalView;
        }else{ showType = show_notingView; }}); }... }Copy the code

2) Native code registers an EventChannel with the same name

Take a look at when this method is called in the Now project. The code block is as follows:

self.eventChannel = [FlutterEventChannel eventChannelWithName:@"now.qq.com/event" binaryMessenger:self];
[self.eventChannel setStreamHandler:self];
Copy the code

The client also creates an instance of FlutterEventChannel, created using the class method. The setStreamHandler method is to inject a utility class that notifies the Flutter of the stream of events that need to be called. Once this method is set up, we need to implement the proxy method it provides. Before implementing the proxy method, we also need a closure function of FlutterEventSink. We declared the closure function as a property.

@property (nonatomic, strong) FlutterEventSink eventSink;
Copy the code

3) Native calls to Flutter

Here’s how to use this property and how to call back to the Flutter page. First, we need to implement the proxy method of FlutterStreamHandler. In the onListenWithArguments proxy method, we save the closure function of type FlutterEventSink. It is easy to use in the place of the call later.

- (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments
                                       eventSink:(FlutterEventSink)events {
    self.eventSink = events;
    return nil;
}
Copy the code

The timing of the call is as follows. After we have successfully deleted the dynamic, we will call back to the Flutter. Pass in a feedsId parameter

- (void)onDeleteFeedsRequestSucceed:(NSArray *)feedsArray {
    ...
    if(self.eventSink){ self.eventSink(model.feedsId); }... }Copy the code

conclusion

These are some of the steps Now Live uses Flutter to implement dynamic search list pages. Welcome to explore. The Now live Terminal team is committed to making our own contribution to Flutter ecology. We look forward to better Flutter!