前段时间用Flutter For Web撸了一个公司内部使用的项目,本篇文章就把开发过程中碰到的坑点记录一下。
1.环境配置
Flutter For Web的开发需要将Flutter SDK升级至任意一个非Stable版本即可,升级完成后需要开启相关配置。
- 首先执行
flutter config
什么都没开启过的同学跟我上图的状态应该是一致的,--enable-web
就是开启web开发的,同理 --enable-macos-desktop
就是开启MacOS的,这里使用MacOS的小伙伴我推荐一并把MacOS的权限打开,因为后面有个坑需要用MacOS来填,不是MacOS的也不用紧张,这个坑只是会影响开发效率。
成功开启之后
此时执行
flutter devices
,即可看到对应的web设备
对应的代码编辑器需要大退重新启动就能选择对应设备
有些同学会遇到执行了--enable-web
去执行flutter devices
依旧看不到对应平台的设备,并且执行flutter config
看到后面会多出来(Unavailable)标志当前设置为失效状态.
最后检查出原因:虽然我与团队内部的另外一个小伙伴都为1.17.3的版本,但是我是用的github上面的标签版本,而他是从Flutter官网下载的。官网下载的压缩包为当时版本的一个快照,后续如果更新版本并不会移除Stable channel的标记,但是github是实时更新的。
他的版本执行flutter doctor
会有Stable channel的标记
但是从github上面Clone的项目执行出来会变成unknown
2.开发
Flutter For Web本身用起来跟Flutter差距不大
2.1 网络请求
身为一个老移动端开发,从一开始就没想到会在网络请求这里栽跟头,Web的跨域问题属实在初期给我愁的焦头烂额。有条件的前端同学可以直接让后端兄弟在接口请求头里添加 Access-Control-Allow-Origin
,也是非常舒服的。奈何我司分了相当多的领域,跟网关域的交涉并不顺畅,他们也有他们的难处,让本不富裕的时间雪上加霜。
最后搜集了各方面的资料,分为下面两种场景:
2.1.1 本地调试
-
本地调试相对容易一些,首先最简单的办法就是ip直连,比较粗暴但是简单有效。
-
第二种是一种比较取巧,也是我个人比较推荐的一种。在Flutter SDK如下位置添加
'--disable-web-security',
然后删除下面两个文件,之后执行flutter doctor
,等待重新编译出新的flutter_tools的可执行快照,直接flutter run -d chrome
。就是这种随心所欲的感觉,芜湖,起飞!
这种方式仅适用于本地启用的项目! 这种方式仅适用于本地启用的项目! 这种方式仅适用于本地启用的项目!
- 第三种就是本地自己搭建一个Web服务,然后通过代理服务完成请求,这种方式是最通用的方式,现在能搜到的办法大多数是这种。笔主采用的是Nginx。关于如何本地搭建Nginx这个问题,笔主与百度、Google等各大搜索门户建立了深入的合作,直接去搜索就行。
配置好本地服务,监听对应的启动端口即可。这时候肯定就会有小伙伴发问了,每次启动的时候发现端口都是随机的,Nginx该怎么配置监听的端口号呢。对于这个问题我只能说,问得好~
在我们执行flutter run -d Chrome
时是可以添加参数的。我们直接在后面添加--web-port=* --web-hostname=*
,或配置在代码编辑器里配置
VSCode
Android Studio
2.2 调试
这个部分我暂时没有很好的方案,FFW的调试有点惨不忍睹。正常编译器的在FFW的运行时断点速度有亿点点慢,对于这方面在调试UI时我暂时使用的是Flutter For Desktop。
部署之后只能通过print的形式进行输出。(PS:大佬有好的办法可以指教一下,小弟感激不尽)
2.3 交互
这里交互指跟其他项目如何交互。笔主开发的FFW项目是通过iframe的形式嵌入到公司已有的后台管理系统。目前用到的方式是通过 postmessage 的形式进行互相通信的。具体的通信代码如下:
FFW侧
import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart';
// ignore: avoid_web_libraries_in_flutter
import 'dart:html' as html;
import 'dart:convert';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
builder: BotToastInit(),
navigatorObservers: [BotToastNavigatorObserver()],
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
html.MessageEvent _nativeEvent;
html.MessageEvent get nativeEvent => _nativeEvent;
set nativeEvent(html.MessageEvent event) {
_nativeEvent = event;
_nativeEvent.ports.first.onMessage.listen((html.MessageEvent event) {
Map eventData = jsonDecode(event.data);
String type = eventData['type'] ?? "";
BotToast.showText(text: "Port2收到消息:type = $type");
});
}
void _incrementCounter() {
setState(() {
if (nativeEvent != null) {
nativeEvent.ports.first.postMessage("我是Flutter Port2");
BotToast.showText(text: "port2已发送消息");
}
});
}
@override
void initState() {
// TODO: implement initState
super.initState();
html.window.onMessage.listen((html.MessageEvent event) {
nativeEvent = event;
Map eventData = jsonDecode(event.data);
String type = eventData['type'] ?? "";
BotToast.showText(text: "收到消息:type = $type");
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
vue侧 因为涉及到太多公司业务代码,这里我就先不贴了,后续整理出一个最小demo再贴出来,大家也可以自行去Google web项目postmessage。
3.上线
3.1 部署
这个部分不多讲,执行Flutter build web --release
,等待完成之后,在根目录build文件夹中找到web,剩下的就是正常web项目部署。
结尾
后续在阅读遇到什么问题希望大家可以帮忙指正一下,在此感激不尽。