原创: 涂金林
![98c0e00896dabdfa5e683e122cd61e50.png](https://i-blog.csdnimg.cn/blog_migrate/279f235d28d6e3ebafb0663dd2f32f3f.jpeg)
Flutter For Web 已经发布半年多时间,跑在 Flutter 实践道路上的腾讯企鹅辅导团队是如何应用的?
今年 9 月,作为腾讯 Flutter 实践团队之一的我们,有幸参与了 GDD 大会上 Flutter 应用视频的录制,感受到国内众多开发者对 Flutter 的热情。两天时间,讲道理,其实没有太多的干货,但收获还是满满的。有那么点空闲的时间,也关注了一下其他的同学,大家讨论的重心还是第一天主会场的内容。我想吸引大家的一直是 Flutter 新版本 stable1.9 的重磅发布。
新版本一个重要功能就是 Flutter For Web 仓库合入 Flutter master 主仓库,意味着我们可以真正地使用一套代码、一套资源部署大前端。辅导团队经过一段时间的准备,使用 Flutter 开发的 Web 页面也即将发布,希望和大家分享下实践过程和踩坑实例,欢迎一起交流探讨。
页面改造
Flutter For Web 谷歌官方一直建议暂时不要在生产环境使用。但如果不尝试,我们永远不知道这里会藏有什么样的秘密,什么样的场景适合。对现有 Flutter 改造,增加对浏览器的支持,突破而不失稳重。
1. 增加 Web 支持
我们原先的版本使用 Flutter 1.5.4,增加 Web 支持,我们需要切换到最新的 master 分支,改造运行。
![ef704f09b46e56c6c3d03e822d3507a0.png](https://i-blog.csdnimg.cn/blog_migrate/21e8155cb36df2b7668bdbee72eac815.jpeg)
如果我们只是新建一个新的项目,只需切换到最新的 master 分支,创建一个新的项目即可。创建完成之后,根目录下会多出一个 web 文件夹,里面只有一个 web 入口文件 index.html。
![17aaf7d0daf81fa6a47d9cc949c9b944.png](https://i-blog.csdnimg.cn/blog_migrate/3e9647d1d919a473c69248d982465b00.jpeg)
2. 修改 dart:io 库
原有项目中如果直接运行 flutter run -d chrome,会发现控制器中 import 报错,原因是 dart: io 库不支持 web。io 库是 Flutter 中非常常用的库,主要是平台相关的一些 api,改造的第二步我们就需要屏蔽 dart:io 的引入。
Flutter For Web 最终运行的是在浏览器中的 js 代码,Flutter For Phone 使用,Platform 引擎与 Native 通信。js 在平台上是通过其他的系统支持(iOS 中的 JavaScriptCore) 与 Native 通信,二者是完全不同的方式,所以 dart: io 无法继续支持。
既然 dart: io 不支持 web,那我们仍然想使用原先的 Flutter 业务 UI 代码,该 如何实现(上文我们说过,我们想使用同一套代码、同一套资源整合大前端)?我们使用不同平台下支持的能力库区分。
import 'main_web.dart' if (dart.library.io) "main_io.dart"; //dart.library.io
![1c605fd0dcbf379c4fabf23268e8414b.png](https://i-blog.csdnimg.cn/blog_migrate/7c4ca2c097a848bc46118a8b5af5db17.jpeg)
UI 侧我们仍然使用同一套代码,逻辑侧通过 library 库分区 native 还是 web,逻辑侧对应不同的平台实现。这几天,我看到一个新的区分方法,简单而且实用:web 侧我们可以判断 0 和 0.0 是否是一个对象。
![4448904c2f20e5c72e04029fe1172ce2.png](https://i-blog.csdnimg.cn/blog_migrate/5ddddeb7439f529e3b6bb1b2dd7df5e1.jpeg)
区分完不同平台,我们还需要辨别不同设备,这里我建议搭建 app 初始化的时候配置完成,后续方便实用。
3. 网络请求
原有 Flutter 项目我们通过 MJFlutter 调用 Native 的网络接口,实现数据请求。辅导 web 侧是通过 CGI 请求获得后台数据,现在 web 需要另一种方式,给大家推荐几种方式:
a. package:http/http.dart
pub 链接:https://pub.dev/packages/http#-readme-tab-
b. package:http/html.dart
c. package:dio/dio.dart
pub 链接:https://pub.dev/packages/dio#-installing-tab-
dio 这个网络库,大家之前可能用过,需要注意一点,3.0 之前的版本 Flutter For Web 不支持 (dart:io)。3.0 之后的版本,这个库经过官方大改造,现已支持 Flutter For Web 开发。我推荐的是 dio 这个库,使用起来会比较方便,参数简单好理解。
有了基本的网路请求,配置完 CGI,接下来就是界面的展示。但是你会发现,所有的业务请求都没有回包,无法得到数据。如果作为移动端开发,没有经常接触到 http 请求,一下子发蒙,我觉得可以理解。这个是 Web 开发中经常遇到的 跨域问题。前端开发们这里可以举手了,这个问题我会。简单的处理方式就是开发过程中挂上一个代理,具体就不详细展开了。
网路问题解决,进入发布调试,上文我们介绍过 Flutter For Web 的最终产物是三个文件。如何发布,前端同学又可以举手了。有一个简单的方式:github pages(配置方法:https://pages.github.com/)。
JavaScript 扩展
与使用 Flutter 实现的页面不同,Flutter For Web 的页面使用 Http 请求获取后台数据。Http 请求中很重要的一点是 header 中的 cookie,这里就需要通过 dart 与 js 之间的交互,还是要称赞下 Flutter 团队的实力。
官方说明:https://dart.dev/web/js-interop
pub 地址:https://pub.dev/packages/js
![3f5d2ed13ec0d7175baddbc5523d4df5.png](https://i-blog.csdnimg.cn/blog_migrate/84e1bb41acf7832e34f9987883f34b69.jpeg)
![cc0f49e564e5358b32bf80a4f8ce329a.png](https://i-blog.csdnimg.cn/blog_migrate/200ed37d221ee2af6382e6f7ec770361.jpeg)
dart 封装 js 方法
![95c5afaf79b25b563accded563c1013a.png](https://i-blog.csdnimg.cn/blog_migrate/a8751424373db8de8b9f41cd9d560bae.jpeg)
![e7083aef85df3c30573d8baac93827d4.png](https://i-blog.csdnimg.cn/blog_migrate/6ad3895b9ab4b340b12aeb6f8357b947.jpeg)
添加完 js 支持后,dart 侧调用 getStringFromJS 方法,并打印结果。
通过 js.dart 这个库,可以实现 web 侧的主要功能。例如 cookie、localStorage。
![7bf8f98e60c5e31c408682980f789bf7.png](https://i-blog.csdnimg.cn/blog_migrate/c58cf6af9356c6c9f04d0fc46236dc20.jpeg)
![9ceee699aa2df0de3cad24b6cb933acd.png](https://i-blog.csdnimg.cn/blog_migrate/daeac5967a2693abcbbd913f69c2236b.jpeg)
dart 封装 js 方法
处理完 Web 侧的基本能力后,需要部署 dart 转成 js 后,与 native 交互的能力。我们知道,目前 app 中 native 与 web 交互的方式主要通过 jsBridge。Native 侧我们已经有了成熟的体系主动调用和接收调用 web 的能力,这部分我们可以不需要修改,减少开发工作量。在 Flutter 侧我们需要添加交互支持,也就是需要在 native_api.js 中添加与 native 交互的能力。
JavaScript 调用 Native 的方式,主要有两种:注入 API 和 拦截 URL SCHEME,你可以根据自己的业务能力,选择合适的方式。相较于 JavaScript 调用 Native,Native 调用 JavaScript 比较简单,不管是 iOS 的 UIWebView 还是 WKWebView,还是 Android 的 WebView 组件,都以子组件的形式存在于 View/Activity 中,直接调用相应的 API 即可。
例如 iOS 中 JavaScript 调用 Native,设置 webView 的 title:
展望
整体来说,Flutter For Web 达到成熟还有一段时间。在这段时间里,尽早布局,未免不是一件好事。Flutter For Web 现阶段的使用场景并不多,不如直接使用 Web 开发便捷、稳定。但我们可以通过 Flutter,拉近与大前端的距离,感受大前端的魅力,做一个真正的大前端开发。
腾讯企鹅辅导团队会继续实践,不管是 Flutter 实现的页面,还是 Flutter 转 Web 的页面。Flutter For Web 一个复杂的场景,我们会尝试降级及热更新能力,与 Web 同学通力合作,封装 App 和 Web 的基础 API、集成 CI 等,欢迎与我们一起交流。
![03e2e699370b2654bdbd32d324a4d438.png](https://i-blog.csdnimg.cn/blog_migrate/1a288acba6320c7085517705e0b289e6.jpeg)
技术服务于业务,业务推动技术,二者并行,提升用户体验,Flutter 道路上期待大家的反馈。