我們在開發 Flutter Web 應用程式時遇到的第一個技術挑戰之一是路由問題。當 Router API 發佈時,我們立即重新設計了路由系統以採用它,這被證明對 Web 應用程式更有效。雖然它提供了更好的整合到瀏覽器歷史記錄中,但它對於巢狀路由來說過於複雜。因此,我們讓在頁面之間共享狀態控制器(在本例中是 MobX 儲存)成為可能,以模擬巢狀路由。這種方法使維護和實作變得更加簡單。
我們學到的最主要的一點是 Dart 和 JavaScript 之間的互操作性對於 Web 應用程式的重要性。如果您還目標是 Flutter 支援的其他平台,那麼使用 Web 特定函式庫會有一些限制,但如果您的主要關注點是 Web 應用程式,那麼它可以節省大量時間。我們了解到,使用 JavaScript 函式庫和嵌入 HTML 可以彌補缺少 Web 支援的 Plugin。例如,我們使用這種方法來建立程式碼預覽 Widget,我們將其作為 HTML 嵌入,並使用 JavaScript 函式庫來進行語法高亮顯示。我認為,在建立 Flutter Web 應用程式時,熟悉 JavaScript 非常有幫助,同時還要了解 Dart/JavaScript 互操作性和 HTML 嵌入的優點和限制。
我們還了解到,使用 Flutter 為 Web 應用程式優化效能與為 Flutter 支援的其他平台優化效能並沒有太大區别。在大多數情况下,可以使用兩種方法解决问题:
能夠與瀏覽器程式碼整合對於利用 Web 平台的優勢至關重要。Flutter Web 應用程式有兩種方式可以與 HTML 整合:1) 在 Flutter Web 應用程式中使用 HTML 平台視圖,或 2) 將 Flutter 作為內容島嵌入到現有的 Web 應用程式中(有點像 Web 的加到應用程式)。雖然前者現在存在,可能需要改進,但後者將是一個需要進一步設計和開發的新功能。
使用自訂元素嵌入(加到應用程式)
目前,將 Flutter Web 應用程式嵌入到現有的網站/Web 應用程式中的唯一方式是透過 iframe。雖然這對某些使用案例有效,但對於那些逐漸遷移 Web 應用程式以使用 Flutter 來說並不總是理想的解決方案。
在此路線圖中,我們將調查和設計自訂解決方案,讓您能夠嵌入 Flutter Web 應用程式,類似於加到應用程式的行動情境。
这项工作改进了相机和 image_picker 外掛在 Android 上的功能和稳健性。此外,您会注意到 camera 外掛 的早期版本已提供,其中包含 Web 支援 (#4151)。此預覽版為在 Web 上查看相机预览、拍照、使用闪光灯和缩放控件提供了基本支持。它目前不是 认可外掛,因此您需要 显式地添加它 才能在您的 Web 應用程式中使用。
Writing a good code sample was originally published in Flutter on Medium, where people are continuing the conversation by highlighting and responding to this story.
我這次的 Google Summer of Code (GSoC) 經驗比去年更好。(查看 Learn testing with the new Flutter sample 以閱讀更多有關我去年參與的專案的資訊。)我認為今年的經驗更好,因為專案更具技術性,涵蓋了更多概念,並且與我之前從未參與過的內容相關。這次我也認識團隊中的更多人。我認為 GSoC 將會是我永遠不會忘記的人生的一部分。
Flutter 的熱重載功能非常棒,按下鍵盤上的 “r” 鍵,就能在幾秒鐘內看到變更的效果。在終端機(或 IDE 底部)您可能會看到類似的訊息:Reloaded 1 of 553 libraries in 297ms。但熱重載背後的運作原理是什麼?Dart 和 Flutter 團隊又是如何讓它變得更快呢?
foo.dart 曾經定義了一個字段為 var z = 42。另一個檔案使用此字段:var z2 = z * 2。Dart 類型推斷確定 z 是一個整數,而 z2 是一個整數,因為 z 是一個整數。現在,字段變更為 var z = 42.2。這次 Dart 類型推斷將會確定該字段是一個雙精度數,但如果不重新編譯另一個函式庫,那么 z2 將仍然(錯誤地)被標記為一個整數。
Note: This article was originally written in Chinese by the ByteDance team and translated into English.
Flutter, a technology that ByteDance has been utilizing and contributing to for several years now, was recently highlighted on the main stage of Google I/O. Developed and open-sourced by Google, the multi-platform framework for front-end UI development has garnered over 120,000 stars on GitHub.
Today, there are over 500 Flutter developers at ByteDance, with over 200 actively developing with the framework. These developers utilize Flutter not only for mobile apps but also experiment with it on web, desktop, and embedded platforms.
Beyond this, ByteDance has conducted fundamental work throughout the organization and made significant contributions to the Flutter project by submitting dozens of pull requests (PRs).
How did ByteDance make Flutter truly work for them?
The story of Flutter at ByteDance began two years ago.
At the time, the ByteDance front-end engineering team noticed that many teams within the company needed to develop for multiple platforms, but lacked a tool to achieve high-efficiency, high-performance, multi-platform development.
When Google open-sourced Flutter, the ByteDance team discovered that with Flutter, they only needed to develop the app once to support platforms such as Android, iOS, and web. Also, because Flutter has its own rendering engine, they could achieve more consistent performance across platforms.
With Flutter, the Android, iOS, and web versions of an app automatically stay in sync. There is no need to design and program the UI separately for each platform, eliminating a significant portion of redundant work.
To support business development more efficiently, the ByteDance team performed fundamental work on the framework itself, such as optimizing performance, creating app frameworks, containerizing, and supporting “add to app.” They also improved Flutter performance tools, including improvements to the Frames Per Second (FPS) info in the Frame chart and the timeline events chart. Both of these charts are part of the Performance View in Flutter DevTools.
When adopting Flutter, the ByteDance team encountered some unique challenges. For example, Flutter must be added to the app installation package, increasing the size of the app downloaded by users. Additionally, Flutter uses the Dart programming language, which is larger in size than native code, further increasing the package size.
The ByteDance team started a special plan to optimize the package size by compressing the iOS data section and stripping out the Skia library and other libraries (such as BoringSSL, ICU, text rendering, and libwebp). They analyzed Flutter Dart code against iOS native code and found that to implement the same business feature, the Dart code generated more machine code instructions. To close the gap, they reduced alignment of instructions, removed debugging trap instructions, dropped redundant initialization of stores with null, removed RawInstruction headers with bare instructions mode, compressed StackMaps, removed CodeSourceMap, and so on.
Individually, each of these optimizations reduced the package size by 0.2 to 4 MB and significantly reduced the total package size when combined. The ByteDance team shared their experience with Google engineers, and many improvements made their way to the Flutter open source project for the benefit of the larger community.
However, when ByteDance released their first Flutter app, new issues emerged. Users asked: ‘Why is the UI so janky when I scroll in the app?’
When the ByteDance team looked into the issue, they saw that when a FlutterView extended a TextureView, the UI was noticeably jankier than when it extended SurfaceView. However, in the official Timeline tool, the UI thread time and GPU thread time for each rendered frame are about the same, with TextureView pulling a bit ahead occasionally.
The metrics contradicted the real-world user experience, which puzzled the team.
At first, the team used the Timeline tool to troubleshoot the issue, to no avail. After digging into the tool’s source code, they discovered the root cause of the issue.
SurfaceView had better performance than TextureView. Because SurfaceView had its own surface, and rendering was performed in its own OpenGL context, it could interact with SurfaceFlinger independently and took full advantage of triple-buffering. On the other hand, TextureView was a regular view that depended on the surface of its host window for rendering. That meant the rendering wasn’t performed immediately after the UI and GPU threads had finished their work but needed to wait for the native main thread and renderThread before the view could interact with SurfaceFlinger. That was a much longer rendering pipeline than that of SurfaceView.
These findings not only helped the team eliminate the jank but led to 10 PRs being submitted to the Flutter open source project. With this fundamental work done, Flutter eventually became the go-to framework for multi-platform app development at ByteDance. Soon, the ByteDance team’s work with Flutter will be available to external developers using their mobile development framework, veMARS, benefiting the entire developer community.
From experiment to production: How ByteDance put Flutter into use
It wasn’t exactly smooth sailing for ByteDance to put Flutter into real-world use.
At first, the ByteDance team chose a mature product and planned to re-implement the app’s video playback feature with Flutter.
The feature, originally written in native code for Android and iOS, wasn’t straightforward to rewrite with Flutter. After six months, the team came to the conclusion that it would be difficult to make all the live data compatible and challenging to update the existing business logic.
The team decided that it wasn’t productive to update the existing features of a mature product with the new framework. Flutter’s strengths would be better used in a brand-new app. The team lead said, “In a mature product, everything is already well built with native Android or iOS technology. There isn’t much gain in re-implementing the features with Flutter only to make minor improvements. It also increases the package size since the Flutter engine is included in the package. In new products or new scenarios, however, Flutter can greatly increase our productivity.”
With this changed mindset, the team turned their focus to new business areas such as education.
One of their education apps in China helps students learn the order of strokes of Chinese characters; the team wanted to add a stroke tracking feature.
To implement it, the team took inspiration from some open-source projects and decided to use SVG paths to represent strokes. The paths would then be adjusted and positioned to compose the characters:
They defined the skeleton of each stroke to guide the movement of the virtual brush pen, so the pen moves just like it would in calligraphy:
Based on the defined order of the skeletons, a circle with a certain radius is drawn along each skeleton, and together these circles form the stroke. After that, the team added keyframes to ensure that the frame rate of the animation is high enough to avoid jank.
That is how they created the smooth tracking effect, as shown in the following GIF:
The feature, built with Flutter, now supports over 9,000 Chinese characters, including most of the commonly used characters. Compared to developing with native code, Flutter saved time and resources.
Today, many apps by ByteDance employ a hybrid approach to development, combining the strengths of Flutter and other technologies, with newer apps leaning towards pure Flutter. For apps such as Xigua Video, TikTok Volcano, and Open Language, Flutter increased the productivity of the teams by about 33%.
ByteDancers Embrace the Latest Technology
Even now, the Flutter team at ByteDance continues to explore the latest technologies. According to the team lead, “We have in our team many tech enthusiasts with global vision, and will continue to explore global technology developments and discuss the implementation of technology. We have close connections and collaborations with many tech companies. We have quarterly sync meetings with Google, for instance, to exchange progress, thoughts, needs, and ideas from both sides.”
One day, the maintainer of the Dart open source project on GitHub came to the ByteDance team lead with the following remarks, “Someone from your team submitted more than a dozen PRs to Dart and they’re all very good and well thought out.”
The Dart open source project maintainer was talking about Frank. Frank is a passionate open-source contributor and just got his bachelor’s degree three years ago. His journey in the open source world first started during his first year of university in 2015. One of the projects he created and open-sourced on GitHub had over 700 stars. “It has had hundreds of downloads per year, and many game developers use it to create demos”, Frank said.
After graduation, Frank joined the Flutter team at ByteDance and became one of the most active open-source contributors on the team, contributing a number of PRs to Dart and Flutter. Frank remembers that when he was working on the package size issues, he proactively followed up on a relevant issue on the Dart GitHub project and noticed that the Specializer component could use some further tuning. He created a patch with his improvements to the Dart compiler middleware and submitted it to the project. The patch wasn’t accepted initially because of the large number of code blocks affected and a few minor concerns. He modified the patch seven times before it was accepted, and it was merged into the code base a week later.
There are many other open-source enthusiasts like Frank in the Flutter team at ByteDance.
The ByteDance team summarized this passionate attitude toward innovation with the following words:
“There are indeed many people in the industry who prefer mature technology, but it takes time for every technology to mature, and there will always be people like us who love to stay on the cutting edge.”
This is especially true for something as novel as Flutter. There needs to be some daring people who take the first steps. At ByteDance, the Flutter engineering team, as well as the engineering teams that they support, actively try and embrace new technologies. Doing this benefited ByteDance tremendously and greatly increased our productivity.
ByteDance has always wanted to be part of things that could push the industry forward, and Flutter is likely to be one of those things.