初识Flutter web

级别:★☆☆☆☆
标签:「Flutter web」「Dart Server」「blocked by CORS Policy」「跨域」
做者: WYW
审校: QiShare团队
php


前言 笔者最近了解了Flutter web相关的内容,本文会分享建立Flutter web项目、Flutter web项目预览,Flutter web项目和Flutter mobile(Flutter Android/iOS)项目的差异、搭建简易Dart服务器(解决跨域问题)、上线Flutter web项目相关内容。html

1、建立Flutter web 项目

准备Flutter web 环境

更新本地环境为 beta channel最新版。(dev channel 也能够)

flutter channel betanginx

flutter upgradegit

Flutter有以下4个channel:github

flutter channel
Flutter channels:
  beta
* dev
  master
  stable
复制代码

Flutter 官方建议使用 stable 的channel。web

master 是当前最新的channel;chrome

dev 是当前最新的充分测试后的channel;api

beta是每月Flutter官方调整选出来的最好的dev的channel,并提高为beta channel;跨域

stable是Flutter 认为是当前最稳定的channel。浏览器

稳定性而言:master < dev < beta < stable 。更多内容可查看:Flutter build release channels

开启项目支持Flutter web

flutter config --enable-web

若是想在当前已有项目Flutter mobile项目的基础上,添加Flutter web支持,可 cd 到 Flutter mobile 项目目录下,添加Flutter web支持。

新建Flutter web项目

若是以前没有建立过Flutter 项目,新建一个Flutter web项目可使用以下命令。

flutter create 项目名(小写) 如:flutter create qi_flutter_web_demo

现有项目生成Flutter web 相关文件

若是以前建立过Flutter 项目,想现有项目生成web文件夹及index.html等文件可以使用以下命令。

flutter create .

Flutter web 新增index.html 等

运行项目命令:flutter run -d chrome

遇到问题:运行失败

运行失败报错以下:

wangyongwangdeiMac:qi_flutter_page wangyongwang$ flutter run -d chrome

Flutter assets will be downloaded from storage.flutter-io.cn. Make sure you trust

this source!

Downloading Web SDK... 1.1s

Launching lib/main.dart on Chrome in debug mode...

Error compiling dartdevc module:qi_flutter_page|lib/main_web_entrypoint.ddc.js

packages/qi_flutter_page/main_web_entrypoint.dart:9:18: Error: Too few positional arguments:

1 required, 0 given.

entrypoint.main();

^                                                             
复制代码

AssetNotFoundException: qi_flutter_page|lib/main_web_entrypoint.ddc.js

Failed after 23.3s

Building application for the web... 33.5s

Failed to build application for the Web.

猜想缘由:访问网址https://storage.flutter-io.cn.不可达

起初,笔者猜想缘由是这个网址https://storage.flutter-io.cn.访问不可达;不过试过运行新建立的Flutter web项目,发现新建的Flutter web项目能够正常运行,能够排除问题不在于网址https://storage.flutter-io.cn.访问不可达。

Document not found

继续看这段报错,能够发现Flutter web 项目的main 方法中不能有参数。

packages/qi_flutter_page/main_web_entrypoint.dart:9:18: Error: Too few positional arguments: 1 required, 0 given. entrypoint.main(); ^

AssetNotFoundException: qi_flutter_page|lib/main_web_entrypoint.ddc.js
Failed after 22.9s

问题在于main方法中参数

运行Flutter web 项目的时候,main方法中不能有参数。

void main(List<String> args) {

}

// 删除main方法名中的参数后,能够正常运行。

void main() {

}
复制代码

笔者以以前写的项目qi_flutter_page为例:运行起来的效果以下:

Flutter web 项目预览

Flutter web 项目预览

上周和同事CH聊天学到的内容:Flutter web项目显示的网页的特色:

显示网页源代码的时候,能够网页发现显示的内容是html的body 中嵌套的main.dart.js。

显示页面源文件

页面源文件

2、Flutter web 项目预览

运行Flutter web项目 默认会在Chrome浏览器中显示,不过在本机的Safari 浏览器中及模拟器中的浏览器中输入相应的网址,也能够显示相应的视图。

Flutter web 项目预览

3、Flutter web项目 与 Flutter mobile 项目的不一样

笔者在把现有Flutter mobile项目,直接支持Flutter web 的过程当中遇到了网络请求报异常的问题,另外简单测试了2个三方库的在Flutter web项目中的体现。

HttpClient() 不能用于Flutter web 项目

try {
  HttpClient client = HttpClient();
} catch (e) {
  print('捕获异常:$e');
}
复制代码

捕获异常:NoSuchMethodError: invalid member on null: 'indexOf'

Flutter web项目的网络请求可使用html.httpRequest。

import 'dart:html' as html;

html.HttpRequest.request(url).then((responseValue) {
     
 });
复制代码

三方库支持状况

笔者这里举2个本身使用过三方库,shared_preferences、url_launcher。

下列代码对于Flutter web 项目中仍然支持打开加载url的窗口。

String soUrl = 'https://www.so.com';
if (await canLaunch(soUrl)) {
  await launch(soUrl);
}
复制代码

由以下代码及相应结果可知,shared_preferences 也支持 Flutter Web 项目。

void _incrementCounter() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    int counter = (prefs.getInt('counter') ?? 0) + 1;
    print('Pressed $counter times.');
    await prefs.setInt('counter', counter);
  }
复制代码

ListTile2 Pressed 1 times. ListTile2 Pressed 2 times. ListTile2 Pressed 3 times. ListTile2 Pressed 4 times. ListTile2 Pressed 5 times.

三方库通常会注明支持的平台(Android、iOS或Web)。 url_launcher 5.4.1支持 Flutter web 项目。 shared_preferences 支持 Flutter web 项目。 sqflite本身注明了支持Android和iOS,是否支持Flutter web没有作说明。

以下图所示:

url_launcher

shared_preferences

sqflite

4、简易Dart服务器

使用以下代码,能够本地启动一个Dart服务。

main() async {
  var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 9988);
  await for (var request in server) {
    request.response
      ..headers.contentType = ContentType('text', 'plain', charset: 'utf-8')
      ..write('Hello Dart! 你好Dart')
      ..close();
  }
 }
复制代码

浏览器中直接请求http://127.0.0.1:9988 示意图以下:

简易Dart 服务器示意

笔者在Flutter web项目中请求,http://127.0.0.1:9988的时候,遇到了跨域问题,下边分享下相关问题及处理方式。

跨域问题

跨域问题描述

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不一样源服务器上的指定的资源。当一个资源从与该资源自己所在的服务器不一样的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求

好比,站点 domain-a.com 的某 HTML 页面经过 的 src 请求 domain-b.com/image.jpg。网…

出于安全缘由,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。 引自HTTP访问控制(CORS)

举2个例子好比咱们自身当前域名为abc.com, 访问def.com 会出现跨域的问题。 好比咱们自身当前域名为abc.com端口号为1234(abc.com:1234),那么访问abc.com:5678也会出现跨域问题。

笔者使用Flutter web项目请求服务端资源的时候遇到了跨域问题。

http://localhost:55355/#/ 中的内容访问http://127.0.0.1:9988

Flutter web项目跨域现象图现象图以下:

Flutter web项目跨域问题

出现当前跨域问题的缘由是端口号不一样,访问Flutter web项目的url 和 请求服务端资源的url的 端口号 不一样。

请求的响应头中设置可跨域的origin,解决跨域问题

设置跨域的url 有2种设置方式:

  • 1.设置一个或多个url;
    • 如:request.response ..headers .add('Access-Control-Allow-Origin', request.headers['origin'])
  • 2.设置跨域的值为*;
    • ..headers.add('Access-Control-Allow-Origin', '*')
      复制代码

注意:若是在本地测试使用,可使用第二种方式,直接了当。可是通常线上的话最好使用第一种方式设置是否能够跨域请求。由于设置是否能够跨域,算是服务器在响应浏览器请求数据时的一种保护策略。

其中重点是在响应头中添加能够跨域的请求域。

..headers.add('Access-Control-Allow-Origin', 'http://localhost:55355')

如'Access-Control-Allow-Origin'能够指定特定的url,使url可以跨域请求。

若有须要指定容许多个url进行跨域请求。能够根据请求的origin的值,判断是否要作跨域响应头的处理。

如:以下代码设置了当请求的origin 为http://localhost:63062http://localhost:55355 的时候,会添加跨域处理的响应头。

main() async {
  var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 9988);

  var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 9988);
  await for (var request in server) {
    var accessControlAllowOrigin = [
      'http://localhost:63062',
      'http://localhost:55355'
    ];
    if (request.headers['origin'] != null) {
      for (String tempAllowOrigin in accessControlAllowOrigin) {
        if (request.headers['origin'].first.contains(tempAllowOrigin)) {
          request.response
            ..headers
                .add('Access-Control-Allow-Origin', request.headers['origin'])
            // ..headers.add('Access-Control-Allow-Origin', '*')
            ..headers.contentType =
                ContentType('text', 'plain', charset: 'utf-8')
            ..write('Hello Dart! 你好Dart 跨域')
            ..close();
        }
      }
    } else {
      request.response
        ..headers.contentType = ContentType('text', 'plain', charset: 'utf-8')
        ..write('Hello Dart! 你好Dart 不须要跨域')
        ..close();
    }
  }
 }
复制代码

解决跨域问题

笔者在上边说明了处理运行Flutter web项目的时候,处理本地服务端接口和Flutter web项目运行网址出现跨域问题的处理方式。(其实对于编译后的Flutter web项目的产物直接放到本身的服务端项目的的静态文件目录下的时候,不会出现上述问题) 有时候咱们的请求内容可能就须要跨域去请求数据,并且对方若是也不便添加相应的跨域响应头。此时,可以使用Nginx 作反向代理来处理跨域问题。

Nginx 反向代理解决远端跨域问题
server {
        listen       9080;
        server_name  localhost;
        
        location ~ /columns/Qtest {
            proxy_pass https://testerhome.com;
        }
    }
复制代码

经上述处理,能够在本地的127.0.0.1:9080/columns/Qtest请求到 Qtest测试之道testerhome.com/columns/Qte… 相应数据。

本地Flutter web 项目跨域访问TesterHome Qtest测试之道

Nginx 配置反向代理及rewrite访问路径可实现访问远端文件不跨域。

location / {
           proxy_pass https://weekly.75team.com;
        }
        
        location ~ /api/qiwuzhoukanWeb {
            rewrite /api/qiwuzhoukanWeb /;
            proxy_pass https://weekly.75team.com;
        }
复制代码

经上述处理,能够在本地的127.0.0.1:9080/api/qiwuzhoukanWeb请求到 奇舞周刊weekly.75team.com 相应数据。

本地Flutter web 项目跨域访问奇舞周刊

5、Flutter web 项目上线

flutter build web会在项目的build 目录中生成相应的资源文件及html 和js文件,把相关文件放置到服务端静态文件目录下便可。实现上线Flutter Web项目。

Flutter web项目编译产物

参考学习网址


了解更多iOS及相关新技术,请关注咱们的公众号:

小编微信:可加并拉入《QiShare技术交流群》。

关注咱们的途径有:
QiShare(简书)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公众号)

推荐文章:
用AdHoc来测试iOS线上推送 Swift 5.1 (9) - 结构体和类
Swift 实现一个兼容iOS、tvOS、OSX的抽象层
iOS Password AutoFill
iOS 给UILabel添加点击事件
用SwiftUI给视图添加动画
用SwiftUI写一个简单页面
Swift 5.1 (8) - 枚举类型 iOS App启动优化(三)—— 本身作一个工具监控App的启动耗时
iOS App启动优化(二)—— 使用“Time Profiler”工具监控App的启动耗时
iOS App启动优化(一)—— 了解App的启动流程
奇舞周刊