翻桌吧!Flutter for Desktop

img

前言

自從 Google I/O 公開 Flutter for Web 之後大家便開始瘋狂測試,似乎都忘了它目前只是預覽版本,接下來大家另一個期待便是 Flutter 什麼時候開始支援桌面系統,今天剛好看到一篇令人心動的文章-Flutter for Desktop: Create and Run a Desktop Application,這篇文章使用的是 Google 之前在 GitHub 上釋出的試驗性專案 flutter-desktop-embedding,這也意味著目前官方的進度,接下來當然實機測試看看了。

環境

首先我們先下載 flutter-desktop-embedding 專案,可以直接下載壓縮檔或是透過 Git 指令下載。
git clone https://github.com/google/flutter-desktop-embedding.git
img
開啟專案內的 README.md 可以看到各個開發需要的安裝的工具。
img

Tools

You will need developer tools for your platform:

  • Linux: A recent version of GCC

  • macOS: The current version of Xcode

  • Windows: Visual Studio 2017 or 2019, including the “Desktop development with C++” workload.

因為筆者是在 Windows 上所以需要安裝 Visual Studio 2017 以上的版本,特別要注意的是需要安裝 **使用 C++ 的桌面開發(Desktop development with C++)**。
img

若未安裝 **使用 C++ 的桌面開發(Desktop development with C++)**,執行時會出現找不到 vcvars64.bat 的錯誤。
img

Flutter SDK

接下來我們開啟專案內的範例程式 flutter-desktop-embedding\example\,從 pubspec.yaml 內可以看到執行環境需要 Flutter SDK 1.5.9-pre.270 以上版本。
img

如果沒有修改,在下載 packages 時也會提醒。
img

如果版本太低可透過指令 flutter upgrade 來升級 SDK 版本,透過 flutter --version 可以查看目前的版本資訊,筆者目前版本為 1.6.1-pre.41
img
img

環境變數 ENABLE_FLUTTER_DESKTOP

開啟範例程式的 README.md(flutter-desktop-embedding\example\README.md) 可以知道還需要新增一個環境變數 ENABLE_FLUTTER_DESKTOP,並將值設為 true

README.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

### Enable Desktop Support

The desktop support in the `flutter` tool is still highly experimental, and
must be enabled with an environment variable. Run the command below in the
terminal/console you will be using to build and run the example.

On macOS or Linux:

1
export ENABLE_FLUTTER_DESKTOP=true


On Windows:

* PowerShell:
1
$env:ENABLE_FLUTTER_DESKTOP="true"

* CMD:

1
set ENABLE_FLUTTER_DESKTOP=true

img
透過指令 flutter devices,可以驗證是否連結成功。
img

執行

接下來透過開啟終端機並切換到範例程式的路徑 flutter-desktop-embedding\example,最後透過指令 flutter run 來執行,效果如下:
img
開啟 main.dart 可以看到其實與原本我們自己建立的專案幾乎一樣,比較明顯的是在呼叫 runApp 之前特別將執行的環境設為 fuchsia

main.dart
1
2
3
4
5
6
void main() {
// See https://github.com/flutter/flutter/wiki/Desktop-shells#target-platform-override
debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;

runApp(new MyApp());
}

這主要是因為目前 TargetPlatform 只有 androidfuchsiaiOS 3種,目前桌面系統均無法識別,為了避免發生錯誤,所以暫時設定為 fuchsia,這樣也可避免將 Package 與 Android 或 iOS 相關的部分一起打包進來。
img

範例二

與之前測試 Flutter for Web 時一樣,我們套用上一篇文章 Flutter 當我們黏在一起:Provider 的範例。
我們用開發工具開啟範例專案,筆者是用 Visual Studio Code,並在 pubspec.yaml 內加入 provider 這個 package,儲存後透過 flutter package get 來下載(VS Code 儲存 pubspec.yaml 時會自動執行指令)。
img
接著將 rx.dart 複製到 lib 資料夾下。
img
最後將 main.dartMaterialApphome 改為 RxPage
img
在透過指令 flutter run 重新執行,可以看到操作效果與 App 以及 Web 一樣。
img
完整程式碼如下:

main.dart
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import 'dart:io' show Platform;

import 'package:flutter/foundation.dart' show debugDefaultTargetPlatformOverride;
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:example_flutter/rx.dart';

final controller = StreamController<bool>();

void main() {
// See https://github.com/flutter/flutter/wiki/Desktop-shells#target-platform-override
debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;

runApp(new MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
// See https://github.com/flutter/flutter/wiki/Desktop-shells#fonts
fontFamily: 'Roboto',
),
// home: MyHomePage(title: 'Flutter Demo Home Page'),
home: StreamProvider.value(
initialData: false,
stream: controller.stream,
child: RxPage(),
),
);
}
}

class RxPage extends StatefulWidget {
@override
_RxPageState createState() => _RxPageState();
}

class _RxPageState extends State<RxPage> {

@override
Widget build(BuildContext context) {
List<Widget> list = <Widget>[];
bool isLogin = Provider.of<bool>(context);
if (isLogin) {
list.add(UserAccountsDrawerHeader(
accountEmail: Text("jonnyhuang@outlooj.com"),
accountName: Text("Jonny"),
currentAccountPicture: CircleAvatar(
child: Text("J"),
),
));
} else {
list.add(DrawerHeader(
decoration: BoxDecoration(
color: Colors.orange,
),
child: Text("Guest"),
));
}
list.add(ListTile(
title: Text(isLogin ? "登出" : "登入"),
trailing: Icon(Icons.exit_to_app),
onTap: () {
controller.add(!isLogin);
},
));

return Scaffold(
appBar: AppBar(
title: Text('RxDart'),
),
drawer: Drawer(
child: ListView(
children: list,
),
),
body: Page1Page(),
);
}
}

class Page1Page extends StatefulWidget {
@override
_Page1PageState createState() => _Page1PageState();
}

class _Page1PageState extends State<Page1Page> {

@override
Widget build(BuildContext context) {
bool isLogin = Provider.of<bool>(context);
return Scaffold(
body: Container(
color: isLogin ? Colors.green : Colors.grey,
alignment: Alignment.center,
child: MaterialButton(
child: Text(isLogin ? "登出" : "登入"),
onPressed: () {
controller.add(!isLogin);
},
),
),
);
}
}

後記

比起 Flutter for Web 的進度似乎還有點落後,但是我們可以看到 Flutter 移植到其他平台時修改幅度並不大,可以看的出來 Flutter 團隊不只是想讓 Flutter 只是可以在各種平台上執行而已,減少重工降低維護成本在實務應用上更加重要,這不論是對於公司還是個人來說應該都是很大的導入誘因。