Flutter基本組件Basics Widget學習

1. 概述

上一篇說到,Basics Widget 並不是 Flutter 的一個專門的Widget類別,而是 Flutter 官方挑選一些開發常用的 Widget 構成的,希望我們掌握到一些最基本的開發能力。

包括:

  • 文本 Text
  • 按鈕 Button
  • 圖片 Image
  • 單選框、復選框
  • 輸入框、表單
  • 指示器
  • Container

2. 常用組件

2.1 Text

Text 用於顯示簡單樣式文本,然後可以填充一些文本顯示樣式的屬性,如下例子:

Text("Hello World",
        textAlign: TextAlign.left,
        maxLines: 1,
        overflow: TextOverflow.ellipsis,
        textScaleFactor: 1.5);
  • textAlign
    文本對齊方式
  • maxLinesoverflow
    maxLines 指定文本顯示的最大行數。
    當文本內容超過最大行數時, overflow 指定瞭階段方式, 例如 ellipsis 就是將多餘的文本用 “…” 表示
  • textScaleFactor
    代表文本相對於當前字體大小的縮放因子,想你對於去設置文本的樣式 style 屬性的 fontSize, 它是調整字體大小的一個快捷方式, 該屬性的默認值可以通過 MediaQueryData.textScaleFactor 獲得, 如果沒有 MediaQuery,那麼會默認值為 1.0

2.1.1 TextStyle

TextStyle 用於指定文本樣式,例如顏色、字體、粗細、背景等,如下:

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: "Flutter",
        home: Scaffold(
            appBar: AppBar(
              title: const Text("Basics Widget"),
            ),
            body: Text(
              "Hello World",
              style: TextStyle(
                  color: Colors.blue,
                  fontSize: 19.0,
                  height: 2,
                  fontFamily: "Courier",
                  background: Paint()..color = Colors.yellow,
                  decoration: TextDecoration.underline,
                  decorationStyle: TextDecorationStyle.dashed),
            )));
  }

效果如圖:

一些屬性:

  • height
    行高,它不是一個絕定的值,因為具體的行高為 height*fontSize ,同理行寬也是
  • fontFamily
    由於不同平臺默認支持的字體集不同,所以在手動指定字體時一定要先在不同平臺測試一下
  • fontSize
    改屬性和 Text 的 textScaleFactor 都用於控制字體大小,但是有兩個區別,
    ①:fontSize 可以精確指定字體大小, 而 textScaleFactor 隻能縮放比例
    ②: textScaleFactor 主要是用於系統字體大小設置改變時,對Flutter 應用字體進行全局調整,而 fontSzie通常用於單個文本,字體大小不會跟隨系統字體大小變化

2.1.2 TextSpan

如果我們需要對Text內容不同部分按照不同的樣式顯示,就可以使用 TextSpan,代表文本的一個“片段”,看看 TextSpan的定義:

  const TextSpan({
    this.text,
    this.children,
    TextStyle? style,
    this.recognizer,
    MouseCursor? mouseCursor,
    this.onEnter,
    this.onExit,
    this.semanticsLabel,
    this.locale,
    this.spellOut,
  })

其中 styletext 代表樣式和文本內容, children是 List<InlineSpan>? 類型,也就說 TextSpan 可以包含其他 Span

reconizer 用於表示該文本片段上用於手勢進行識別處理,下面我們看一個效果圖,然後用 TextSpan 來實現:

body: const Text.rich(TextSpan(children: [
              TextSpan(text: "Home: "),
              TextSpan(
                text: "https://flutterchina.club",
                style: TextStyle(color: Colors.blue),
                recognizer: _recognizer
              ),
            ]))));

這裡的代碼,用 TextSpan實現瞭一個基礎文本和一個鏈接片段

  • Text.rich 方法將 TextSpan 添加到 Text 中,之所以可以這樣做,是因為 Text 其實就是 RichText 的一個包裝,而 RichText 是可以顯示多種多樣的 widget
  • _reconizer 是點擊鏈接的處理器

2.1.3 DefaultTextStyle

在 Widget 樹中, 文本的樣式默認是可以被繼承的,因此如果 Widget樹的某一個節點處設置一個默認的文本樣式,那麼該節點的子樹所有的文本都會默認使用這個樣式,而 DefaultTextStyle 正是用於設置默認文本樣式的,看下面例子:

DefaultTextStyle(
  //1.設置文本默認樣式  
  style: TextStyle(
    color:Colors.red,
    fontSize: 20.0,
  ),
  textAlign: TextAlign.start,
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: <Widget>[
      Text("hello world"),
      Text("I am Jack"),
      Text("I am Jack",
        style: TextStyle(
          inherit: false, //2.不繼承默認樣式
          color: Colors.grey
        ),
      ),
    ],
  ),
);

這裡的代碼首先設置瞭一個默認的樣式,字體大小為20,、顏色為紅色,然後將 DefaultTextStyle 設置給瞭子樹,這樣一來 Column 所有子孫 Text 默認都會繼承該樣式, 除非 Text 設置 inherit: false,如下所示:

2.1.4 使用字體

在 Flutter 中可以使用自定義的字體,或者其他第三方字體, 這裡就不介紹配置瞭,具體可以看官方文檔:字體

2.2 Button

Material 組件庫提供瞭多種多樣的按鈕,他們都是直接或間接對 RawMaterialButton 的包裝定制,所以大部分屬性都一樣。另外 Marterial 庫中的按鈕都有以下共同點:

  • 按下時都有水波紋
  • 動畫統一用 onPressed 屬性來設置回調,當按鈕按下時會執行該回調,如果不提供回調則按鈕會處於禁用狀態,不會響應用戶點擊

2.2.1 ElevatedButton

即 帶陰影的按鈕, 默認帶有陰影和灰色背景,按下後陰影會變大,如下所示:

代碼如下:

        child: ElevatedButton(
          child: const Text("i am ElevatedButton"),
          onPressed: () {},
        ),
      ),

2.2.2 TextButton

文本按鈕,按下後會有背景色,如下圖所示:

2.2.3 OutlinedButton

默認有一個邊框,不帶陰影且背景透明,按下後,邊框顏色會變亮、同時出現背景和陰影,如下圖所示:

2.2.4 IconButton

可以點擊的 Icon, 不包含文字,點擊後會出現背景,如下所示:

代碼設置為:

IconButton(
 icon: Icon(Icons.eleven_mp),
 onPressed: () {},
),

2.2.5 帶圖標的按鈕

上面學到的 ElevatedButtonTextButtonOutlinedButton 都有一個 icon() 的構造函數,這樣就可以代入一個圖片進去,例如設置:

ElevatedButton.icon(
          icon: const Icon(Icons.send),
          label: const Text("發送"),
          onPressed: () {},
        ),

效果為(這裡有編碼問題,可以無視):

2.3 圖片及Icon

2.3.1 圖片

可以通過 Image 組件來加載並顯示佈局, Image 的數據源可以是

  • asset
  • 文件
  • 內存
  • 網絡
2.3.1.1 ImageProvider

ImageProvider 是抽象類,主要定義瞭圖片的獲取接口 load(),從不同的數據源獲取圖片需要實現不同的 ImageProvider,如 AssetImage 是實現瞭從 Asset 中加載圖片, NetworkImage 則實現瞭從網絡中加載圖片。

2.3.1.2 Image Widget

Image 組件在構建時有一個必選的 image 參數,它對應一個 ImageProvier,下面分別演示一下如何從 asset 和 網絡中加載圖片。

1.從 asset 中加載圖片

在工程根目錄下創建一個 images 目錄,並將圖片拷貝到該目錄。

接下來在 pubspec.yaml 文件的 flutter部分 中,寫入(註意縮進):

flutter:
  ..
  assets:
    - assets/images/bobo.jpg

最後在代碼中使用:

Image(
  image: AssetImage("images/bobo.jpg"),
  width: 100.0,
)

就能展示圖片。

(不過我這裡遇到一個問題,使用手機運行Flutter應用能正常展示圖片,但是使用 Chrome 模擬器會報錯,不知道是什麼原因造成的

2.從網絡URL中加載圖片

直接使用代碼:

Image(
  image: NetworkImage("https://www.wahaotu.com/uploads/allimg/201904/1554901831804910.jpg"),
  width: 100.0,
)

可以正常展示圖片。

(不過這裡出現瞭很上面一樣的問題,但是使用官方使用的url又能正常展示圖片

2.3.1.3 Image 參數

我們可以來看下 Image 的參數,通過這些參數可以控制圖片外觀、大小、混合效果等。

  const Image({
    Key? key,
    required this.image,
    this.frameBuilder,
    this.loadingBuilder,
    this.errorBuilder,
    this.semanticLabel,
    this.excludeFromSemantics = false,
    this.width,
    this.height,
    this.color,
    this.opacity,
    this.colorBlendMode,
    this.fit,
    this.alignment = Alignment.center,
    this.repeat = ImageRepeat.noRepeat,
    this.centerSlice,
    this.matchTextDirection = false,
    this.gaplessPlayback = false,
    this.isAntiAlias = false,
    this.filterQuality = FilterQuality.low,
  })
  • widthheight
    設置圖片寬高,當不指定寬高時,會根據當前父容器的限制盡可能的顯示其原始大小,如果隻設置其中一個,那麼另一個屬性默認會按比例縮放
  • fit
    該屬性用於用於在圖片的顯示空間和圖片本身大小不同時指定圖片的適應模式。適應模式是在 BoxFit 中定義的,它是一個枚舉類型,有這些值:
    fill:拉伸填充滿顯示空間 ,圖片會便是
    cover:會按圖片的長寬比放大後居中填滿顯示空間,圖片不會變形,超出顯示部分會被剪裁
    contain:圖片默認適應規則,圖片會保證圖片本身長寬比不變的情況下縮放以適應當前的顯示空間
    fitWidth:圖片寬度會縮放到顯示空間的寬度,高度會按比例縮放,居中顯示,圖片不會變形
    fitHeight:和上面的反著來
  • none:圖片沒有適應策略,會在顯示空間內顯示圖片

  • colorcolorBlendMode:在圖片繪制時可以對每一個像素進行顏色混合處理,color指定混合色,而 colorBlendMode 指定混合模式下,因為用的比較少,這裡就不做實例
  • repeat:當圖片本身大小小於顯示空間時,指定圖片的重復規則,這裡也不做展示

2.3.2 Icon

Android中有 svg 矢量圖, 而 Flutter 中的也有,就是 Icon,它有下面這些優點:

  • 體積小
  • 因為是矢量圖,所以拉伸不會影響清晰程度
  • 可以通過 TextSpan 和 文本混用
  • 可以引用到文本樣式

Flutter 默認實現瞭一套Icon,在 pubspec.yaml 的配置文件可以看到:

flutter:
  uses-material-design: true

來看下官方的示例代碼:

String icons = "";
// accessible: 0xe03e
icons += "\uE03e";
// error:  0xe237
icons += " \uE237";
// fingerprint: 0xe287
icons += " \uE287";

Text(
  icons,
  style: TextStyle(
    fontFamily: "MaterialIcons",
    fontSize: 24.0,
    color: Colors.green,
  ),
);

效果為:

為瞭不讓開發者碼點,Flutter 封裝瞭 IconDataIcon來專門顯示字體圖片,上面的例子也可以用下面方式實現:

Row(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    Icon(Icons.accessible,color: Colors.green),
    Icon(Icons.error,color: Colors.green),
    Icon(Icons.fingerprint,color: Colors.green),
  ],
)

我們也可以使用自定義的字體圖標,這裡就不贅述瞭,可以看看官方示例:Icon自定義字體圖標

2.4 單選開關和復選框

Flutter 提供瞭 Material 風格的 開關Switch復選框Checkbox,它們都繼承自 StatfulWidget,但是它們不會保存選中的狀態,選中狀態是由父組件來管理的。 當 Switch 或者 Checkbox 被點擊時,會觸發 onChanged 回調,我們可以在此回調中處理選中狀態改變邏輯,下面看官方例子:

class SwitchAndCheckBoxTestRoute extends StatefulWidget {
  @override
  _SwitchAndCheckBoxTestRouteState createState() => _SwitchAndCheckBoxTestRouteState();
}

class _SwitchAndCheckBoxTestRouteState extends State<SwitchAndCheckBoxTestRoute> {
  bool _switchSelected=true; //維護單選開關狀態
  bool _checkboxSelected=true;//維護復選框狀態
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Switch(
          value: _switchSelected,//當前狀態
          onChanged:(value){
            //重新構建頁面  
            setState(() {
              _switchSelected=value;
            });
          },
        ),
        Checkbox(
          value: _checkboxSelected,
          activeColor: Colors.red, //選中時的顏色
          onChanged:(value){
            setState(() {
              _checkboxSelected=value!;
            });
          } ,
        )
      ],
    );
  }
}

代碼中需要維護 SwitchCheckbox 的選中狀態,所以 Widget 繼承自 StatefulWidget。 在其 build 方法中分別狀態瞭 Switch 和 Checkbox, 並且用兩個 bool 值來維護分別的選中狀態。 當按鈕被點擊時,會回調 onChanged 回調選中狀態出去,此時我們需要調用 setState() 方法來觸發 Flutter 重繪。

為什麼要這樣子設計,我的理解是:

  • 將開關、復選框的狀態拋給父組件,可以更加靈活,比如在勾選時候做一些網絡請求,即異步的操作
  • 一般來說,這些item是否選中,是和用戶數據關聯的,用戶數據也不可能是他們的私有狀態,所以放在一起管理更好

2.4.1 屬性

它們的屬性比較簡單,常用的有:

  • activeColor:設置激活狀態的顏色
  • tristate: 是否為三態,僅 Checbox有,一般情況下隻有 “true” 和 “false”,表示選中和非選中,如果設置瞭 tristate 後,還會增加一個 “null” 狀態

此外, Checkbox 不可設置寬高,其大小是自定義的,而 Switch 也僅能設置寬度而已。

2.5 輸入框以及表單

Flutter Material組件提供瞭 輸入款TextField表單Form

2.5.1 輸入框 TextField

2.5.1.1 屬性

來看下 TextField 提供的屬性:

  const TextField({
    ...
    this.controller,
    this.focusNode,
    this.decoration = const InputDecoration(),
    TextInputType? keyboardType,
    this.textInputAction,
    this.textCapitalization = TextCapitalization.none,
    this.style,
    this.strutStyle,
    this.textAlign = TextAlign.start,
    this.textAlignVertical,
    this.textDirection,
    this.readOnly = false,
    ToolbarOptions? toolbarOptions,
    this.showCursor,
    this.autofocus = false,
    this.obscuringCharacter = '•',
    this.obscureText = false,
    this.autocorrect = true,
    SmartDashesType? smartDashesType,
    SmartQuotesType? smartQuotesType,
    this.enableSuggestions = true,
    this.maxLines = 1,
    this.minLines,
    this.expands = false,
    this.maxLength,
    this.maxLengthEnforcement,
    this.onChanged,
    this.onEditingComplete,
    this.onSubmitted,
    this.onAppPrivateCommand,
    this.inputFormatters,
    this.enabled,
    this.cursorWidth = 2.0,
    this.cursorHeight,
    this.cursorRadius,
    this.cursorColor,
    this.selectionHeightStyle = ui.BoxHeightStyle.tight,
    this.selectionWidthStyle = ui.BoxWidthStyle.tight,
    this.keyboardAppearance,
    this.scrollPadding = const EdgeInsets.all(20.0),
    this.dragStartBehavior = DragStartBehavior.start,
    this.enableInteractiveSelection = true,
    this.selectionControls,
    this.onTap,
    this.mouseCursor,
    this.buildCounter,
    this.scrollController,
    this.scrollPhysics,
    this.autofillHints,
    this.restorationId,
    this.enableIMEPersonalizedLearning = true,
  })

屬性比較多,列幾個關鍵的講解:

  • controller
    編輯框的控制器,通過它可以設置/獲取編輯框的內容、選擇編輯內容、監聽編輯文本改變事件。大多數情況下我們都需要顯示提供一個 controller 來與文本框交互,如果設置的話, TextField 內部會創建一個
  • focusNode
    用於控制 TextField 是否占有當前鍵盤的輸入焦點
  • InputDecoration
    用於控制 TextField 的外觀顯示,如提示文本、背景顏色、邊框等。
  • keyboardType
    用於設置該輸入框默認的鍵盤輸入類型, 有文本、電話、email等格式
  • textInputAction
    鍵盤動作按鈕圖標,就是右下角的那個圖標設置
  • style
    文本的樣式(正在編輯中的)
  • textAlign
    輸入框內編輯文本在水平方向的對齊方式
  • autofocus
    是否自動獲取焦點
  • obscureText
    是否隱藏正在編輯的文本, 比如輸入密碼的場景,文本內容會用 “•” 來代替
  • maxLines
    最大行數
  • maxLenthmaxLengthEnforcement
    maxLenth 代表輸入框文本的最大長度,設置後輸入框右下角會顯示輸入的文本計數
    maxLengthEnforcement 決定輸入文本長度超過 maxLength 時如何處理,如截斷
  • toolbarOptions
    長按時出現的菜單,可以選擇 copy、cut、paste 、selectAll
  • onChange
    輸入框內容改變的回調, 當然 controller 也可以做到監聽
  • onEditingCompleteonSubmitted
    作用一樣,都是在輸入完成時觸發,比如點擊瞭鍵盤的 完成鍵、搜索鍵不同的是兩個回調簽名不同
  • inputFormatters
    指定輸入格式,當用戶輸入內容改變時,會根據指定格式來校驗
  • enable
    如果為false, 則輸入框會被禁用
  • cursorWidthcursorRadiuscursorColor
    分別表示自定義輸入框光標寬度、圓角和顏色

一個簡單的設置代碼如下:

Column(children: const <Widget>[
        TextField(
          autofocus: true,
          decoration: InputDecoration(
            labelText: "用戶名",
            hintText: "請輸入用戶名或密碼",
            prefixIcon: Icon(Icons.person)
          ),
        ),
        TextField(
          decoration: InputDecoration(
            labelText: "密碼",
            hintText: "請輸入密碼",
            prefixIcon: Icon(Icons.lock)
          ),
          obscureText: true,
        )
      ]),

2.5.1.2 通過 controller 獲取輸入內容

我們可以通過 onChange 拿到內容。 當然也可以使用 controller 來獲取

步驟為:

定義一個 controller
final TextEditingController _tfController = TextEditingController();
然後在 TextFiled 中傳入這個 controller
TextField(
  controller: _tfController,
  ...
)

最後就可以通過 : print(_tfController.text) 來獲得輸入框的內容

2.5.1.3 通過 controller 監聽文本內容變化

可以通過 onChange 來監聽文本, controller 可以通過設置監聽器來監聽文本,如下:

  @override
  void initState() {
    super.initState();
    _tfController.addListener(() { 
      print(_tfController.text);
    });
  }

controller 的功能更多,除瞭監聽文本,還可以設置默認值、選擇文本等,這裡就不多贅述。

2.5.1.4 控制焦點

可以使用 FocusNodeFocusScopeNode 來控制焦點。默認情況下是由 FocusScope 來管理,可以在這個范圍內通過 FocusScopeNode 在輸入框之間移動焦點、設置默認焦點。

我們可以通過下面代碼來獲取當前 Widget 樹中默認的 FocusScopeNode:

focusScopeNode = FocusScope.of(context)

拿到句柄後,可以使用下面代碼來獲取焦點:

focusScopeNode.requestFocus(focusNode);

其中 focucsNode 是為 TextField 創建的 FocusNode, 這個操作可以讓該 TextField 獲取焦點。 調用 focusNode.unfocus() 可以取消焦點。

2.5.1.5 監聽焦點狀態改變事件

通過 FocusNode 可以監聽焦點改變的事件:

focusNode.addListener((){
   print(focusNode.hasFocus);
})

true為獲取焦點,false為失去焦點

2.5.2 表單

表單Form 對輸入框進行分組和統一操作。 就像 Android 的原生組件 RadioGroup 之於 RadioButton 一樣, Form 可以管理內容校驗、輸入框重置等。

Form 繼承自 StatefulWidget,其狀態管理在 FormState 裡面,來看看 From 的定義:

class Form extends StatefulWidget {
  const Form({
    Key? key,
    required this.child,
    @Deprecated(
      'Use autovalidateMode parameter which provides more specific '
      'behavior related to auto validation. '
      'This feature was deprecated after v1.19.0.',
    )
    this.autovalidate = false,
    this.onWillPop,
    this.onChanged,
    AutovalidateMode? autovalidateMode,
  })
  ...
  • autovalidate
    是否自動校驗輸入內容,當為true時,每一個 FormField 內容發生變化時都會校驗合法性,並直接顯示錯誤信息,否則就需要通過調用 FormState.validate() 來手動校驗
    v1.19 已經廢棄瞭,改成使用 AutovalidateMode
  • autovalidateMode
    自動校驗模式,是上面的替換,它有三個枚舉值:
    disable:當 FormField 內容改變時不做校驗
    always:即使用戶沒有用戶交互也要校驗合法性
    onUserInteraction:隻有在用戶交互時才會去校驗合法性
  • onWillPop
    決定 Form 所在的路由是否可以直接返回。該回調返回一個 Future 對象,如果 Future 的最終結果是 false,則當前路由不會返回,如果為 true,則會返回到上一個路由。
    這個屬性通常是用於攔截返回按鈕的
  • onChanged
    Form 的任意一個 FormField 內容發生改變時就會調用該方法
2.5.2.1 FormField

Form 的子孫元素是 FormField 類型,FormField 是一個抽象類,定義瞭幾個屬性, FormState 內部通過他們來完成操作, FormField 部分定義如下:

  const FormField({
    Key? key,
    required this.builder,
    this.onSaved,
    this.validator,
    this.initialValue,
    @Deprecated(
      'Use autovalidateMode parameter which provides more specific '
      'behavior related to auto validation. '
      'This feature was deprecated after v1.19.0.',
    )
    this.autovalidate = false,
    this.enabled = true,
    AutovalidateMode? autovalidateMode,
    this.restorationId,
  })
  • onSaved
    保存時的回調
  • validator
    驗證合法性的回調
  • initValue
    初始值

為瞭方便使用, Flutter 提供瞭一個 TextFormFild 組件,繼承自 FormField 類,還包裝瞭 TextFileld ,可以直接當成 Form 的 FormField 來使用, 相當於用 Form 來管理 TextField

2.5.2.2 FormState

Form 表單的狀態類就是 FormState, 可以通過 Form.of 或者 GlobalKey 獲得,通過獲得它來對 Form 的子孫 FormField 進行統一操作。

FormState 常用的三個方法:

  • FormState.validate():調用此方法後, 會調用 Form 子孫 FormField.validate() 回調,如果有一個檢驗失敗,那麼會返回 false,這樣所有校驗失敗的 Widget 都會給出錯誤提示
  • FormState.save():調用此方法後,會調用 子孫的 FormFild.save() 回調,用於保存表單內容
  • FormState.reset(): 會將子孫 FormField 的內容清空
2.5.2.3 示例

我們做一個用戶登錄的程序,再點擊登錄前需要做到輸入檢查:

  • 用戶名不能為空,如果為空則提示“用戶名不能為空”
  • 密碼不能小於6位,如果小於6位則提示 “密碼不能少於6位”

代碼如下:

import 'package:flutter/material.dart';

class FormTestRoute extends StatefulWidget {
  const FormTestRoute({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => _FormTestRouteState();
}

class _FormTestRouteState extends State<FormTestRoute> {
  final TextEditingController _usernameController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();
  final GlobalKey _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Form demo'),
        ),
        body: Form(
            key: _formKey,
            autovalidateMode: AutovalidateMode.onUserInteraction,
            child: Column(
              children: [
                TextFormField(
                  autofocus: true,
                  controller: _usernameController,
                  decoration: const InputDecoration(
                      labelText: "username",
                      hintText: "username or email",
                      icon: Icon(Icons.person)),
                  validator: (username) {
                    return username!.trim().isNotEmpty
                        ? null
                        : "username cannot empty";
                  },
                ),
                TextFormField(
                  controller: _passwordController,
                  decoration: const InputDecoration(
                      labelText: "password",
                      hintText: "please input your password",
                      icon: Icon(Icons.lock)),
                  obscureText: true,
                  validator: (pwd) {
                    return pwd!.trim().length >= 6
                        ? null
                        : "password digit cannot less than 6!";
                  },
                ),
                // login button
                Padding(
                  padding: const EdgeInsets.only(top: 28.0),
                  child: Row(
                    children: [
                      Expanded(
                          child: ElevatedButton(
                        onPressed: () {
                          if ((_formKey.currentState as FormState).validate()) {
                            print("Loing success");
                          }
                        },
                        child: const Padding(
                          padding: EdgeInsets.all(16.0),
                          child: Text("Login"),
                        ),
                      ))
                    ],
                  ),
                )
              ],
            )));
  }
}

效果如下圖所示:

以上所述是小編給大傢介紹的Flutter基本組件Basics Widget學習,希望對大傢有所幫助。在此也非常感謝大傢對WalkonNet網站的支持!

推薦閱讀: