Flutter 即學即用系列部落格——04 Flutter UI 初窺

前面三篇可以算是一個小小的里程碑。
主要是介紹了 Flutter 環境的搭建、如何建立 Flutter 專案以及如何在舊有 Android 專案引入 Flutter。
這一篇我們來學習下 Flutter 的 UI。
前言
說到 UI,我就簡單說下 Flutter 作為一門跨平臺語言具有的優勢之一,提高效率吧。
舉個例子:
假設現在要開發一個介面,Android 開發需要一天,iOS 開發也需要一天。那麼就是兩天。
如果你用 Flutter 開發,就只需要一天(因為 Android 和 iOS 都可以共用一套 Flutter 程式碼)。這樣效率自然就提高了。
另外,假設後面產品發現介面有個位置需要調整,如果是 Android 或者 iOS 單獨開發,則兩個端都需要進行額外調整。
而 Flutter 就一套程式碼而已,所以相較之下 Flutter 更易維護。
官網關於 UI 的介紹 User interface
這邊筆者按照自己的感受和認識進行說明。
讀者看完之後有了個基本的認識,後續不管是閱讀官方文件還是使用搜索引擎搜尋相關問題,相信會事半功倍。
記住一句話:
Flutter 裡面一切皆 Widget。
目錄

1. 基本配置
我們緊接之前文章,現在進入 MyApp/sub/my_flutter 位置。
開啟 main.dart。如果提示下圖:

Dart support is not enabled for the project
我們點選右邊第一個(Enabled Dart support)或者第二個(Open Dart settings)都是 OK 的。
如果點選第二個,需要配置 dart 的目錄。
dart 的目錄在 flutter 的 bin 目錄下面的 cache 目錄下面。
舉個例子,筆者的 flutter bin 目錄(terminal 執行 which flutter )為 /Users/nesger/Desktop/nesger_folder/flutter/flutter/bin/ ,那麼 dart 目錄就在 /Users/nesger/Desktop/nesger_folder/flutter/flutter/bin/cache/dart-sdk 。
然後直接拷貝下面程式碼替換 main.dart 的程式碼。
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('My Flutter'), ), body: Center(), ) ); } }
實際編譯器可能會顯示如下圖,就是會有註釋顯示每個控制元件。

這有好處也有壞處。好處就是你可以看到哪一塊是哪一個 Widget。壞處就是視覺干擾。
這個是自動產生的,不可刪除。
可以通過如下操作控制是否顯示:
Android Studio->Preferences->Editor->General->Appearance

2. main.dart 學習
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('My Flutter'), ), body: Center(), ) ); } }
執行 flutter run 執行後可以看到下圖:

對比上面程式碼可看到頂部藍色區域是 AppBar 這個 Widget 來控制的。
你可以自行修改 Text 裡面的內容然後按 r 鍵通過熱過載看下效果。
我們可以看到,Flutter 裡面的 dart 程式碼一個比較明顯的地方就是一個 Widget 套著一個 Widget,有點樹形的樣子。
比如這裡

我們嘗試把 AppBar 去掉,可以看到介面顯示就是一片純白的介面。
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center(), ) ); } }

那麼這裡的 MaterialApp Widget 是不是必需的呢?
其實 MaterialApp 說明這個介面是按照 Material Design 的風格。
我們看下如果去掉會怎樣?
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center(), ); } }

所以說 Scaffold 這個 Widget 不能直接返回,我們需要給它外面套一層 MaterialApp。
可以看下這個連結: https://github.com/nesger/FlutterNote/issues/4
我們返回之前的狀態,然後給他加一個 Hello World,看下怎樣?
我們知道,Flutter 一切皆 Widget,所以需要顯示 Hello World,就需要 Widget。
Widget 可以通過這個連結檢視: https://flutter.io/docs/development/ui/widgets
可以看到 Text 這個 Widget

點選進入

再點選進入,可以看到介紹以及 Sample。
大家以後如果要看其他 Widget 也可以按照同樣的方式學習。
當然如果時間要求比較緊的話,大家學完部落格可以直接在搜尋引擎輸入關鍵字看下別人的 Sample,然後化用一下就沒問題啦。

我們點選右邊複製,然後簡單修改如下:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: Text( 'Hello World!', textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: TextStyle(fontWeight: FontWeight.bold), ), ), ), ); } }
可以看到我們將其放到 Center 這個 Widget 裡面,表示居中,同時作為它的一個 child。

大家可以試下去掉 Center 會怎樣,直接將 Text 作為 body,如下:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Text( 'Hello World!', textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: TextStyle(fontWeight: FontWeight.bold), ), ), ); } }
可以想象應該是不居中的。
到了這裡你應該可以發現,我們整個頁面其實是 body 對應的 Widget 來控制的。
上面我們的例子都是 MaterialApp,是不是一定只能這個 Widget 在最外層?
不是的,只是這裡 Scaffold 跟它配對而已,我們可以修改如下:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: Text( 'Hello, world!', textDirection: TextDirection.ltr, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: TextStyle(fontWeight: FontWeight.bold), ), ); } }
注意多了一行 textDirection: TextDirection.ltr, 沒有會報錯。
3. 以 Text 為例子初窺 Widget 寫法
我們點進去 Text 原始碼看下,

對比一下上面我們的程式碼:
Text( 'Hello, world!', textDirection: TextDirection.ltr, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: TextStyle(fontWeight: FontWeight.bold), )
我們可以這樣認為,括號裡面的是待傳入引數。其中沒有花括號{}包裹的是必填項,有花括號{}的是選填項。
有花括號{}的在傳入引數時需要指定引數,格式為 引數:值 。不同引數之間逗號分隔。
所以我們可以猜測上面 Center 下面的 child 應該是在花括號裡面。所以才會有上面的寫法,我們跟進去原始碼看看。

確實跟我們猜的一樣。而且由於引數的型別是 Widget,所以可以傳 Text 也是沒問題的。
所以到這裡你再回顧一下上面是不是就知道上面程式碼的寫法了呢?
總結
由於 Flutter UI 內容比較多,講起來篇幅會比較大。
所以我們會拆分成幾篇文章進行講解。
回顧一下,本篇文章主要講解如下內容:
- dart sdk 配置和 dart 原始碼括號後面編譯器提示的顯示和隱藏。
- 通過 main.dart 的修改初步熟悉 Flutter 介面的寫法。
- 通過 Text 說明如何在官方文件上面查詢控制元件和對應 Sample。
- 通過一個具體的小控制元件 Text 初窺 Flutter Widget 的寫法和使用方法。
小彩蛋
這個彩蛋是微信群裡一個小夥伴說到的。這裡分享給大家。
簡單說就是設定 Android Studio 的背景圖。
先上圖


大家覺得哪種更加賞心悅目呢?
可以根據自己的喜好確定是否設定。
設定方法為

第一步:Android Studio->Preferences

第二步:點選 Appearance,右邊的 Background image...

第三部:輸入圖片所在位置
其中 Opacity 是不透明度。
0 表示完全透明,跟沒設定一樣。100 表示完全不透明。
一般預設即可。
背景圖公眾號回覆「ASBG」獲取。
更多閱讀:
Flutter 即學即用系列部落格——01 環境搭建Flutter 即學即用系列部落格——02 一個純 Flutter Demo 說明
Flutter 即學即用系列部落格——03 在舊有專案引入 Flutter