技术经验谈 技术经验谈
首页
  • 最佳实践

    • 抓包
    • 数据库操作
  • ui

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • 总纲
  • 整体开发框架
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

hss01248

一号线程序员
首页
  • 最佳实践

    • 抓包
    • 数据库操作
  • ui

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • 总纲
  • 整体开发框架
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 学习资料

  • 技术选型

  • 要点

    • dart语言核心要点
    • 工程化

      • flutter依赖管理
      • flutter代码模板优化和禁用.android文件夹刷新
      • flutter客户端项目适配web做的一些工作
        • 正确的解决方式
        • cookie问题
          • 后台配置
        • flutter端:
        • 效果:
          • web in mobile时不显示appbar,避免重复的标题栏:
        • ~~7.1 配置 URL 策略~~
        • ~~7.2 nginx配置~~
        • 输入中文时残留首字母
        • 字数统计异常
        • flutter_pulltorefresh :
        • lottie-flutter:
      • web跨域问题终结者
      • flutter编译流程修改
      • flutter工程的模块化架构
      • flutter web编译瘦身
      • webview_flutter官方插件的增强-对inputfile和权限请求的支持
    • flutter异步
    • flutter工程化
    • flutter存储相关
    • flutter状态管理
    • flutter网络框架以及相关要点
    • flutter图片相关
    • flutter可观测性和调试
    • flutter插件开发
    • flutter路由管理
    • 遇到的问题
  • 经验

  • flutter
  • 要点
  • 工程化
hss01248
2022-09-30
目录

flutter客户端项目适配web做的一些工作

# flutter客户端项目适配web做的一些工作

# 1 按平台引入一些不兼容的库/类,或者直接移除

  • 比如dart:ffi, dart:io

条件引入示例:

import 'dart:io' if (dart.library.html) 'dart:html';
1
  • 不能在代码里使用File(path), File类在io和html包里都有,但构造方法的参数个数不一致,直接导致编译失败
  • 不能在代码里使用Platform类(调用到才报错,不会导致编译失败)

image-20220930160753267

image-20220930161016150

# 2 dio适配

    if (getx.GetPlatform.isWeb) {
      //var cookieJar = PersistCookieJar(storage: GetXStorageImpl());
      //var cookieJar=PersistCookieJar(storage: Storage);
      // DioUtils.instance.dio.interceptors.add(CookieManager(cookieJar));
      //没用,httponly的cookie无法读取和保存
      //web要设置options.extra["withCredentials"] = true; 但tmd还是没有用
      /*  interceptors0.add(InterceptorsWrapper(onRequest: (options, handler) async {
          options.extra["withCredentials"] = true;
          return handler.next(options);
        }));*/
      //要用专门给浏览器写的adapter,并开启withCredentials
      BrowserHttpClientAdapter adapter = BrowserHttpClientAdapter();
      adapter.withCredentials = true;
       //还需要cookie本身host匹配  这个只能后台设置好,一般推荐设置泛二级域名,并且设置好cookie的跨域功: SameSite=None; Secure
      DioUtils.instance.dio.httpClientAdapter = adapter;
    } else {
      getApplicationDocumentsDirectory().then((appDocDir) {
        String appDocPath = appDocDir.path + "/cookies/";
        var cookieJar = PersistCookieJar(storage: FileStorage(appDocPath));
        //var cookieJar=PersistCookieJar(storage: Storage);
        DioUtils.instance.dio.interceptors.add(CookieManager(cookieJar));
      });
      //pc代理自动读取和设置到dio
      ProxyHepler.config(DioUtils.instance.dio)
          .then((value) => null)
          .onError((error, stackTrace) => null);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

请求拦截器在web上不允许修改ua:

if(!GetPlatform.isWeb){
  //web不允许设置ua
  options.headers['user-agent'] = packageUserAgent;
}
1
2
3
4

请求的header匹配以下不安全字符时,将被终止,具体参考如下:

Accept-Charset Accept-Encoding Connection Content-Length Cookie Cookie2 Content-Transfer-Encoding Date Expect Host Keep-Alive Referer TE Trailer Transfer-Encoding Upgrade User-Agent

# 3 本地调试的跨域问题

包括请求跨域和cookie跨域两个问题

除了localhost调试的跨域问题, 最终打包发布的跨域问题要在nginx或后台服务代码里配置.

# 半吊子的方式:

利用flutter-cors工具,修改pc上flutter sdk里chrome的配置,仅本机有效,仅请求跨域有效.

cookie跨域还得服务端修改或者用代理服务器修改. 不值得做.

具体见: https://blog.hss01248.tech/pages/cdc4b9/#%E8%A7%82%E5%AF%9F%E8%80%85

# 正确的解决方式

两种方式:

  • 类似web端开发,引入self -proxy库,本地自建代理服务器进行代理请求.需要改项目代码,已包装好,一劳永逸解决请求跨域和cookie跨域,让你拥有和web开发一样的丝滑体验: https://pub.dev/packages/web_dev_proxy_shelf

# cookie问题

https://github.com/flutterchina/dio/issues/1131

https://github.com/flutterchina/dio/issues/1270

其实这些都不是dio框架的问题,也不是flutter编译的问题,而是后台配置的问题

只要后台配置了cookie跨域,flutter侧就可以正常使用cookie

必须后台配置,flutter编译成web后无法在拦截器里强制设置cookie,否则报错.这是浏览器的内部设定导致的.

见上面的列表

# 后台配置

整体请求配置跨域+ 返回cookie时配置: SameSite=None

        ResponseCookie cookie2 = ResponseCookie.from("staff-token",token) // key & value
                .httpOnly(false)		// 禁止js读取
                .secure(true)		// 在http下也传输
//                    .domain("localhost")// 域名
                .path("/")			// path
                .maxAge(24*3600*100L)	// 有效期
                .sameSite("None")	// 大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外
                .build();
        // 设置Cookie Header
        response.setHeader(HttpHeaders.SET_COOKIE, cookie2.toString());
1
2
3
4
5
6
7
8
9
10

整体配置跨域: 非必须.

也可以在nginx入口配置

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")//项目中的所有接口都支持跨域
                .allowedOriginPatterns("*")//所有地址都可以访问,也可以配置具体地址
                .allowCredentials(true)
                .allowedMethods("*")//"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"
                .maxAge(36000);// 跨域允许时间
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# flutter端:

 if (getx.GetPlatform.isWeb) {
      //要用专门给浏览器写的adapter,并开启withCredentials
      BrowserHttpClientAdapter adapter = BrowserHttpClientAdapter();
      adapter.withCredentials = true;
      //还需要 后台设置好cookie的跨域: SameSite=None; Secure
      DioUtils.instance.dio.httpClientAdapter = adapter;
1
2
3
4
5
6

# 效果:

image-20220930180145119

下一个请求携带cookie:

image-20220930180238537

stat接口后台收到的cookie:

image-20220930180332742

# 4 flutter插件适配

为防止某只支持Android/ios的插件在web上调用异常,要加await再捕获:

比如

try{
	await  _flipperNetworkPlugin.reportRequest(uniqueId,DateTime.now().millisecondsSinceEpoch,
      '${options.baseUrl}${options.path}',options.method,headers,body);
}catch(e,s){
  debugPrint("print by dio-flutter: reportRequest--> "+e.toString());
  //+s.toString()
}
1
2
3
4
5
6
7

# 5 debug时外部访问localhost

flutter run -d chrome --web-hostname xxx.xxx.xxx.xxx --web-port xxxx

# 6 网页标题

https://juejin.cn/post/6967993169115873316

加一行:

@override
Widget build(BuildContext context) {
    SystemChrome.setApplicationSwitcherDescription(ApplicationSwitcherDescription(
      label: '当前页面的标题',
      primaryColor: Theme.of(context).primaryColor.value,
    ));


  return Container(...);
}
1
2
3
4
5
6
7
8
9
10

# web in mobile时不显示appbar,避免重复的标题栏:

根据platformType进行判断即可,在全局配置中修改

# 7 路由和url path

https://www.jianshu.com/p/17c1984d8670 flutter web路由实践杂谈

默认是host/#路由,和vue一样,怎么处理成host/路由?

https://flutter.cn/docs/development/ui/navigation/url-strategies

Flutter Web 应用支持两种基于 URL 的路由的配置方式:

Hash(默认) 路径使用 # + 锚点标识符 (opens new window) 读写,例如:flutterexample.dev/#/path/to/screen。

Path 路径使用非 # 读写,例如:flutterexample.dev/path/to/screen。

目前flutter3.7.0推荐使用Hash模式. 不要再使用path模式. hash模式的带#一样可以分享出去直接访问. path模式有各种bug, 404,以及参数无法传递等等.

# 7.1 配置 URL 策略

让 Flutter 使用 path 策略,请使用 flutter_web_plugins (opens new window) 库中提供的 setUrlStrategy (opens new window) 方法。

content_copy

import 'package:flutter_web_plugins/url_strategy.dart';

void main() {
  usePathUrlStrategy();
  runApp(ExampleApp());
}
1
2
3
4
5
6

此时,访问子路由会出现404,这时需要配置nginx:

# 7.2 nginx配置

原来是托管应用页面的 web 服务(例如 Nginx),默认会把多级路径的 URL 按目录去解析,访问最后一个目录下的 index.html 文件。而对于单页应用来说,显然后续的路径都是作为参数来使用的。而 Hash 模式由于使用一个井号把后续路径隔开了,自然就没有这个问题了。

通过在 Nginx 的配置文件中添加如下配置:

location / { 
    # ...
    try_files $uri $uri/ /index.html;
}

1
2
3
4
5

意为:先尝试访问 URL 和其路径下的文件,如果不存在,则直接访问根目录的 index.html。

这样就能在线上部署环境顺利解析并传递参数了。

为什么开发的时候没有出现这个问题呢?原因就是在调试模式下,Flutter 优化了本地开发服务器,会优雅的处理各类 URL 策略并指向根目录的 Html 文件,也就是替我们做了上面这一步。

参考: https://juejin.cn/post/7167710704550543397

# 8 图片跨域

使用canvaskit时,图片的拉取不是用img标签,而是ajax请求,所以,使用html渲染器即可,imageview会被编译为img标签

flutter run -d chrome --web-renderer html
flutter build web --web-renderer canvaskit
flutter build web --web-renderer html    # 这个命令
1
2
3

# 9 第一次白屏时间太长

Dart.js的瘦身后面再做,先加个炫酷的loading再说. 其实就是在index.html里贴个css动画

参考: CSS 3.0实现加载动画 (opens new window)

实际效果: https://navi.hss01248.tech/

可以直接改web模板:

# 10 输入框的一些问题

# 输入中文时残留首字母

参考: https://blog.csdn.net/m0_55782613/article/details/129329827

一、Bug样例

建立一个web demo flutter run -d chrome --web-renderer html 出现问题: 输入中文的时候,比如打字 hao, 第一个字母h会先输入,变成 h奥

二、解决

网上资料说是因为在text onChange中使用了setState刷新会打断输入,实际测试,就算不设置onChange, 不设置controller,一样会导致错误

最后将TextField替换为 TextFormField之后,问题得到解决

三、总结

这个问题在StackOverflow上面查不到相关解释,Flutter在Web上的兼容性问题还是存在的

# 字数统计异常

中文输入法统计正在编辑中文的过程中会统计英文,假如限制5个中文,当输入4个中文后,最后一个中文输入2个及以上英文时,会触发最大字数限制,这当然不是我们想要的效果。

下面说下如何修复这个问题,关键是 TextField 中 「controller.value.composing」 这个属性,官方文档说明:

❝The range of text that is still being composed. 仍在编写的文本范围。 ❞

  TextEditingController _controller = TextEditingController();
  int _wordLength = 0;
  /// 计算字数,不算正在编辑的文字
  void _computeWordCount() {
    var valueLength = _controller.value.text.length;
    var composingLength =
        _controller.value.composing.end - _controller.value.composing.start;
    setState(() {
      _wordLength = valueLength - composingLength;
    });
  }


TextField(
  controller: _controller,
  onChanged: (value){
    _computeWordCount();
  },
  decoration: InputDecoration(
      counterText: '$_wordLength/32'
  ),
),
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 在html渲染引擎下支持不好的一些库:

# flutter_pulltorefresh (opens new window) :

不支持web pc 滚轮滚动

解决方案:

https://github.com/peng8350/flutter_pulltorefresh/issues/339

# lottie-flutter (opens new window):

pc的web和移动端的web兼容能力不同

https://github.com/xvrh/lottie-flutter/issues/206

解决方案: web下用其他动画实现

编辑 (opens new window)
上次更新: 2023/05/09, 17:55:07
flutter代码模板优化和禁用.android文件夹刷新
web跨域问题终结者

← flutter代码模板优化和禁用.android文件夹刷新 web跨域问题终结者→

最近更新
01
截图后的自动压缩工具
12-27
02
图片视频文件根据exif批量重命名
12-27
03
chatgpt图片识别描述功能
02-20
更多文章>
Theme by Vdoing | Copyright © 2020-2025 | 粤ICP备20041795号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式