Flutter隨筆(二)——使用Flutter Web + Docker + Nginx打造一個簡單的Web項目

前言

  Flutter作為一個跨平台UI框架,功能十分強大,僅用一套程式碼便能編譯出Android、iOS、Web、windows、macOS、Windows、Linux等平台上的應用,各平台應用體驗高度一致,目測前途一片光明,形勢一片大好。

  Flutter支援Android和iOS已經很長一段時間了,相信很多同學對使用Flutter開發Android和iOS應用都已經駕輕就熟了,今天我們就來體驗一把Flutter Web。目標是創建一個簡單的Flutter Web項目,然後打包部署到伺服器,通過瀏覽器進行訪問。

1.創建Flutter工程

  首先我們需要創建一個Flutter工程。我使用了Android Studio來創建工程,如下:

  上圖中需要注意的地方是Platforms,在Platforms欄中我們要選擇程式運行的平台,因為我們最終要打包出Web項目,所以我們務必勾選Web選項。
  工程創建好之後的目錄結果如下:

  除了我們熟悉的Android和iOS目錄外,還多了Web目錄,Web目錄下存放了Web項目所需要的全部文件。

2.編寫flutter程式碼

  接下來,我們編寫flutter程式碼,程式碼非常簡單,僅實現了在主頁點擊按鈕跳轉到對應子頁面的功能。
  主頁 main.dart 的程式碼如下:

import 'package:flutter/material.dart';
 
import 'ChildPage.dart';
 
void main() {
  runApp(MyApp());
}
 
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'xy_flutter',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'xy_flutter'),
    );
  }
}
 
class MyHomePage extends StatelessWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
 
  final String title;
  final List<HomeMenu> menus = [
    HomeMenu('page1', '頁面1'),
    HomeMenu('page2', '頁面2'),
    HomeMenu('page3', '頁面3'),
  ];
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: ListView.builder(
          itemCount: menus.length,
          itemBuilder: (context, index) {
            return ListTile(
              title: Text(menus[index].title),
              onTap: () {
                HomeMenu homeMenu = menus[index];
                String id = homeMenu.id;
                String title = homeMenu.title;
                if (id == 'page1') {
                  Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (context) => ChildPage(title: title)));
                } else if (id == 'page2') {
                  Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (context) => ChildPage(title: title)));
                } else if (id == 'page3') {
                  Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (context) => ChildPage(title: title)));
                }
              },
            );
          }),
    );
  }
}
 
class HomeMenu {
  String id;
  String title;
 
  HomeMenu(this.id, this.title);
}

  子頁面 child_page.dart 的程式碼如下:

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
 
class ChildPage extends StatefulWidget {
  final String title;
 
  const ChildPage({Key key, this.title}) : super(key: key);
 
  @override
  State<StatefulWidget> createState() {
    return _ChildPageState();
  }
}
 
class _ChildPageState extends State<ChildPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text(widget.title)), body: Text(widget.title));
  }
}

3.打包Web工程

  在工程根目錄下執行以下命令進行編譯打包:

flutter build web

  命令執行成功之後,編譯生成的文件被輸出到了 工程根目錄/build/web/ 目錄下,如下:

4.上傳Web工程到伺服器

  通過文件傳輸工具(例如FileZilla等)將編譯後生成的所有文件上傳到伺服器。我上傳到伺服器的路徑為 home/ubuntu/docker/xy_flutter/web/home/ubuntu/docker/xy_flutter/web/ 目錄和工程編譯出來的web目錄對應,即 home/ubuntu/docker/xy_flutter/web/ 目錄下的文件和工程編譯出來的web目錄下的文件完全一致。

5.創建Nginx Docker容器

  接下來,我們在伺服器中創建一個Nginx的Docker容器,用來運行Web項目。
  如果伺服器還沒有安裝Docker,需要先安裝Docker,關於如何安裝Docker可以參考我之前整理的資料 Docker遊記1——安裝Docker 
  如果已經安裝好了Docker,但還沒有拉取Nginx的Docker鏡像,需要先拉取一下鏡像,可以通過如下命令拉取最新的Nginx Docker鏡像:

docker pull nginx:latest

  鏡像拉取下來之後,可以通過如下命令查看當前所有已拉取下來的鏡像:

docker images

  得到的結果如下:

REPOSITORY     TAG       IMAGE ID       CREATED        SIZE
nginx          latest    87a94228f133   2 days ago     133MB

  我們可以看到已經有Nginx的鏡像了。
  接著我們利用拉取下來的鏡像創建一個容器,執行以下命令:

docker run --name xy_flutter -p 8088:80 -d nginx

  這行命令表示使用Nginx鏡像創建並持續運行一個名為xy_flutter的容器,並將伺服器的8088埠映射到容器的80埠。創建好之後,可以通過以下命令查看創建好的容器:

docker ps

  結果如下:

CONTAINER ID   IMAGE               COMMAND                  CREATED         STATUS         PORTS                                       NAMES
0c6f697a8e0c   nginx               "/docker-entrypoint.…"   5 seconds ago   Up 3 seconds   0.0.0.0:8088->80/tcp, :::8088->80/tcp       xy_flutter

  容器創建好之後,我們就可以通過瀏覽器訪問容器對應的Nginx服務了,因為我的伺服器地址IP地址是 //49.234.163.66/ ,容器映射的伺服器埠是8088,所以訪問地址是 //49.234.163.66:8088/ ,訪問後跳轉的頁面如下:

  已經看到Nginx在歡迎我們了!

6.部署Web工程

  我們通過瀏覽器訪問Nginx服務時,跳轉的頁面其實是容器中的 /usr/share/nginx/html/index.html 文件,我們可以進入容器查看。
  執行以下命令進入容器:

docker exec -it {容器ID} /bin/bash

  進入容器後,我們通過`cd`命名訪問 /usr/share/nginx/html/ 目錄,目錄中的文件如下:

  要部署Web工程,把Web工程中的所有文件複製到容器的 /usr/share/nginx/html/ 目錄就可以了,Web工程中的index.html會覆蓋 /usr/share/nginx/html/index.html 文件。我們通過如下命令把伺服器本地的文件複製到容器中:

docker cp {伺服器中Web工程目錄下的所有子文件} {容器ID}:/usr/share/nginx/html

  我的伺服器中Web工程目錄下的所有子文件是 home/ubuntu/docker/xy_flutter/web/. ,容器ID是 0c6f697a8e0c ,所以我執行的命令是:

docker cp home/ubuntu/docker/xy_flutter/web/. 0c6f697a8e0c:/usr/share/nginx/html

7.部署完成

  在上述步驟中,我們已經將Web工程拷貝到容器中的指定位置了,到這裡部署工作其實也就完成了,我們再次在瀏覽器中輸入 //49.234.163.66:8088/ 訪問,可以看到已經可以打開我們的Web工程了,如下:

  至此,我們就完成了從創建Flutter Web項目到部署至伺服器的整個流程!

最後奉上項目地址