如何在Flutter中實現任何UI

語言: CN / TW / HK

在這篇文章中,您將學習如何將任何使用者介面影象、作品或螢幕轉換為Flutter程式碼。

這不是一個關於建立一個應用程式的教程。相反,它是一個指南,將幫助您將遇到的任何使用者介面實現到您已經擁有的應用程式中。本教程還解釋了Flutter中各種各樣的UI概念。

什麼是Flutter?

Flutter是谷歌的一個開源框架,用於從一個程式碼庫中構建漂亮的、本地編譯的、多平臺的應用程式。- (來源:Flutter.dev)

在Flutter中,與大多數框架相反,Dart是你用來編碼的唯一程式語言。這是Flutter的一個未被充分強調的好處。特別是對於一個可以構建桌面、移動和網路應用的工具。

大多數UI平臺使用不止一種語言。例如,在前端Web開發中,你必須寫HTMLCSSJavaScript。對於Android,你必須寫Kotlin(或Java)和XML。但在Flutter中,只有一種語言。Dart。

再加上只有一種程式語言的好處,Flutter很簡單,因為Flutter中的所有東西都是一個widget。比如說 [AnimatedWidget](https://api.flutter.dev/flutter/widgets/AnimatedWidget-class.html), [BottomNavigationBar](https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html), [Container](https://api.flutter.dev/flutter/widgets/Container-class.html), [Drawer](https://api.flutter.dev/flutter/material/Drawer-class.html), [ElevatedButton](https://api.flutter.dev/flutter/material/ElevatedButton-class.html), [FormField](https://api.flutter.dev/flutter/widgets/FormField-class.html), [Image](https://api.flutter.dev/flutter/widgets/Image-class.html), [Opacity](https://api.flutter.dev/flutter/widgets/Opacity-class.html), [Padding](https://api.flutter.dev/flutter/widgets/Padding-class.html), ...

這也是Flutter易於使用的部分原因--它基本上是純英語。小工具的名字反映了它們是什麼,它們的屬性很容易理解。

Flutter中的小部件

widget是一個Dart,它擴充套件了StatefulWidgetStatelessWidget

您的本地Flutter安裝中帶有幾個widget。要檢視預設情況下可用的部件,請在您喜歡的編輯器中開啟您的Flutter安裝包的資料夾。然後在所有檔案中搜索 "extends StatefulWidget "和 "extends StatelessWidget",並記下結果的數量。

flutter-packages-widget-count

Flutter 2.10開始,你將得到408個有狀態的部件和272個無狀態的部件。那就是總共有680個widget可供你使用和實現UI。

這些部件通常擁有你所需要的一切。pub.dev,Dart和Flutter的包管理器,有更多的widget,你可以用來實現UI。

要數清pub.dev中的部件是很困難的。但是搜尋一個空字串(不要在搜尋欄中輸入任何東西,然後按搜尋圖示),並將SDK設定為Flutter,就會返回當前釋出的包的總數。

pub.dev-widget-count

在寫這篇文章的時候,pub.dev裡有超過23000個Flutter包。每個包都至少有一個widget。這意味著,除了可用的680個,你還有超過23000個來自pub.dev的widget可以實現。這意味著你真的可以在Flutter中輕鬆實現你想要的任何UI。

除了許多可用的部件外,您還可以在實現UI時建立自己的部件。

小部件樹

以下是您在建立一個新的Flutter專案並刪除註釋後得到的部分程式碼。

dart @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add), ), ); }

新Flutter專案的部分佔位符程式碼

父級Scaffold ,採取appBar,body, 和floatingActionButton 引數。反過來,該 [AppBar](https://api.flutter.dev/flutter/material/AppBar-class.html)也接受一個title 引數,該引數有一個Text 的值。

body 取一個 ,該值有一個 。 又有兩個 s作為 。該 採取 回撥,'增量' ,和一個 ,以獲得一個 。Center Column child Column Text children FloatingActionButton onPressed tooltip Icon child

Screenshot--142--2

Flutter部件樹分解

這是一個簡單的widget樹。它有父輩和子輩。childchildren 是大多數Flutter部件的共同屬性。隨著widget不斷吸收更多的widget子代,你的應用程式逐漸成長為一個大的widget樹。

當您在Flutter中實現UI時,請記住,您正在構建一個widget樹。你會注意到,你的程式碼從左邊緣向內縮排。它似乎在左邊形成了某種虛擬的大於號(空的空間)。

注意:巨大的縮排程度是你需要重構你的程式碼的一個標誌。這意味著您需要將一些小部件的層次結構提取到一個單獨的小部件中。

如何在Flutter中實現任何UI

1.從左上角開始寫你的程式碼,然後向下移動到右下角

您將根據每個元素在UI中的位置來實現一個又一個的UI widget。因此,您將首先為出現在UI頂部的東西編寫程式碼。然後你繼續為在頁面上向下移動的其他專案寫程式碼,直到你到達該UI的底部。

Screenshot--143-

這是很直觀的。

在橫軸上,從左到右。如果有必要,或者如果是一個從右到左的使用者介面,那麼就從右到左來實現它。

2.選擇一個小部件

接下來,你需要從邏輯上確定你要為一個給定的UI元素使用的小部件。最低限度,對於一個給定的UI元素,你將使用你所熟悉的簡單的小部件,根據它們的名字來決定它們的作用。

有可能UI元件的名稱就是小部件的名稱。如果你覺得很難做出選擇,快速的線上搜尋會給你答案。Flutter有一個很好的線上社群。

3.使用widget組

如果一組UI專案是垂直排列的,一個接一個,則使用一個 [Column](https://api.flutter.dev/flutter/widgets/Column-class.html).如果它們是水平排列的,一個接一個,使用a [Row](https://api.flutter.dev/flutter/widgets/Row-class.html).如果它們被放置在彼此的頂部,使用a [Stack](https://api.flutter.dev/flutter/widgets/Stack-class.html),浮動的widgets被包裹在 [Positioned](https://api.flutter.dev/flutter/widgets/Positioned-class.html)小部件。

a.列/行

ColumnRow ,你可以改變或調整小部件如何在主軸或橫軸上對齊。使用它們的 [CrossAxisAlignment](https://api.flutter.dev/flutter/rendering/CrossAxisAlignment.html)[MainAxisAlignment](https://api.flutter.dev/flutter/rendering/MainAxisAlignment.html)屬性來進行這種調整。

對於橫軸,你可以對準中心,結束,開始,和伸展。對於主軸,你可以對準中心、末端、周圍的空間、中間的空間、均勻的空間和末端。

Column ,垂直軸是主軸,而水平軸是交叉軸。在Row ,水平軸是主軸,而垂直軸是交叉軸。

1Untitled-1-1

改編自https://arzerin.com/2019/11/20/flutter-column/

Untitled-1

改編自https://arzerin.com/2019/11/20/flutter-row/

ColumnRow中,如果你想讓一個特定的子部件儘可能多地佔用可用空間,可以將該部件包裹在一個 [Expanded](https://api.flutter.dev/flutter/widgets/Expanded-class.html)小元件內。如果你熟悉web前端,你會注意到Columns和Rows就像 [display: flex;](https://developer.mozilla.org/en-US/docs/Web/CSS/flex)在CSS中。

b.堆疊小部件

通過Stackchildren's list中的最後一個widget(s)出現在前面的子項之上。

你可能需要編輯Stack的 [alignment](https://api.flutter.dev/flutter/widgets/Stack/alignment.html)來表示小部件的相對位置。比如 [topCenter](https://api.flutter.dev/flutter/painting/AlignmentDirectional/topCenter-constant.html), [center](https://api.flutter.dev/flutter/painting/AlignmentDirectional/center-constant.html), [bottomEnd](https://api.flutter.dev/flutter/painting/AlignmentDirectional/bottomEnd-constant.html),等等。

Stack'的大小是基於非定位的小部件計算的(子列表中的小部件沒有被包裹在一個 [Positioned](https://api.flutter.dev/flutter/widgets/Positioned-class.html)父級)。在編碼時,記住你的Stack 應該至少有一個非定位的widget,或者它應該被包裹在一個明確設定了Stack'size的父widget中。

Positioned 採取任何或全部的 [bottom](https://api.flutter.dev/flutter/widgets/Positioned/bottom.html), [top](https://api.flutter.dev/flutter/widgets/Positioned/top.html), [left](https://api.flutter.dev/flutter/widgets/Positioned/left.html), [right](https://api.flutter.dev/flutter/widgets/Positioned/right.html).它們設定子代相對於Stack 的位置。負值使子代向相反方向移動。然而,負值會把孩子的一部分剪掉。使用 [clipBehavior: Clip.none](https://api.flutter.dev/flutter/widgets/Stack/clipBehavior.html)Stack ,以顯示定位小元件的所有部分。

Screenshot--148-

完整的程式碼在這裡

4.建立自定義小部件

當你建立小部件樹時,你會注意到兩件事。

  1. 要麼樹上的一個塊增長得太大了,它是一個獨立的邏輯單元。
  2. 或者一些小工具的塊或集可能會在輕微的變化下重複。

這兩個跡象表明你應該重構你的程式碼。這意味著你應該把這些部件提取出來,在另一個Dart檔案中定義它們。

你的程式碼編輯器會幫助你進行重構。不管有沒有編輯器,你所需要做的就是。

  1. 建立一個新的Dart檔案。檔名應該反映新的部件的名稱。
  2. 建立一個擴充套件StatefulWidget或StatelessWidget的新類,這取決於新widget是否有狀態
  3. 然後從一個方法中返回widget chunk [build](https://api.flutter.dev/flutter/widgets/StatelessWidget/build.html)方法返回widget chunk。
  4. (可選)如果需要的話,你的新Dart類可以在其建構函式中加入位置引數或命名引數,以自定義widget的外觀。

```dart // in counter_display.dart import 'package:flutter/material.dart';

class CounterDisplay extends StatelessWidget { @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('You have pushed the button this many times:'), Text('$counter', style: TextStyle(fontSize: 24)), ], ); } }

// in main.dart // // ... body: Center(child: CounterDisplay()), // ... ```

你會建立許多自定義部件,它們又會成為更多自定義部件的後代,這很好。小元件樹是為了在需要時持續增長。

5.增加更多的自定義功能

你不會僅僅因為重構和重複(DRY程式碼)而定製widget。你會因為你正在實現的使用者介面而建立自定義widget。

你將建立自定義widget,因為許多可用的widget並不總是能滿足特定UI的確切需求。你需要以某種特殊的方式組合它們來實現一個特定的使用者介面。

a.容器小部件

[Container](https://api.flutter.dev/flutter/widgets/Container-class.html)是一個強大的小元件。你可以用不同的方式設計它。如果你習慣於Web前端,你會發現它就像一個 [div](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div)在HTML中。

Container 是一個基礎部件。你可以用它來建立任何UI作品。

一些Container 引數是 [constraints](https://api.flutter.dev/flutter/widgets/Container/constraints.html), [decoration](https://api.flutter.dev/flutter/widgets/Container/decoration.html), [margin](https://api.flutter.dev/flutter/widgets/Container/margin.html), [padding](https://api.flutter.dev/flutter/widgets/Container/padding.html), [transform](https://api.flutter.dev/flutter/widgets/Container/transform.html),以及其他一些引數。當然,Container 需要一個 [child](https://api.flutter.dev/flutter/widgets/Container/child.html)它可以是任何 widget。

decoration 屬性可以接受一個 [BoxDecoration](https://api.flutter.dev/flutter/painting/BoxDecoration-class.html),而後者又可以接受其他幾個屬性。這就是Container 的靈活性的核心。BoxDecoration 需要的引數包括 [border](https://api.flutter.dev/flutter/painting/BoxDecoration/border.html), [borderRadius](https://api.flutter.dev/flutter/painting/BoxDecoration/borderRadius.html), [boxShadow](https://api.flutter.dev/flutter/painting/BoxDecoration/boxShadow.html), [color](https://api.flutter.dev/flutter/painting/BoxDecoration/color.html), [gradient](https://api.flutter.dev/flutter/painting/BoxDecoration/gradient.html), [image](https://api.flutter.dev/flutter/painting/BoxDecoration/image.html), [shape](https://api.flutter.dev/flutter/painting/BoxDecoration/shape.html),等等。

通過這些引數和它們的值,你可以按照你的口味實現任何UI。您可以使用Container ,而不是Flutter自帶的許多材料部件。這樣你的應用程式就能符合你的口味。

b.GestureDetector / InkWell

[GestureDetector](https://api.flutter.dev/flutter/widgets/GestureDetector-class.html)顧名思義就是檢測使用者的互動。不是每一個使用者介面都是一個按鈕。而在實現UI時,你將需要一些小部件來對使用者的行為做出反應。在這種情況下,使用GestureDetector

GestureDetector 可以檢測不同型別的手勢:輕拍、雙擊、輕掃...... 當然需要一個GestureDetector [child](https://api.flutter.dev/flutter/widgets/GestureDetector/child.html)(可以是任何widget),以及針對不同手勢的不同回撥,如 [onTap](https://api.flutter.dev/flutter/widgets/GestureDetector/onTap.html), [onDoubleTap](https://api.flutter.dev/flutter/widgets/GestureDetector/onDoubleTap.html), [onPanUpdate](https://api.flutter.dev/flutter/widgets/GestureDetector/onDoubleTap.html)(用於滑動), ...

注意: child 預設情況下,當用戶與GestureDetectors的空位互動時,回撥不會被呼叫。如果你想讓你的GestureDetector 對空位上的手勢做出反應(在它的child ),那麼就把 [behavior](https://api.flutter.dev/flutter/widgets/GestureDetector/behavior.html)``GestureDetector 的屬性為 [HitTestBehavior.translucent](https://api.flutter.dev/flutter/rendering/HitTestBehavior.html).

dart GestureDetector( // set behavior to detect taps on empty spaces behavior: HitTestBehavior.translucent, child: Column( children: [ Text('I have space after me ...'), SizedBox(height: 32), Text('... that can detect taps.'), ], ), onTap: () => print('Tapped on empty space.'), )

GestureDetector的用法,表明HitTestBehavior.translucent行為。

[InkWell](https://api.flutter.dev/flutter/material/InkWell-class.html)它類似於GestureDetector 。它對一些 GestureDetector 響應的手勢做出反應。然而,當與之互動時,它顯示出波紋效應(GestureDetector's不會)。

QqEZ3

From https://stackoverflow.com/q/58285012/13644299

InkWell 必須有一個 [Material](https://api.flutter.dev/flutter/material/Material-class.html)祖先。所以,如果你最上面的部件是 [MaterialApp](https://api.flutter.dev/flutter/material/MaterialApp-class.html)你不需要擔心。否則,把InkWell 包在一個Material

如果你要改變InkWell's parent orchild 的顏色,你也應該做這個包裝。如果你不這樣做,波紋就不會顯示。你還必須設定 [color](https://api.flutter.dev/flutter/material/Material/color.html)``Material widget的顏色來顯示波紋。你可以將color 設定為 [Colors.transparent](https://api.flutter.dev/flutter/material/Colors/transparent-constant.html)然後Flutter會處理剩下的事情。

如何實現滾動介面

滾動是一個有點微妙的話題。預設情況下,小元件在Flutter中不會滾動。如果您的ColumnRow 將會是可滾動的,請使用 [ListView](https://api.flutter.dev/flutter/widgets/ListView-class.html)代替。ListView ,也需要children 引數。

ListView 也有工廠建構函式,如 [ListView.builder](https://api.flutter.dev/flutter/widgets/ListView/ListView.builder.html)[ListView.separated](https://api.flutter.dev/flutter/widgets/ListView/ListView.separated.html).builder 可以讓你更多地控制子節點的構建過程,而separated 則考慮到了分離器 (如 [Divider](https://api.flutter.dev/flutter/material/Divider-class.html)為例)。

預設情況下,ListView,垂直滾動其子代。然而,你可以改變 [scrollDirection](https://api.flutter.dev/flutter/widgets/ScrollView/scrollDirection.html)改為ListView[Axis.horizontal](https://api.flutter.dev/flutter/painting/Axis.html)來水平滾動其子代。

有時,你可能想使用 [SingleChildScrollView](https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html)而不是ListView 。顧名思義,它只需要一個 [child](https://api.flutter.dev/flutter/widgets/SingleChildScrollView/child.html)並且它可以滾動。你可以傳遞widget組作為它的child

還有其他的滾動小工具

但要特別注意的是 [CustomScrollView](https://api.flutter.dev/flutter/widgets/CustomScrollView-class.html).它給了你巨大的滾動控制權,與其他的不同。它需要 [slivers](https://api.flutter.dev/flutter/widgets/CustomScrollView/slivers.html),這又是具有強大滾動機制的滾動小部件。

[SliverFillRemaining](https://api.flutter.dev/flutter/widgets/SliverFillRemaining-class.html), [SliverFillViewport](https://api.flutter.dev/flutter/widgets/SliverFillViewport-class.html), [SliverGrid](https://api.flutter.dev/flutter/widgets/SliverGrid-class.html), [SliverList](https://api.flutter.dev/flutter/widgets/SliverList-class.html), [SliverPersistentHeader](https://api.flutter.dev/flutter/widgets/SliverPersistentHeader-class.html)以及其他一些,都是你在 slivers列表中包含的小部件的例子 這些小部件大多數都有一個委託,用來處理如何發生滾動。

一個使用CustomScrollView 的好例子是 [SliverAppBar](https://api.flutter.dev/flutter/material/SliverAppBar-class.html),在那裡你希望AppBar在預設情況下被展開,在滾動時被縮小。

ap

另一個例子是用一個 [DraggableScrollableSheet](https://api.flutter.dev/flutter/widgets/DraggableScrollableSheet-class.html)的情況下,你會把一些動作按鈕貼在底部。

bs

關於CustomPaint

這是Flutter給UI世界帶來終極靈活性的地方。

[CustomPaint](https://api.flutter.dev/flutter/widgets/CustomPaint-class.html)是Flutter中的Canvas API對HTML或SVG對影象的作用。

CustomPaint 是Flutter中的一個小部件,它讓你能夠不受限制地設計和繪畫。它為您提供了一個畫布,您可以在上面用 [painter](https://api.flutter.dev/flutter/widgets/CustomPaint/painter.html).

visualizer

來自https://blog.codemagic.io/flutter-custom-painter/

你很少會用到CustomPaint。但要知道它的存在。因為可能會有非常複雜的UI,widget組合可能無法實現它們,你除了用CustomPaint

當那個時候到來的時候,對你來說並不困難,因為你已經熟悉了其他部件。

總結

對於一個給定的UI作品,選擇一個widget,編寫它的程式碼,用其他widget構建這個widget,看看你用Flutter實現了什麼偉大的UI。

實現UI是移動、網路和桌面應用開發的一個主要部分。Flutter是一個UI工具包,為這些平臺構建跨平臺的UI。Flutter的宣告性和其豐富的部件使UI實現變得簡單。

繼續在Flutter中實現UI。當您這樣做時,它將成為您的第二天性。而您將能夠在Flutter中實現任何UI。