初識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的啓動流程
奇舞週刊

相關文章
相關標籤/搜索