🐦 Flutter 移动端开发
Flutter 是 Google 推出的跨平台 UI 框架,使用 Dart 语言,一套代码可同时运行在 iOS 和 Android 平台。与 Web 技术栈不同,Flutter 自绘引擎提供接近原生的高性能体验。
Flutter vs React Native vs Web 对比
| 维度 | Flutter | React Native | 移动端 Web (H5) |
|---|---|---|---|
| 渲染方式 | Skia 自绘引擎 | 桥接到原生组件 | 浏览器 WebView 渲染 |
| 性能 | ⭐⭐⭐⭐⭐ 接近原生 | ⭐⭐⭐⭐ 需优化 Bridge | ⭐⭐⭐ 受限于 WebView |
| 开发语言 | Dart | JavaScript / TypeScript | HTML + CSS + JavaScript |
| 热重载 | ✅ 秒级 Hot Reload | ✅ Fast Refresh | ✅ 浏览器自动刷新 |
| UI 一致性 | ⭐⭐⭐⭐⭐ 像素级一致 | ⭐⭐⭐ 依赖原生组件 | ⭐⭐ 浏览器差异大 |
| 包管理 | pub.dev (30,000+) | npm (海量) | npm (海量) |
| 学习曲线 | 中等(需学 Dart + Widget) | 低(前端开发者友好) | 低(前端基础即可) |
| 适用场景 | 高性能跨平台 App | 快速迭代的跨平台 App | 营销页、内嵌 H5、混合 App |
Flutter 与 WebView 混合开发
在实际项目中,经常需要在 Flutter 中嵌入 H5 页面,或 H5 页面调用 Flutter 原生能力。
// ===== Flutter 中嵌入 H5(WebView)=====
import 'package:webview_flutter/webview_flutter.dart';
class H5Page extends StatefulWidget {
@override
State<H5Page> createState() => _H5PageState();
}
class _H5PageState extends State<H5Page> {
late final WebViewController _controller;
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
onPageFinished: (url) {
// 页面加载完成后注入 JS
_controller.runJavaScript('console.log("Flutter 注入的代码")');
},
),
)
// Flutter ↔ H5 双向通信
..addJavaScriptChannel('FlutterBridge',
onMessageReceived: (message) {
// 接收 H5 发来的消息
print('H5 发来消息: ${message.message}');
},
)
..loadRequest(Uri.parse('https://your-h5-page.com'));
}
// 原生调用 H5
void callJSFunction(String data) {
_controller.runJavaScript('window.onFlutterMessage($data)');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('H5 页面')),
body: SafeArea(
child: WebViewWidget(controller: _controller),
),
);
}
}
// ===== H5 端调用 Flutter(JavaScript)=====
// 在 H5 页面中发送消息给 Flutter
function sendToFlutter(action, params) {
if (window.FlutterBridge) {
FlutterBridge.postMessage(JSON.stringify({ action, params }));
}
}
// 接收 Flutter 发来的消息
window.onFlutterMessage = function(data) {
console.log('Flutter 发来消息:', data);
};
Flutter ↔ WebView 混合开发避坑指南
| 坑点 | 问题描述 | 解决方案 |
|---|---|---|
| JS Bridge 未就绪 | H5 页面过早调用 FlutterBridge.postMessage(),但 Bridge 还未注入完成,导致消息丢失 |
H5 端轮询等待 Bridge 就绪,或 Flutter 在 onPageFinished 后主动通知 H5 |
| 键盘遮挡 WebView | H5 内输入框聚焦时键盘弹出,WebView 不会自动调整,导致输入框被遮挡 | 设置 resizeToAvoidBottomInset: true(Scaffold 默认),或手动监听 viewInsets 调整 padding |
| Android 与 iOS WebView 差异 | webview_flutter 在 Android 用 Chromium,iOS 用 WKWebView,部分 JS/CSS 行为不一致 |
关键功能两端测试;避免使用 WebKit 私有 API;使用 flutter_inappwebview 替代可获得更统一的行为 |
| 返回键拦截 | 用户在 WebView 中浏览多页后按返回键,直接退出了整个页面而非返回 H5 上一页 | 使用 WillPopScope(或 PopScope)拦截返回事件,先判断 _controller.canGoBack(),有历史则 goBack() |
| Cookie 同步 | Flutter 原生登录态(Token)需要同步到 WebView,否则 H5 需要重新登录 | 通过 JS Bridge 传递 Token,H5 写入 localStorage;或加载 URL 时在 Header 中注入 Cookie |
| 白屏/加载慢 | H5 页面网络慢或资源大时,WebView 长时间白屏,体验差 | Flutter 端先展示骨架屏/Native Loading;设置 onPageFinished 后切换;H5 端启用资源预加载和离线缓存 |
| 内存泄漏 | WebView 未正确释放,反复进出页面后内存持续增长,导致 App 卡顿或 Crash | 在 dispose() 中调用 _controller.clearCache() 等清理;避免在多个页面中重复创建 WebView;及时移除 JS Channel |
| HTTPS 混合内容 | H5 页面为 HTTPS,但内部引用了 HTTP 资源(图片、接口),在 iOS WKWebView 中默认被拦截 | H5 端所有资源统一使用 HTTPS;Flutter 端可配置 App Transport Security 例外(仅开发调试用) |
| 文件上传/下载 | H5 中的 <input type="file"> 在 WebView 中默认无法触发 |
使用 flutter_inappwebview 处理文件选择回调;或通过 JS Bridge 由 Flutter 原生端实现文件选择和上传 |
| 调试困难 | WebView 中的 H5 控制台、网络请求无法直接查看,排查问题效率低 | Android: Chrome chrome://inspect 远程调试;iOS: Safari 开发菜单中连接 WebView;Flutter 端开启 onConsoleMessage 回调打印日志 |