Flutter 手机端项目转web运行部分问题记录
最近尝试将平板程序编译成web应用发布,发布后程序可以运行,但是存在一些问题。现将问题和解决过程记录如下
-
web应用加载耗时问题
- 加载时会去www.gstaic.com下载/chromium/canvaskit.js文件和canvaskit.wasm文件,
- 加载时会去www.gstaic.com下载notosanssc字体文件和roboto字体
打包指令
我的打包指令为:
flutter build web -t lib/main.dart --no-tree-shake-icons --release
解决下载字体的问题
-
将需要下载的资源文件预下载放入项目中
- 从对应的字体网址下载好roboto.ttf和notosanssc.otf
- 将字体文件放在项目中的资源文件中
- 在项目的ymal文件中加入Font声明
- 打包项目,打包后build文件如下
运行测试发现roboto.ttf文件已经可以从资源中下载,但是notosanssc.otf任然会从gstaic.com下载
-
解决notosanssc.otf仍然需要下载的问题
- 修该ymal中Roboto字体的资源文件为notosanssc.otf
- 打包测试
- 不会再从gstaic中下载文件。这个方式不清楚原因,有知道原因的欢迎评论指正
-
解决canvaskit.js文件和canvaskit.wasm问题
- buil目录中发现存在canvaskit.js和canvaskit文件
-
修改--dart-define=FLUTTER_WEB_CANVASKIT_URL为:canvaskit/,即
- --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/
-
此时编译指令为:
-
flutter build web -t lib/main.dart --no-tree-shake-icons --release --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/
-
-
测试
-
chrome和safari加载时间不一致的问题
-
canvaskit.wasm这个文件在chrome中需要耗时16s,在safari仅仅需要60ms
-
chrome
-
-
safari
-
这个问题也不清楚原因,暂时未解决
-
-
web浏览器刷新,url会重置的问题
我的项目是一个插件项目,需要主程序通过url传入一个资源地址,然后插件项目读取地址栏的地址,去下载并加载对应文件。主要代码如下:
void main() async {
String? assetsApi = Uri.base.queryParameters['assetsApi'];
printDebug("wbeApp start assetsApi:$assetsApi");
runApp(MyApp(interface: MyControl(assetsApi: assetsApi ?? "")));
}
Widget build(BuildContext context) {
return GetMaterialApp(
home: AppUI(
interface: interface,
) ,
);
}
- 此时debug直接运行:http://localhost:51335/#/
此时程序可以读取地址栏的url,并正确加载
-
但是加载完成后,无法记录我们输入的url地址栏url再次变为:http://localhost:51335/#/
-
解决方法
- 使用setUrlStrategy方法
-
import 'package:flutter_web_plugins/flutter_web_plugins.dart'; void main() async { String? assetsApi = Uri.base.queryParameters['assetsApi']; printDebug("wbeApp start assetsApi:$assetsApi"); setUrlStrategy(PathUrlStrategy()); runApp(MyApp(interface: MyControl(assetsApi: assetsApi ?? ""))); }
- 对app增加router
-
Widget build(BuildContext context) { String basePath = Uri.base.path; debugPrint("basePath:$basePath"); String router = "/?assetsApi=${interface.url}"; debugPrint("router:$router"); return GetMaterialApp( initialRoute: router, routes: { router: (_) { return AppUI( interface: interface, ); } }, onGenerateRoute: (settings) { debugPrint('onGenerateRoute name: ${settings.name}'); }, onUnknownRoute: (settings){ debugPrint('onUnknownRoute name: ${settings.name}'); }, // home: AppUI( // interface: interface, // ) , ); } }
- 此时从url加载完成程序后,地址栏不会重置,会记住上次的目录
- initialRoute的值不知道如何填写的时候,可以从onUnknownRoute或者onGenerateRoute中寻找
-
dart.io和dart:js
web项目中使用dart.io库中的API时会异常报错
非web项目无法使用dart:js的API时也会异常
那如何在一个功能中通过平台判断来使用不同的API呢
-
如何在同一个项目中同时使用dart.io和dart.js两个库的
操作如下:
- 定义接口类abstract class,使用import if 来做头文件导入判断
app.dart
import 'desktop_mobile.dart'
if (dart.library.html) 'web.dart';
abstract class App{
Future<Map> init();
factory App() => getApp();
}
- 实现非web接口类
mobileApp.dart
-
import 'dart:convert'; import 'package:flutter/services.dart'; import 'app.dart'; class MobileApp implements App { @override Future<Map> init() async { String data = await rootBundle.loadString("assets/project_json.json"); Map jsonData = jsonDecode(data); return jsonData; } } App getApp() => MobileApp();
-
实现web接口类
- 从js传过来的json格式数据可能导致dart无法识别,所以改为传jsonString
webApp.dart
import 'dart:convert';
import 'package:flutter/services.dart';
import 'app.dart';
import 'dart:js' as js;
class WebApp implements App {
@override
Future<Map> init() async {
String? jsonString = js.context["appConfig"];
jsonString ??= await rootBundle.loadString("assets/project_json.json");
return jsonDecode(jsonString);
}
}
App getApp() => WebApp();
- 使用
main.dart
import 'app/app.dart';
Map jsonData = await App().init();
-
runtimeType在web和手机端不同
web平台下数据的runtimeType和非原生不一样,判断数据类型时最好用is关键字来判断
如:
dynamic values = json[key];
if (values != null && values is Map) {
}
而不使用
dynamic values = json[key];
if (values != null && values.runtimeType.toString().contains("Map")) {
}
以下是部分整理
-
macos平台
// int
dynamic int1 = 1; // int
int int2 = 1; // int
// double
dynamic double1 = 1.0; // double
double double2 = 1.0; // double
// number
dynamic num1 = 2; // int
dynamic num2 = 2.0; // double
num num3 = 2; // int
num num4 = 2.0; // double
// string
dynamic string1 = "1.0"; // String
String string2 = "1.0"; // String
// map
dynamic map1 = {"1":2};// _Map<String, int>
dynamic map2 = {"1":2,"2":"2"};// _Map<String, Object>
dynamic map3 = {"1":2,"2":"2",1:'2'};// _Map<Object, Object>
Map map4 = {"1":2}; // _Map<dynamic, dynamic>
Map<dynamic,dynamic> map5 = {"1":2}; // _Map<dynamic, dynamic>
Map<String,dynamic> map6 = {"1":2}; // _Map<String, dynamic>
Map<dynamic,String> map7 = {"1":"2"};// _Map<dynamic, String>
// list
dynamic list1 = [1,2];// List<int>
dynamic list2 = ["1",2];// List<Object>
dynamic list3 = [1,2,{}];// List<Object>
List list4 = [{"1":2},{"1":2,"2":"2"}];// List<dynamic>
-
web平台
// int
dynamic int1 = 1; // int
int int2 = 1; // int
// double
dynamic double1 = 1.0; // int
double double2 = 1.0; // int
// number
dynamic num1 = 2; // int
dynamic num2 = 2.0; // int
num num3 = 2; // int
num num4 = 2.0; // int
// string
dynamic string1 = "1.0"; // String
String string2 = "1.0"; // String
// map
dynamic map1 = {"1":2};// IdentityMap<String, int>
dynamic map2 = {"1":2,"2":"2"};// IdentityMap<String, Object>
dynamic map3 = {"1":2,"2":"2",1:'2'};// LinkedMap<Object, Object>
Map map4 = {"1":2}; // LinkedMap<dynamic, dynamic>
Map<dynamic,dynamic> map5 = {"1":2}; // LinkedMap<dynamic, dynamic>
Map<String,dynamic> map6 = {"1":2}; // IdentityMap<String, dynamic>
Map<dynamic,String> map7 = {"1":"2"};// LinkedMap<dynamic, String>
// list
dynamic list1 = [1,2];// List<int>
dynamic list2 = ["1",2];// List<Object>
dynamic list3 = [1,2,{}];// List<Object>
List list4 = [{"1":2},{"1":2,"2":"2"}];// List<dynamic>
-
Flutter web嵌入web项目
Flutter build的产物作为一个静态资源放在web项目中,web项目想调到flutter页面中,使用window.open函数
window.open(URL,name,specs,replace)
转载自:https://juejin.cn/post/7247028435340017724