Due to the pandemic, this year’s Google Developer Summit was held online, with the theme “More than Code” and a comprehensive introduction of product updates and a range of technical support for local developers. My focus is on mobile development. At this conference, the presentation on Flutter was mainly focused on Flutter performance optimization and new features.

As the second fastest growing open source project in the world, more and more Chinese developers are using Flutter to achieve cross-platform development, including Tencent English Jun team, Ali Xianyu team, etc. Its progress in openness is due to the open source community, ecological construction and support for the Web.Interested readers can learn more on the Google Developer website:Google Developer website

Let’s take a look at these new features and performance optimizations.

Optimization of Flutter performance

Our first talk will be by Google software engineer Yuqian Li. He is one of the software engineers on the Flutter team, focusing on improving its performance. Here are the details of the speech:

In the second half of 2019, the Flutter team received 23 quantifiable performance improvements; In the first half of 2020, the Flutter team received 27 quantitative performance improvements. The Flutter team received 49 performance improvements from 78 developers in the first half of 2020.

Tool performance is important, and so is performance testing. Having good performance testing can:

  • Quickly reproduce the problem;
  • Iterating and verifying solutions;
  • Provide data, stimulate further work and prevent backsliding.

In general, energy consumption is related to rendering speed, and the longer the rendering time per frame, the higher the energy consumption, but energy consumption is not a measure of rendering speed, because in some cases fast rendering speed can cause higher energy consumption, and slow rendering speed can cause no energy consumption.

Although the running time on CPU is short, the GPU energy consumption increases because the new algorithm utilizes more GPU cores. Some TASKS on cpus are blocked by other I/O or GPU tasks and wait for a long time. However, the waiting time does not consume much power.

Therefore, it is necessary to add energy consumption test in addition to speed. Since most of the problems the Flutter team receives on GitHub are related to iOS, this time the Flutter energy test will be added to iOS first, and the Android power test tool will be added later.

Developers can use the Flutter Gallery App to view CPU/GPU usage in Timeline, or use integration tests to automatically detect CPU/GPU usage.

Flutter has also added SkSL shader build preconditioning to help developers eliminate shader build delays. If a Flutter program first renders certain kinds of animations with obvious lag, but later renders these animations completely disappear, then this is probably the result of a shader compiling lag. Developers can use –trace-skia and check the Timeline to see if the shader is stuck.

It is worth mentioning that SkSL can be automatically generated and tested, which saves a lot of manpower for the constantly updated Flutter App.

Test tool for memory and package volume

Next, a discussion on Flutter testing tools by Youyang Hou, a User experience researcher at Flutter. Hou joined Google in 2017 and the Flutter team in 2019. She is a user experience researcher on the Flutter team, focusing on improving the programmer experience of Flutter products and development tools.

This time, the Flutter team updated the Dart development tool. The Dart development tool is a tool suite for Flutter and Dart developers. It includes the following widgets:

  • Inspector of layout
  • Performance Debugging
  • Memory debugging
  • Network Debugging
  • Package Size debugging (App Size)
  • Debugger
  • Logging

Connect to your device and run the Flutter app. Click the Open DevTools button in the toolbar at the bottom of Android Studio to enable the debug function.

Memory debugger functionality

The memory debugger for Flutter provides the following functions:

  • Event pane (Dart and Android Memory)
  • Manual and Automatic Snapshot and Garbage Collection
  • Memory analysis
  • Heap Allocation Accumulators
  • Place memory statistics into a JSON file through a command line interface

The memory test

Memory tests provide the following functions:

  • Memory tests are performed directly through ADB interactions
  • Dart development tool memory testing
  • IOS Memory Test

More information can be found in this article written by a Flutter engineer: How to conduct a Flutter memory test

Package volume debugger feature

The package volume debugger provides the following functions:

  • Visualized the total size of the application, including functional-level Dart AOT snapshots;
  • Analyze snapshots and application packages (APK, IPA, etc.);
  • Analyze differences between snapshots or application packages (APK, IPA, etc.);
  • View application size attribution data at the software package level.

Hybrid development of Pigeon and Flutter

What is a Pigeon

In the early Hybird development mode, the interaction between the front-end and Native required the Native dual end to provide the interface for JS. In this case, how to standardize naming and parameters becomes a problem. If a protocol file is maintained separately and the three ends develop according to the protocol file, it is easy to change the protocol and not synchronize in time, or the actual development process is not in accordance with the specification, which may lead to various accidents.

The same problem occurred in the development of the Flutter plugin package because it involved the Native dual-end code development capability and the Dart side exposed a unified interface to users. At this time, Pigeon was born. Pigeon is the official recommended plugin management tool for Flutter. Can be used to solve and optimize platform channel related problems in Native plug-in development.

The Pigeon plugin officially provided by Flutter generates double-ended generic template code through dart entry. The Native part only needs to rewrite the interface in the template, and there is no need to care about the implementation of methodChannel. Input and output parameters are also constrained by the generated template code. Interface addition or parameter modification only need to update protocol file on DART side and generate dual-end template to achieve synchronous update, effectively avoiding the problem of dual-end code synchronization caused by parameter modification and parameter addition. The following is a schematic diagram of Pigeon’s working principle.

Here’s an example from Pigeon:

You can see that the whole code is much cleaner after Pigeon was added, and the type definition is normalized.

Pigeon access

So let’s see how we can get Pigeon from zero. So far, Pigeon has released version 0.1.15, as you can see below.First, create a Flutter project called Testpigeon, open the project’s pubspec.yaml file and add the following dependencies.

dependencies:
  pigeon: ^0.115.
Copy the code

Then, a pigeons directory was created under the project directory according to official requirements, which was used as the entry file to store the DART side, including interfaces, parameters, definition of return values, etc., and then native code was produced through pigeon’s command. Next, create a new message.dart file and add the following.

import 'package:pigeon/pigeon.dart';

class SearchRequest {
  String query;
}

class SearchReply {
  String result;
}

@HostApi()
abstract class Api {
  SearchReply search(SearchRequest request);
}
Copy the code

In the message.dart file above, the @hostAPI () annotation identifies the communication object and interface, and all we need to do is run the following command to generate the corresponding code for the project.

flutter pub run pigeon  --input pigeons/message.dart
Copy the code

The command above is a short form of the following command:

flutter pub run pigeon  --input pigeons/message.dart  --dart_out lib/pigeon.dart  --objc_header_out ios/Runner/pigeon.h --objc_source_out ios/Runner/pigeon.m --java_out android/app/src/main/java/Pigeon.java --java_package "com.xzh.testpigeon"
Copy the code

The meanings of the parameters are as follows:

  • –input: Introduces the message.dart file we created;
  • –dart_out: Outputs the DART template file;
  • –objc_header_out and –objc_source_out output object-c files;
  • — Java_out outputs Java files;

After the command is executed, the dart file is exported to the lib directory, the object-c file is exported to the ios/Runner directory, and the Java file is exported to the specified com.xzh. Testpigeon “package name. We then open the native project code using Android Studio and Xcode respectively.

Android engineering Code

Open the Native Android project of the Flutter project with Android Studio and generate the following code:

// Autogenerated from Pigeon (v0.1.15), do not edit directly.
// See also: https://pub.dev/packages/pigeon

package com.xzh.testpigeon;

import io.flutter.plugin.common.BasicMessageChannel;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StandardMessageCodec;
import java.util.ArrayList;
import java.util.HashMap;

/** Generated class from Pigeon. */
@SuppressWarnings("unused")
public class Pigeon {

  /** Generated class from Pigeon that represents data sent in messages. */
  public static class SearchReply {
    private String result;
    public String getResult(a) { return result; }
    public void setResult(String setterArg) { this.result = setterArg; }

    HashMap toMap(a) {
      HashMap<String, Object> toMapResult = new HashMap<>();
      toMapResult.put("result", result);
      return toMapResult;
    }
    static SearchReply fromMap(HashMap map) {
      SearchReply fromMapResult = new SearchReply(a); Object result = map.get("result");
      fromMapResult.result = (String)result;
      returnfromMapResult; }}/** Generated class from Pigeon that represents data sent in messages. */
  public static class SearchRequest {
    private String query;
    public String getQuery(a) { return query; }
    public void setQuery(String setterArg) { this.query = setterArg; }

    HashMap toMap(a) {
      HashMap<String, Object> toMapResult = new HashMap<>();
      toMapResult.put("query", query);
      return toMapResult;
    }
    static SearchRequest fromMap(HashMap map) {
      SearchRequest fromMapResult = new SearchRequest(a); Object query = map.get("query");
      fromMapResult.query = (String)query;
      returnfromMapResult; }}/** Generated interface from Pigeon that represents a handler of messages from Flutter.*/
  public interface Api {
    SearchReply search(SearchRequest arg);

    /** Sets up an instance of `Api` to handle messages through the `binaryMessenger` */
    static void setup(BinaryMessenger binaryMessenger, Api api) {
      {
        BasicMessageChannel<Object> channel =
            new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.Api.search".new StandardMessageCodec());
        if(api ! = null) { channel.setMessageHandler((message, reply) -> {
            HashMap<String, HashMap> wrapped = new HashMap<>();
            try{@SuppressWarnings("ConstantConditions")
              SearchRequest input = SearchRequest.fromMap((HashMap)message);
              SearchReply output = api.search(input);
              wrapped.put("result", output.toMap());
            }
            catch (Exception exception) {
              wrapped.put("error".wrapError(exception));
            }
            reply.reply(wrapped);
          });
        } else {
          channel.setMessageHandler(null); }}}}private static HashMap wrapError(Exception exception) {
    HashMap<String, Object> errorMap = new HashMap<>();
    errorMap.put("message", exception.toString());
    errorMap.put("code", exception.getClass().getSimpleName());
    errorMap.put("details", null);
    returnerrorMap; }}Copy the code

The generated pigeon.java code contains apis for developers to implement the interaction logic, and developers can retrieve dart requests via SearchRequest and return data to Dart via SearchReply. Then, we also need to implement Api interface in the Android entry file MainActivity to complete data interaction, the code is as follows.

public class MainActivity extends FlutterActivity {

  private class MyApi implements Pigeon.Api {
    @Override
    public Pigeon.SearchReply search(Pigeon.SearchRequest request) {
      Pigeon.SearchReply reply = new Pigeon.SearchReply(a); reply.setResult(String.format("Hi %s!", request.getQuery()));
      returnreply; }} @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GeneratedPluginRegistrant.registerWith(this);
    Pigeon.Api.setup(getFlutterView(), new MyApi()); }}Copy the code

First, we implement MyApi objects inherited from Pigeon.Api, and then retrieve dart request data via request.getQuery() in the search() method. SetResult of pigeon.searchReply returns data to the DART end, and finally starts with pigeon.api.setup (getFlutterView(), new MyApi()).

iOS

Use Xcode to open the iOS project of the Flutter project, link the generated pigeons. h and pigeon.m files to the Xcode project, and then introduce the Api protocol to AppDelegate.

#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import "pigeon.h"

@interface AppDelegate : FlutterAppDelegate<Api>

@end
Copy the code

Next, implement the search interface in Appdelegate.m and, based on the response to the dart message received, call the ApiSetup() method to complete the registration.

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"@implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary  *)launchOptions { [GeneratedPluginRegistrant registerWithRegistry:self];// Override point for customization after application launch.
  FlutterViewController* controller =
      (FlutterViewController*)self.window.rootViewController;
  ApiSetup(controller.binaryMessenger, self);
  return[super application:application didFinishLaunchingWithOptions:launchOptions]; } -(SearchReply *)search:(SearchRequest*)input error:(FlutterError **)error { SearchReply* result = [[SearchReply alloc]  init]; result.result = [NSString stringWithFormat:@"%s%@"."Hi ",input.query];
    return result;
}


@end
Copy the code

The Dart test

Finally, we create a new test code in the Dart code, as shown below.

import 'pigeon.dart';

void main(a) {
  testWidgets("test pigeon", (WidgetTester tester) async {
    SearchRequest request = SearchRequest().. query ="Aaron";
    Api api = Api(a); SearchReply reply = await api.search(request);
    expect(reply.result, equals("Hi Aaron!"));
  });
}
Copy the code

Application of Flutter in Alibaba

First, the host introduces the history of Flutter, focusing on its beauty, efficiency, flow and openness.Next, Alibaba’s wireless technology expert Men Liu introduced the application of Flutter in Alibaba. Xianyu is the pioneer of Alibaba’s Flutter technology practice, and also the first large Internet company in China to try Flutter technology. Alibaba’s Taobao is not to be outdone, and also makes Flutter in some modules. But mostly business-level modules, not on the scale of idle fish. We can see the usage of Flutter in Alibaba from the picture below.

Why, then, have so many mobile applications started to develop Flutter? First, let’s take a look at the history of cross-platform technology.As you can see, mobile cross-platform development goes through roughly four phases:

  • Early WebView loading solutions
  • Hybrid solution for native API bridging
  • Native Rendering scheme (Web syntax + Native UI)
  • Custom render (standalone layout/render)

The structure of Flutter is a self-painting rendering scheme that is used. Interested children can study the following structure of Flutter. Why should Flutter be chosen for cross-platform application development? Here are some advantages of Flutter:However, Flutter is not everything. Flutter is currently in a phase of rapid iteration, so to be on the safe side, we only use Flutter for some general business development and modular UI development and some games.In summary, Flutter will be used in some rich interactive applications and new applications, and will continue to be developed native to those with high rendering requirements such as video and live streaming.

What are the lessons and issues to be aware of when developing applications with Flutter? The following chart shows some of the problems alibaba has encountered with Flutter application development that you should avoid when using Flutter.The first problem is that because Flutter is developed using Dart, it increases the learning cost of the developer. Secondly, for large applications, how to ensure code quality and how to run automated test scripts on multiple platforms is also a problem. And because Flutter is a new technology, how to quickly migrate the old business is also a problem we need to consider. To summarize, it’s a matter of debugging, testing, state management, ease navigation stack management, cross-platform compatibility, and how to find solutions.Although Flutter already provides a number of tools, there is a question of how to integrate Flutter into Alibaba’s client development workflow.First, to improve development efficiency and reduce initial access costs, we integrated the Flutter Toolkit into the Alibab DevOps workflow, developed some tools, packaging and publishing platforms, and built a debugging environment. Next, we developed some middleware based on existing technology accumulation.Let’s look at an example of how to solve the memory footprint of a multi-graph list page. The characteristics of this type of problem are as follows:

  • Pages are long, images are plentiful, and the first load takes a long time
  • Lots of images are loaded and textured at the same time, and memory is skyrocketing
  • The granularity of each Cell in Sliver is large. A single Cell occupies multiple screens and is difficult to recycle

For list Flutter list memory reclamation, you can readRefine the memory reclamation of the Flutter List to solve the problem of large cellsThis article.

For the memory problem of the above multi-graph long list, we can start to optimize from the following aspects:

  • Break up the Cell to make each item smaller
  • According to the coordinates to judge whether the picture is in the screen, and then lazy loading and recycling of the picture
  • Get the width and height of the image in advance, reduce layout and redraw
  • Texture recycling is done on a per-image basis, not per Cell in the Sliver
  • External access to the native photo library, to achieve shared local cache

Finally, let’s look at the systematic construction of Flutter in Alibaba. Firstly, the systematic construction of Flutter mainly starts from basic capacity building, research and development platform and sustainable iteration.Here are some specific plans for Flutter on Alibaba.At present, Flutter has been applied on a large scale in Alibaba, and we are steadily recommending the construction of our own technology system. We will share some achievements of Flutter construction with the community later.

Bonus: Google Developer Conference