How Alibaba Solved Flutter‑Web Challenges: A Practical Deployment Guide
This article details Alibaba’s experience building and deploying Flutter‑Web pages for the ICBU seller platform, covering version selection, code reuse, build optimization, deployment workflow, debugging, native JS integration, performance tuning, and compatibility fixes across various H5 containers.
Introduction
Flutter has become a mature cross‑platform framework since its first release in 2015, and it is widely used at companies such as Alibaba, Meituan, and Pinduoduo. More than 90% of new ICBU seller business is built with Flutter, and the ICBU client team has many Flutter engineers.
Flutter for Web (FFW) entered a stable branch with Flutter 2.0 in March 2021. The ICBU seller’s foreign‑trade information module needs a web page to host content, with the following requirements:
Replicate the UI and functionality of the app page (including a custom HTML parser written in Dart).
Quickly go live.
Keep features in sync with the app.
Because there was no front‑end support, the team chose FFW to implement the page.
Why Choose Flutter for Web
Switching to a front‑end stack such as Rax would increase cost and take longer to reproduce the page.
Most of the code for the target page can be reused from existing Dart code.
The Flutter stack is already widely covered by the team.
The project officially started the "FFW pit‑filling" journey.
Demo Links
External content demo: https://alisupplier.alibaba.com/content_page/#/web_news_detail?courseCode=PX8K3IYX&sourceId=cata_0
App download page: https://alisupplier.alibaba.com/content_page/#/sellerapp?s=c_ata
Problem Overview
Creating an FFW project is simple: switch Flutter to the stable channel, run flutter create xxxProject, and run the demo. However, integrating FFW into the Alibaba ecosystem requires handling version selection, code reuse, publishing, and runtime issues.
1. Flutter Version Selection
FFW requires Flutter 2.0+, while the current app uses Flutter 1.x+. The team chose the following branches:
FFA: hummer/release/v1.22.6.3 // UC's Hummer branch
FFW: origin/flutter-2.8-candidate.9 // Official branchFFW does not use the stable renderer because iOS 15 WebGL caused freezes; the issue is fixed in the candidate version (current stable is 2.10.0).
2. Code Reuse
Two main aspects need attention:
Dart code reuse – migrate to null‑safety (Dart 2.12) by adding ? to nullable types, using ?. or !., and replacing @required with the required keyword.
Platform‑specific plugin reuse – ensure the plugin has a Web version and supports null‑safety.
// Nullable variable
User? nullableUser;
// Safe call
nullableUser?.toString();
nullableUser!.toString();
// Required annotation migration
/// Old version
User({@required this.name, @required this.age});
/// New version
User({required this.name, required this.age});API changes such as updated typedef signatures also need manual adjustments.
3. Publishing System
After a local build succeeds, the following steps are required before publishing on the DEF platform:
Replace the src of main.dart.js with the CDN address of the current iteration.
Modify the <base> tag according to the URL‑strategy documentation.
Remove comments (DEF checks for them).
Replace the ?? operator (DingTalk H5 does not support it).
Move index.html and main.dart.js to the DEF product folder.
Only index.html and main.dart.js are needed for production; the rest of the build output can be omitted.
// Example index.html snippet for publishing
<!DOCTYPE html>
<html>
<head>
<!-- Use when deployed under a sub‑directory -->
<base href="/content_page/" />
</head>
<body>
<!-- Replace with CDN URL -->
<script type="text/javascript" src="https://g.alicdn.com/algernon/alisupplier_content_web/1.0.10/main.dart.js"></script>
</body>
</html>4. Build Commands
FFW can be built with two renderers:
// CanvasKit (WebAssembly) – higher performance, larger size
flutter build web --web-renderer canvaskit
// HTML – smaller resources, sufficient for most pages
flutter build web --web-renderer htmlThe team chose the HTML renderer because it produces a smaller bundle while meeting performance requirements.
5. Debugging
FFW can be debugged in Chrome similar to a native app. In VSCode, set breakpoints in Dart code; they appear both in the IDE and Chrome.
6. Page Routing
Multiple pages or URL parameters require route configuration similar to a normal Flutter app.
// MaterialApp configuration
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
onGenerateRoute: RouteConfiguration.onGenerateRoute,
onGenerateInitialRoutes: (settings) {
return [RouteConfiguration.onGenerateRoute(RouteSettings(name: settings))];
},
);
}
}
// Route map
class RouteConfiguration {
static Map<String, RouteWidgetBuilder?> builders = {
'/page_a': (context, params) => PageA(title: params?['title']),
'/page_b': (context, params) => PageB(param1: params?['param1'], param2: params?['param2']),
};
static Route<dynamic> onGenerateRoute(RouteSettings settings) {
var uri = Uri.parse(settings.name ?? '');
var route = builders[uri.path];
if (route != null) {
return NoAnimationMaterialPageRoute(settings: settings, builder: (c) => route(c, uri.queryParameters));
} else {
return CommonPageNotFound(routeSettings: settings);
}
}
}
// Navigator usage
Navigator.of(context).restorablePushNamed('/page_b?param1=123¶m2=abc');7. JS Interop (Calling Native JS from Dart)
Using the js package, Dart can call JavaScript functions directly.
// js_interface.dart
@JS()
library lib.content;
import 'package:js/js.dart';
@JS('alert')
external void jsAlert(String msg);
// Usage
import 'package:mtest/js_interface.dart';
jsAlert('Test message');8. Mtop Integration
To reuse the existing Alibaba Mtop gateway, the team added a Dart wrapper around mtop.js.
// index.html – include mtop.js
<script src="//g.alicdn.com/mtb/lib-mtop/2.6.1/mtop.js"></script>
// js_mtop_interface.dart – definition
@JS()
library lib.mtop;
import 'package:js/js.dart';
import 'package:js/js_util.dart';
import 'dart:convert';
import 'dart:js';
@anonymous
@JS()
class MtopParams {
external String get api;
external String get v;
external dynamic get data;
external String get ecode;
external String get dataType;
external String get type;
external factory MtopParams({required String api, required String v, required dynamic data, required String ecode, required String dataType, required String type});
}
@JS('lib.mtop.request')
external dynamic _jsMtopRequest(MtopParams params);
Object mapToJSObj(Map<String, dynamic> a) {
var object = newObject();
a.forEach((k, v) => setProperty(object, k, v));
return object;
}
Future mtopRequest(String api, Map<String, dynamic> params, String version, String method) {
var jsPromise = _jsMtopRequest(MtopParams(api: api, v: version, data: mapToJSObj(params), ecode: '0', type: method, dataType: 'json'));
return promiseToFuture(jsPromise);
}
@JS('JSON.stringify')
external String stringify(Object obj);
// Usage example
try {
var res = await mtopRequest('apiName', params, version, method);
print('res $res');
} catch (err) {
var data = stringify(err);
}9. Logging and Monitoring
Goldlog and ARMS are used for event tracking and performance monitoring.
// goldlog_interface.dart
@JS()
library lib.goldlog;
import 'package:js/js.dart';
@JS('goldlog.record')
external dynamic _jsGoldlogRecord(String t, String e, String n, String o, String a);
void goldlogRecord(String logkey, String eventType, String queryParams) {
_jsGoldlogRecord(logkey, eventType, queryParams, 'GET', '');
} // ARMS injection in index.html
<script type="text/javascript">
var trace = window.TraceSdk({
pid: 'as-content-web',
plugins: [
[window.TraceApiPlugin, {sampling: 1}],
[window.TracePerfPlugin],
[window.TraceResourceErrorPlugin]
]
});
trace.install();
trace.logPv();
</script>10. Performance Optimizations & Compatibility
Loading animation: show a skeleton iframe before main.dart.js finishes loading.
Consider splitting the large main.dart.js (≈1.2 MB) into smaller chunks for multi‑page apps.
Compatibility fixes:
DingTalk H5 – replace ?? operator and remove try/finally without catch.
WeChat H5 – replace MaterialIcons with image assets.
iOS 15 – WebGL2.0 issue causing freezes; waiting for Flutter stable fix.
RichText underline bug – disable underline or avoid RichText where it interferes.
Provider library – remove a huge error string in ProviderNotFoundException.toString() that breaks JS compilation.
JsonConverter – avoid using it directly; convert Dart lists to JS arrays manually.
11. Open TODOs
DEF cloud build – current 403 issues when fetching Flutter packages.
Local debug CORS for Mtop – need a proper host setup.
Video/Audio playback support.
JS bundle splitting and custom font optimization.
Further research on dynamic Flutter page replacement (Weex‑like) and WebApp conversion.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
