Contact us : Youdao technical team assistant : ydtech01 / mailbox : <ydtech@rd.netease.com>
Since the release of the official version 1.0 of Flutter in 18 years, Youdao Luna team has maintained continuous attention and conducted a lot of attempts in many businesses. Flutter itself unifies the cross-platform features and consistent experience brought by the Skia engine, and high performance under AOT , JIT's hot reloading brings about features such as improved development efficiency, which keep people enthusiasm and continuous investment, and its ecological community is also growing rapidly.
From the actual performance point of view, the design of the entire technology stack is very good. The upper layer Flutter Framework introduces concepts such as Widget/LayerTree to implement the interface description framework by itself, and the lower layer Flutter Engine uses OpenGL to render LayerTree into a user interface.
In the long run, replacing Native with Flutter, achieving double-ended code unification, and saving manpower development is also the direction we continue to explore.
1. Foreword
1.1 Dictionary business attempt
We use Flutter to launch the vocabulary book and listening mock test services in Youdao Dictionary in March and July last year. Now it is Flutter 1.12 and the following is the business display.
- Word book:
- Listening mock test:
We have made bold attempts in relatively independent new businesses. New technologies will inevitably have problems, but we still have to be brave enough to try.
2. Basic introduction to Flutter
2.1 Dart
- Dart single-threaded model
Dart and JavaScript are both single-threaded models, and the operating mechanism is very similar. Dart officially provides a operating principle :
Dart runs on a message loop mechanism in a single thread, which contains two task queues, one is the " queue" 1613193f1a6945 microtask queue , and the other is called the "event queue" event queue . It can be found from the figure that the execution priority of the micro task queue is higher than that of the event queue.
Event queue: Responsible for handling external events such as I/O events, drawing events, gesture events, and receiving other isolate messages; microtask queue: You can add events to isolate by yourself, and the priority of events is higher than event queue.
Event queue model process:
- First check whether the MicroTask queue is empty, and if it is not empty, execute the MicroTask in the MicroTask queue first
- After a MicroTask is executed, check whether there is a next MicroTask, and execute the Event queue until the MicroTask queue is empty
- After taking out an event from the Evnet queue and processing it, return to the first step again to check whether the MicroTask queue is empty
In the event loop, when an exception occurs in a task and is not caught, the program will not exit, and the direct result is that the subsequent code of the current task will not be executed, that is to say, the exception in a task is Will not affect the execution of other tasks.
Exception capture and uploading to the statistical crash platform also apply this model, which will be discussed later
2.2 Flutter Widget
After introducing Dart, let's look at Flutter Widget again. Everything in Flutter is a Widget. By using Widget, the overall page layout, text display, picture display, gesture operation, event response, etc. can be realized.
2.2.1 StatelessWidget
StatelessWidget is a widget with no state -there is no internal state to manage. It describes the user interface more specifically by building a series of other widgets, thereby describing a part of the user interface. When our page does not depend on the configuration information and BuildContext in the Widget object itself, stateless components can be used. For example, when we only need to display a paragraph of text. In fact, Icon, Divider, Dialog, Text, etc. are all subclasses of StatelessWidget.
2.2.2 StatefulWidget
StatefulWidget is the variable state widget . Use the setState method to manage the state changes of the StatefulWidget. Call setState to notify the Flutter framework that a state has changed. Flutter will re-run the build method, and the application can display the latest state. The state is the information that the widget can read synchronously when the widget is built, and these states will change. To ensure that the widget is notified to make dynamic changes when the state changes, you need to use the StatefulWidget. For example, for a counter, we click the button to increase the number by one. In Flutter, Checkbox, FadeImage, etc. are all stateful components.
The life cycle of StatefulWidget can be roughly divided into three stages:
- Initialization : Insert the render tree. The life cycle functions involved in this stage mainly include createState, initState, didChangeDependencies and build.
- running : exists in the render tree, the life cycle functions involved in this stage mainly include didUpdateWidget and build.
- Destruction : Removed from the rendering tree. The life cycle functions involved in this stage mainly include deactivate and dispose.
The specific life cycle calling process is as follows:
2.2.3 Practical scenarios of StatefulWidget and StatelessWidget
In Flutter, component and page data changes are driven by State. Interactive pages or components can inherit StatefulWidget, and static components or pages can inherit StatelessWidget.
StatelessWidget has no internal state. Icon, IconButton and Text are all stateless widgets, and they are all subclasses of StatelessWidget.
StatefulWidget is dynamic. The user can interact with it or it can change over time (perhaps UI updates caused by data changes). Checkbox, Radio, Slider, InkWell, Form, TextField are all StatefulWidgets, and they are all subclasses of StatefulWidget.
judgment of whether to use StatefulWidget or StatelessWidget is :
- If the user interacts with the widget, the widget will change, then it is stateful.
- The state of a widget is a value that can be changed, such as the current value of a slider or whether a checkbox is selected.
- The state of the widget is stored in a State object, which is separated from the layout of the widget.
- When the widget state changes, call setState() to tell the framework to redraw the widget.
III. Mixed development-overall framework
At the beginning of development, we considered two issues:
- Scenario 1 : Both business lines a and b must be developed in the flutter project?
- Scene 2 : M app has a flutter project. At this time, the flutter project in our N app needs to be embedded in the M app. What should we do?
At first we hoped to generate multiple products for embedding. Through Flutter's offline meeting, we found that this idea is a relatively late thing, but we also got another idea to sink " and sink to the same one. Distinguish the business in the project and introduce the concept of componentization for practice;
3.1 Support multi-team development
In a Flutter project, there are usually the following 4 project types, which are briefly summarized below:
1. Flutter Application : Standard Flutter App project, including standard Dart layer and Native platform layer
2. Flutter Module : Flutter component project, only includes Dart layer implementation, Native platform layer sub-project is a hidden project automatically generated by Flutter
3. Flutter Plugin : Flutter platform plug-in project, including the realization of Dart layer and Native platform layer
4. Flutter Package
Flutter pure Dart plug-in project, which only contains the implementation of the Dart layer, and often defines some public Widgets
The dependency management between Flutter projects is managed through Pub, and the product of dependency is direct source code dependency. This dependency method is a bit like Pod in IOS, and it can limit the range of dependency library version number and Git remote dependency path, etc. , Where the specific declaration of dependencies is in the pubspec.yaml file, where the dependency writing is based on YAML grammar, YAML is a language specifically used to write file configuration, the following is a dependency example:
vibration:
git:
url: https://github.com/YoudaoMobile/flutter_vibration.git
ref: 'task/youdao'
flutter_jsbridge_builder:
path: ../../Common/flutter_jsbridge_builder
Therefore, through Flutter Plugin / Flutter Package + Pub to achieve the purpose of decoupling
Flutter Plugin / Flutter Package as the module development. In principle, we divide the project into shell projects, business components, and basic components; the dependencies are shell projects -> business components -> basic components, and cannot rely on inversion, between the same layers Cannot refer to each other.
3.2 Foundation component precipitation
Through the development in the form of components, through the continuous iteration of the business of each team, a set of basic Widget components of CommonUI is gradually precipitated, which is convenient for other businesses and teams to expand and use.
- button Tools: YDLabelButton, YDRaisedButton
- font tools: YDFontWeight, YDText, YDHtmlText
- Mask Tool: YDMaskView
- loading tools: YDDefaultLoadingView, YDLoadingView
- rounded corners Tool: CornerDecoration
- Modal popup tool: YDCupertinoModalPopupRoute
- Click the pop-up window Tool: YDCoordinateTap
- frame sequence animation tool: YDSimpleFrameAniImage
…
YDRaisedButton, YDLabelButton is a set of button content customized by us in accordance with Youdao UI guidelines
YDText integrates the English screen to take words, and solves the problem of abnormal font display in the interface of China, Japan and Korea at the same time.
YDHtmlText we integrate deep customization based on html tag display to achieve the effect of rich text
YDSimpleFrameAniImage frame animation playback component, which solves the problem of flickering when a simple picture is played in a loop for the first time.
YDCupertinoModalPopupRoute imitates the effect of the new ios modal pop-up, and supports the interactive way of sliding and disappearing.
....
3.3 yuan programming
During the development process, we found that most of the content of our bridge is the same, but the parameters and function names are different, so we plan to introduce source_gen to generate the code of the bridge layer. This also brings two benefits. One is To prevent hand errors and unnecessary bugs, the second is to unify the code
source_gen mainly deals with dart source code, and can generate code through annotations.
The general process is a _Builder through source_gen, _Builder needs a generator, and then the generator is used to generate code.
To summarize, applying annotations and generating code in Flutter only requires the following steps:
1. Depends on
dev_dependencies:
source_gen: ^0.9.0
2. Create annotation
class JSBridgeModule {
final String moduleName;
final List<String> enumTypeName;
const JSBridgeModule({this.moduleName : "app", this.enumTypeName : const []});
}
3. Create a generator
class JSBridgeImplGenerator
extends GeneratorForAnnotation<JSBridgeModule> {
JSBridgeImplGenerator() {}
@override
Iterable<String> generateForAnnotatedElement (
Element element, ConstantReader annotation, BuildStep buildStep) {
if (element is! ClassElement) {
final name = element.name;
throw InvalidGenerationSourceError('Generator cannot target `$name`.',
return _generate(classElement, moduleName, enumTypeName: checkEnumTypeName);
}
}
4. Create Builder
Builder getJSBridgeImpGeneratorBuilder(BuilderOptions options) {
return SharedPartBuilder();}
5. Write a configuration file
Create a build.yaml file in the project root directory and configure various parameters
builders:
JSBridgeImpGeneratorBuilder:
import: "package:flutter_jsbridge_builder/builder.dart"
builder_factories: ["getJSBridgeImpGeneratorBuilder"]
build_extensions: {".dart": ["flutter_jsbridge_builder.g.part"]}
auto_apply: dependents
build_to: cache
applies_builders: ["source_gen|combining_builder"]
This provides us with the possibility of outputting a template code
3.4 Resource Management
As we all know, the resource management of flutter pictures and other resources is still in the manual management stage, which is time-consuming and laborious. Therefore, I recommend a flr plug-in developed by the NetEase carefully selected team. With the AndroidStudio plug-in, flr will be used to help Flutter developers after modifying project resources. , You can automatically add a statement for the resource to pubspec.yaml
and generate a centralized resource path file. Flutter developers can use the resource in the code through the resource ID function.
By establishing an automated service to monitor and manage resource changes, then the changed resources will be synchronized to pubspec.yaml
and the corresponding resource files. Text and font resources are also supported. In the future, we will also support the dark mode plan with the flr team.
Address: https://github.com/Fly-Mix/flr-as-plugin/tree/d56e4b989c1de4b493d8e27b3f8ce4100af1f6df
3.5 Exception capture upload
In the introduction of flutter, we introduced the threading model of dart
Event queue model process:
- First check whether the MicroTask queue is empty, and if it is not empty, execute the MicroTask in the MicroTask queue first
- After a MicroTask is executed, check whether there is a next MicroTask, and execute the Event queue until the MicroTask queue is empty
- After taking out an event from the Evnet queue and processing it, return to the first step again to check whether the MicroTask queue is empty
In the event loop, when an exception occurs in a task and is not caught, the program will not exit, and the direct result is that the subsequent code of the current task will not be executed, that is to say, the exception in a task is Will not affect the execution of other tasks.
The Flutter framework has caught exceptions in many key methods for us. When an exception occurs, the error is reported through the FlutterError.reportError method, where onError is a static property of FlutterError, and we can capture the exception by overriding onError; but there are some asynchronous exceptions that we need to capture through the Zone method. The finishing code is as follows:
IV. Product introduction
4.1 Introduction to
1.12 is a watershed. Before that, the Android packaging method was different, and the iOS official also provided some commands to support different packages.
- iOS product composition
Get the product through flutter build ios --release, and then copy the locations of various plugins in flutter-plugins and place them in the .symlinks folder
app.framework : code data segment + picture
flutter.framework:engine+channel+...
FlutterPluginRegistrant : source code, some of flutter's own bridge
podhelper.rd: Fill the bridge into the pod cyclically through the bridge list in flutter-plugins, and import it through the pod in the host project
- android product composition flutter1.0
Enter the .android folder of the flutter project and execute ./gradlew assembleRelease and a flutter-release.aar package will be printed, but there are also official plugins such as path_provider, share_preference, audioplayer and so on. We also need to copy it out. Here we find that there are under the flutter project directory .flutter-plugins This file, this file records the file location of the official plug-in used by your current flutter, we read the file location through the shell, find the corresponding aar and collect it together.
- android flutter 1.12 or later
Flutter 1.12 is packaged and executed by flutter build aar --no-debug ---no-profile to get it.
4.2 Packing problem summary
In flutter1.0 version, enter the android folder and execute ./gradlew assembleRelease to get the aar product, but at this time, the aar embedded in the run will report an error. The error message is that a .dat file is missing. According to the official issue and thinking about the source code, The result of the discussion is that there is one less flutter_assets file content under assets. In fact, you can see it in io/flutter.jar, but flutter will still go to the assets folder to find it, which leads to failure to embed Android. The solution is to copy a flutter_asset from the apk and put it in aar
In flutter 1.12 version, the aar product command is officially provided, but the execution of the command fails when the official library (shared_preferences) is introduced into the project. The reason is that the third party will bring macos and web packages, but this package does not contain the content of the android file. Method: Compatible with its android folder by modifying the official sdk.
4.3 Packing machine problem summary
After configuring the flutter environment on the packager, you need to add the flutter path to the PATH in the Jenkins node configuration, otherwise the flutter command will fail to execute, and the ios package flutter build ios --release will fail because of the code sign does not have permission. Use flutter as much as possible build ios --release --no-codesign to get the environment
Five, the problems encountered
5. Problems on the 1ios side
5.1.1 Problems with mixed stack boost
First of all, thank the Xianyu team for providing a solution for the hybrid stack. During the upgrade from flutter1.9 to 1.12, we encountered a lot of problems and troubles.
1. Multiple callbacks in the life cycle
In version 1.9, the ContainerLifeCycle.Appear method will be called back twice, resulting in repetition of dependent life cycle operations. On the ios side, when viewdidappear, a didShowPageContainer message will be sent through the channel, call nativeContainerDidShow, and then go again in the TransitionBuilder method Call once.
onPageStart and then call nativeContainerDidShow, it will cause two triggers, android also repeats the above operations on the onAppear method.
solution is to remove one of them.
2. After upgrading 1.12, the crash problem in the
There are many versions of this problem, and business scenarios must be considered. Here we first modally create a NavigationViewController, and then push and pop operations on the basis of this NavigationViewController, and then we globally provide an operation of the ViewController before returning to the modal. In the process of global rollback, we cleared the native stack, and then crashed after switching to any vc in the native. But no such problems were found in version 1.9. The reason for the crash is that the surfaceUpdated operation was added to the FlutterEngine itself in version 1.12. When you did not properly handle it after exiting, FlutterEngine thought that there was still a Flutter page on your page. If you refreshed and created it, it crashed. Of course, this is the reason for the crash summarized in our business scenario. It is said that there are other versions of crash problems, and other friends are welcome to add.
The solution is to cyclically call the close method in the process of global rollback to exit the vc in the stack.
5.1.2 Multi-language display exception
When the interface is displayed in Korean/Japanese and Chinese at the same time, the interface displays abnormally
Official issue: https://github.com/flutter/flutter/issues/36527
There are three solutions:
- Increase the font ttf, globally specify to change the font display.
- TextStyle property specification
fontFamilyFallback: ["PingFang SC", "Heiti SC"]
Can be packaged into a widget
- Modify the fontFamilyFallback of all TextTheme under the theme
getThemeData() {
var themeData = ThemeData(
primarySwatch: primarySwatch
);
var result = themeData.copyWith(
textTheme: confirmTextTheme(themeData.textTheme),
accentTextTheme: confirmTextTheme(themeData.accentTextTheme),
primaryTextTheme: confirmTextTheme(themeData.primaryTextTheme),
);
return result;
}
/// 处理 ios 上,同页面出现韩文和简体中文,导致的显示字体异常
confirmTextTheme(TextTheme textTheme) {
getCopyTextStyle(TextStyle textStyle) {
return textStyle.copyWith(fontFamilyFallback: ["PingFang SC", "Heiti SC"]);
}
return textTheme.copyWith(
display4: getCopyTextStyle(textTheme.display4),
display3: getCopyTextStyle(textTheme.display3),
display2: getCopyTextStyle(textTheme.display2),
display1: getCopyTextStyle(textTheme.display1),
headline: getCopyTextStyle(textTheme.headline),
title: getCopyTextStyle(textTheme.title),
subhead: getCopyTextStyle(textTheme.subhead),
body2: getCopyTextStyle(textTheme.body2),
body1: getCopyTextStyle(textTheme.body1),
caption: getCopyTextStyle(textTheme.caption),
button: getCopyTextStyle(textTheme.button),
subtitle: getCopyTextStyle(textTheme.subtitle),
overline: getCopyTextStyle(textTheme.overline),
);
}
5.2 Problems with both ends
flutter pub get failed
When the Flutter project references the third library, it will choose to use git references in the pub, such as:
flutter_boost:
git:
url: 'https://github.com/YoudaoMobile/flutter_boost.git'
ref: 'youdao_0.0.8'
Will report the issue of pub get fail
There was a problem in the process of downloading the package. When you pull the package next time, the .pub_cache
in git
may be an empty directory, which causes an abnormality flutter packages get
So you need to clear out .pub_cache
in git
abnormal directory or perform flutter cache repair, then re-execute flutter packages get
.
5.3 channel communication
Flutter defines three different types of Channel, they are
- BasicMessageChannel: used to pass strings and semi-structured information.
- MethodChannel: Used to transfer method invocation (method invocation).
- EventChannel: Used for communication of event streams.
The channel has a very important variable codec; Codec officially defines two Codec: MessageCodec and MethodCodec
There are 4 different types of MessageCodec:
BinaryCodec;StringCodec;JSONMessageCodec;StandardMessageCodec
At first we used MethodChannel to establish communication, but encountered the problem of a long time-consuming transfer of large memory during use. We passed a series of experiments and official document guidelines. When we need to transfer large memory data blocks, we use BasicMessageChannel and BinaryCodec. Can solve the problem.
The following is the experiment content:
Experimental model: iphone 7 ios 13.7 system
Experimental data: Developers can simulate 1M-2M data by themselves for testing, since real data is involved, it will not be released here.
Experimental results:
From the experimental results, the best transmission efficiency is BinaryCodec. Of course, what kind of code to choose is the most reasonable according to the needs of the project.
BinaryCodec>StringCodec>StandardMessageCodec>JSONMessageCodec
5.4 Long list optimization
5.4.1 List content items vary in length
When we implement a page similar to the above, the idea of different item heights must be similar to the following code, but when we reach a certain order of magnitude, we find that the memory usage is very serious, resulting in some requirements that cannot be achieved, such as supporting scrolling The bar is quickly positioned; it is because of the initialization of CustomScrollView that a lot of widgets are loaded
List<Widget> list = [];
for (int i = 0; i < 10000; i ++) {
list.add(SliverToBoxAdapter(child: Container(height:30,color: Colors.red,),));
list.add(SliverFixedExtentList(delegate: SliverChildBuilderDelegate((context,index){
return Container(child: Text(index.toString()),);
},childCount: 50), itemExtent: 50)
);
}
CustomScrollView(slivers: list,);
But if they are all the same itemExtent, scrolling efficiency and memory performance are good; because you tell the height in advance instead of relying on the layout of the widget itself, the calculation efficiency is much higher. For example, the following code:
List<Widget> list = [];
list.add(SliverFixedExtentList(delegate: SliverChildBuilderDelegate((context,index){
return Container(child: Text(index.toString()),);
},childCount: 20000), itemExtent: 50)
);
CustomScrollView(slivers: list,);
How can have both ?
We decided to customize SliverFixedExtentList, the RenderObject returned by SliverFixedExtentList is RenderSliverFixedExtentBoxAdaptor, we redesigned RenderSliverFixedExtentBoxAdaptor.
The original design idea of RenderSliverFixedExtentBoxAdaptor is to calculate the current index by dividing scrolloffset by itemExtent (itemExtent is hard-coded so it is directly divided), SliverConstraints can get his remainingCacheExtent, which is cacheExtent plus scrolling visible area, and you can get lastIndex. Continuous release and creation during the scrolling process. The ideas we rewrite are as follows:
1. SliverFixedExtentList recalculates and caches the position of each element when creatingRenderObject and updateRenderObject.
2. Then rewrite the performLayout method, compare the position of the element with the scrolloffset through the global first and last index, get the new first and last index, and keep adjusting.
3. Pass the calculated constraint layout to the sub-layout.
The general idea is like this. We designed it like this at the interface level. The following is an example of calling:
YDSliverFixedExtentList(
delegate: SliverChildBuilderDelegate((context, int index) {
if (index > wordList.length - 1){
return Container(color: Colors.transparent,);
}
YDWBListBaseModel model = wordList[index];
if (model is YDWBListHeaderModel) {
return buildSusWidget(model.title);
} else if (model is YDWBListItemModel){
return buildListSingleItem(index, wordList[index], onMoveTap, onDeleteTap);
} else{
return Container();
}
},
childCount: wordList.length + 1,
),
itemHeightDelegate: (index){
if (index > wordList.length - 1){
return 60;
}
var model = wordList[index];
if (model is YDWBListHeaderModel) {
return kItemHeaderHeight;
} else if (model is YDWBListItemModel) {
return kItemHeight;
} else {
return 60;
}
},
itemIndexDelegate: (startIndex, endIndex){
firstIndex = startIndex;
lastIndex = endIndex;
},
5.4.2 How to get the current display list index
IOS development knows that our TableView has an agent to know the index of the Cell displayed on our current page, but what do we do in Flutter?
- Idea 1 : Add GlobalKey to each item, then put it in the model, and then use it to find the GlobalKey of the loop traversal model during the scrolling process, and find the corresponding RenderObject through GlobalKey. The RenderObject has position coordinates and other information, through this The information can be compared to calculate whether the RenderObject corresponding to the key displays the interface
code show as below:
double y=model.key.currentContext.findRenderObject().getTransformTo(null).getTranslation().y;
double height=model.key.currentContext.findRenderObject().paintBounds.size.height;
Then find the corresponding bound model
- Idea 2 : If the index of the display is obtained in real time, the above ideas may not be appropriate. You may need to find the array in the storage model every time. Of course, you can also optimize the algorithm based on Idea 1, and temporarily store the current display. The index is used as the index to be searched next time, reducing the number of loops.
However, what I will introduce next is another way to rewrite SliverChildBuilderDelegate. Its firstIndex and lastIndex will be returned in didFinishLayout in SliverChildBuilderDelegate, but it should be noted that the firstIndex with cacheExtent added is returned at this time, so it may be better than the actual display. To be small, so it can be combined with the idea of one for precise positioning
class MySliverChildBuilderDelegate extends SliverChildBuilderDelegate {
final int markIndex;
MySliverChildBuilderDelegate(
this.markIndex,
Widget Function(BuildContext, int) builder, {
int childCount,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
}) : super(builder,
childCount: childCount,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries,
);
@override
void didFinishLayout(int firstIndex, int lastIndex) {
debugPrint('pre' + 'didFinishLayout firstIndex: $firstIndex, lastIndex: $lastIndex' + "markIndex" + markIndex.toString());
if (firstIndex == 0 && lastIndex == 0) {
return;
}
YDBaseListEvent.notifyIndexChange({"firstIndex": markIndex + firstIndex, "lastIndex": markIndex + lastIndex});
debugPrint(mark + 'didFinishLayout firstIndex: $firstIndex, lastIndex: $lastIndex' + "markIndex" + markIndex.toString());
}
}
Idea 3: You can refer to the long list optimization and rewrite SliverFixedExtentList to expose the interface corresponding to the return index
VI. Follow-up plan
We plan to upgrade to flutter2.0 in the future, but at present, 2.0 still has problems. If 2.0 really solves the problem of multiple engine reuse, we will also try to remove the boost management mechanism, according to https://flutter. cn/posts/flutter-engage-china-event-recap , in the video chapter of multiple engine multiplexing, Yu Xiao analyzed the memory growth problem of multiple engine multiplexing, mainly in
- Thread
- GPU resources
- Skia Context
- Font
- Dart Isolate
For these five parts, 3 new operating system threads will be launched after each engine. Each thread has a cost, especially on ios, which are merged in version 2.0; the other part of GPU resources, Skia Context includes the opengl context, metal context, metal buffer, shader program, and skia program. In order to improve the use of startup, the GPU resource and the content of the Skia Context will be shared; the size of the font will be buffered, if it is not used. Cause a certain amount of waste; dart Isolate actually creates a new engine every time an engine is created, and the 2.0 version also makes a share.
The result is also more objective, the optimization effect is more obvious, 10 starts does not rise but falls, 40M becomes 35M. If you are interested, you can try it, https://github.com/flutter/samples/tree/master/add_to_app , but for the flutter team, version 2.0 only solves the memory problem, and there are other problems, mainly the following Several aspects:
- Only supports AOT, not debug
- IOS IOSurface is uninstalled. If fluttervc is not removed but is overwritten, its metal layer does not release IOSurface, which will affect the mixed stack scene. The current method is to manually remove flutterview when overwriting
- Does not support data sharing
- Does not support memory sharing
- platform View does not support
- Only supports one snapshot
Seven, concluding remarks
We have explored the implementation of flutter in the vocabulary and listening modules. In the early practice, we encountered many problems, but in general it was still in a controllable state; after all the difficulties were solved in the early stage, the business will be based on the following The development will be much smoother and the efficiency will be improved a lot. This is also the one-time development and multi-terminal operation that flutter hopes to bring us. But on the other hand, I hope that developers will be more cautious and practice more in the landing process, and find out and solve them in advance. After all, there are situations that cannot be handled well, and they need to promote the official or ecological to provide better solutions.
In the future, we hope that flutter and the community will continue to work hard on specific issues such as platform consistency and hybrid stack, memory, keyboard, audio and video, and we will further explore the possibilities of flutter in business. thanks for watching.
The above content only represents personal opinions. If there are questions or problems in the content or experimental data, you are welcome to criticize and correct, learn together and grow together.
- This content only represents personal opinions and does not represent NetEase. It is only for internal sharing and dissemination. It is not allowed to leak in any form, otherwise legal responsibility will be pursued.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。