作者:phpxiaofei | 来源:互联网 | 2023-08-21 14:14
FlutterWeb开发进出坑总结-theme:smartblue在前一阵用flutter开发web的过程中,遇到了一些坑,遇到了一些与app端开发不一样的地方,当然也学到了
theme: smartblue
在前一阵用flutter开发web的过程中,遇到了一些坑,遇到了一些与app端开发不一样的地方,当然也学到了一些新知识点,所以总结一下,一方面帮助他人,另一方面,自己也能加深记忆,要不容易忘。(年纪大了?记忆力还不行了呢???)
一、启动运行乱码
没错,启动一个demo,遇到坑了,如图所示
点击Android Studio上方运行按钮,程序启动之后汉字文字显示乱码,这是由于flutter web有三种渲染模式,auto 、html 和 canvaskit,点击运行按钮(flutter build web命令)默认的渲染模式为auto,这种模式在移动端使用html渲染,在pc端使用canvaskit渲染。
解决办法 1: 用命令行运行,并指定渲染模式,就能解决问题。
// 指定渲染模式为html
flutter build web --web-renderer html
解决办法 2: 上面虽然能解决问题,但我习惯用按钮运行程序怎么办?当然也找到了其他办法。在程序包下web/index.html文件中body标签下copy如下代码。
二、Debug启动运行断点失败
web开发和APP端开发一样,也可以断点。项目之初断点是可以的,但是不知道怎么的,debug可以运行,但断不到,很奇怪,花了一上午,发现同事因为发版改了下web/index.html中head->base标签下 href="***"的值。
解决办法 :
// 之前,断点可用
// 同事改动,断点不可用
// 修复后,断点可用
不能断点开发实在是麻烦。
三、Hot Reload热重载、点击浏览器刷新,都会重启整个程序
在APP端开发时,在某个页面点hot reload按钮,只会重新运行当前页面,但是在web中,点热重载会重启,这只是开发中的不方便。已经上线的程序,用户只要点击浏览器刷新就会重启整个程序,无论在哪个页面,都会回到第一个页面,这与我浏览网页的习惯明显是不符的。
查找原因,发现是flutter底层问题,仔细观察web页面是通过不同的url来确定的,而Flutter从始至终都是一个url,只是flutter在一个网页中绘制了不同的页面(与APP端原理一致),所以想解决问题就是要每个页面都有自己的url。
解决办法 : 用静态路由的方式跳转页面和传参,具体代码如下。
// 跳转与传参
static Future toName(String pageName, Map params) {
var uri = Uri(scheme: RoutePath.scheme, host: pageName, queryParameters: params);
return Navigator.of(currentContext).pushNamed(uri.toString());
}
// 取参方式
static Route generateRoute(RouteSettings settings) {
return PageRouteBuilder(
settings: settings,
pageBuilder: (BuildContext c, Animation a,Animation sa) {
var uri = Uri.parse(settings.name ?? ''); //解析页面名
switch (uri.host) {
case RoutePath.name:
return NamePage(uri.queryParameters); 、、传参
default:
return Scaffold(
body: Center(
child: Text('没有找到对应的页面:${settings.name}'),
),
);
}
});
}
通过以上方式,跳转时每个页面都会有自己的url和拼接的参数,这样刷新的时候就不会重启整个程序,会停留在当前页面。
四、用静态路由的方式跳转,全局变量,单例对象丢失,页面栈记录丢失。
没错,坑是连着的,我也是服了。当在某页面热重载或点击浏览器刷新,会停留在当前页面,但是无法返回,就算点击跳转至其他页面,也会报错,因为全局变量都已经丢失,比如:登录信息,用户信息,已经初始化的工具类对象等。
已经有人提了Issues,国内也有大神分析了原因和不完全结局方案
目前flutter web对于浏览器还是没有适配完全,无论Navigator1.0还是Navigator2.0,都存在不可解决的严重问题。目前来看google的对flutter web的意图,还是开发移动web并在App中通过webkit这种内核使用,并没有想开发者使用flutter web来开发真正的web应用,或者后续会完善这部分。
我的解决方案
- 除了登录页和首页,其他页面不用静态路由的方式跳转,这样做即使用户刷新,也不会回到登录页,而是回首页。
- 在有刷新需求的页面上提供刷新图标,可触发刷新,避免用户点击浏览器的刷新。
- 全局变量持久化,用html.window.localStorage并配合工厂模式持久化数据,当被触发刷新,会从本地重新赋值,比如:登录信息等。
- 弱化全局成员变量,非必要不使用全局类的变量,数据尽量放云端,页面间不耦合。
五、检测浏览器/标签页关闭还是刷新
解决办法 : 可以使用函数onBeforeUnload来检查选项卡是否正在关闭。它也可能检测到页面刷新。
import 'dart:html' as html;
html.window.onBeforeUnload.listen((event) async{
// do something
});
或者
import 'dart:html' as html;
html.window.onUnload.listen((event) async{
// do something
});
六、引用 import 'dart:html' 运行提示报错
多端运行,如果引用了html会提示报错。
解决办法 : 可以引用第三方universal_html 2.0.8,帮封装了一层,支持多端。
universal_html :适用于所有平台的“dart:html”,包括 Flutter 和服务器端。简化跨平台开发和 HTML / XML 处理。
七、可点击提示
在平常浏览网页时,鼠标滑动到可点击的文字或按钮上,鼠标“箭头”会变成一个“小手”,或背景出现颜色变化提示。
Flutter中常用的GestureDetector()手势工具,虽然可以实现点击等回调,但是鼠标滑动到可点击区域,鼠标“箭头”并不会变成“小手”,在交互上不符合大众使用网页的习惯。
解决办法 : 使用InkWell替换GestureDetector,用InkWell包住的按钮或文字,鼠标悬停,就会出现小手。
Ink(
width: width,
height: height,
color: color,
child: InkWell(
focusColor: Colors.transparent,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
hoverColor: const Color(0x0818a7fb),
onTap: onTap,
child: Center(child: this)))
分析源码可知,内部用MouseRegion监听了鼠标位置,那什么是MouseRegion呢?
八、鼠标监听控件 MouseRegion
相对于APP端,web端多了个鼠标,可以实现app实现不了的交互效果,比如悬停,划过,进入退出某区域等,都可以用MouseRegion实现。
MouseRegion的属性和说明
| 字段 | 属性 | Col3 |
| --- | --- | --- |
onEnter|PointerEnterEventListener|鼠标进入区域时的回调
onExit|PointerHoverEventListener|鼠标退出区域时的回调
onHover|PointerExitEventListener|鼠标在区域内移动时的回调
cursor|MouseCursor |鼠标悬停区域时的光标样式
opaque|bool |是否阻止检测鼠标
child|Widget |子组件
最后
这是目前遇到有价值的坑,后面遇到新的也会持续更新。
Flutter开发web上,路由和全局变量上的坑还是挺严重的,但只要没有复杂的页面间逻辑,普通展示完全没问题。