開始學習一門全新的語言確實很慢,需要了解很多控件,個人的做法是大家都記不住這麼多,所以就要做一個件事,那就是寫博客,把你看到的情況都列出來,這樣你遇到需求的時候就去翻翻控件那一篇,copycopy就記住了,也沒有那麼不想學了,可以去試試~這篇是我總結的各種前期遇到比較多的控件樣式設置寫法,以後會更新下去,這樣每次記不住回來看一眼,跟API一樣~
1.Widget:多寫幾次你會發現越寫越順,其實不是很繞,一看上去有點小亂,後面由於google智能的換行,所以很好找~試試吧~
import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); //Stateless widgets 是不可變的, 這意味着它們的屬性不能改變 - 所有的值都是最終的. //Stateful widgets 持有的狀態可能在widget生命週期中發生變化. 實現一個 stateful widget 至少需要兩個類: class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // final wordPair = new WordPair.random(); return new MaterialApp( title: 'Welcome to Flutter', debugShowCheckedModeBanner: false, home: new Scaffold( appBar: new AppBar( title: new Text('Welcome to Flutter'), ), body: new Center( child: new Text('Hello World'), ), ), ); } }
2.文本及樣式:我總結了一些常用的後續會繼續更新,直接拿去用,目前看應該夠用~
import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; void main() => runApp(new MyApp()); //void main() { // runApp(new MaterialApp( // home: new MyApp(), // )); //} class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new MaterialApp( title: 'Flutter Demo1', debugShowCheckedModeBanner: false, theme: new ThemeData( primarySwatch: Colors.blue, // fontFamily: 'fontdemo1' ), home: new MyHomePage(title: '文本及樣式'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override State<StatefulWidget> createState() { // TODO: implement createState return new _MyHomePageState(); } } class _MyHomePageState extends State<MyHomePage> { final textStyleAssetFont1 = const TextStyle( fontFamily: 'fontdemo1', ); final textStyleAssetFont2 = const TextStyle( fontFamily: 'fontdemo2', ); final textStyleAssetFont3 = const TextStyle( letterSpacing: 2.0, ); final textStyleAssetFont4 = const TextStyle( height: 2.0, ); @override Widget build(BuildContext context) { // TODO: implement build return new Scaffold( appBar: new AppBar( title: new Text( widget.title, style: textStyleAssetFont2, ), ), body: new Center( child: new Column( // mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ new Text( "1.hi yun~", textAlign: TextAlign.start, style: textStyleAssetFont3, ), new Text( "2.hi yun~" * 16, maxLines: 1, overflow: TextOverflow.ellipsis, style: textStyleAssetFont4, // style: Theme.of(context).textTheme.display1, ), new Text( "3.hi yun~", textScaleFactor: 1.5, style: textStyleAssetFont4, ), new Text( "4.hi yun~" * 16, textAlign: TextAlign.start, style: textStyleAssetFont4, ), new Text( "5.hi yun~", style: TextStyle( color: Colors.blue, fontSize: 18.0, height: 2.0, fontFamily: "Courier", background: new Paint()..color = Colors.yellow, decoration: TextDecoration.underline, decorationStyle: TextDecorationStyle.dashed), ), new Text( "紅色+黑色刪除線+25號", style: new TextStyle( color: const Color(0xffff0000), decoration: TextDecoration.lineThrough, decorationColor: const Color(0xff000000), fontSize: 25.0, ), ), new Text.rich(TextSpan(children: [ TextSpan(text: "6.Yun:"), TextSpan( text: "https://flutterchina.club", style: TextStyle( color: Colors.blue, height: 2.0, ), // recognizer: _tapRecognizer ), ])), DefaultTextStyle( style: TextStyle( color: Colors.red, fontSize: 20.0, height: 2.0, ), textAlign: TextAlign.start, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text("7.hi yun1~"), Text("7.hi yun2~"), Text( "7.hi yun3~", style: TextStyle( inherit: false, color: Colors.grey, height: 2.0, ), ), Text( "8.hi yun~", style: textStyleAssetFont2, ), ], ), ), ], ), ), ); } }
需要注意的是路徑要對:
name: p001_flutter_demo1 description: A new Flutter application. # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 # followed by an optional build number separated by a +. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # Read more about versioning at semver.org. version: 1.0.0+1 environment: sdk: ">=2.0.0-dev.68.0 <3.0.0" dependencies: flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2 english_words: ^3.1.5 dev_dependencies: flutter_test: sdk: flutter # For information on the generic Dart part of this file, see the # following page: https://www.dartlang.org/tools/pub/pubspec # The following section is specific to Flutter. flutter: # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. uses-material-design: true assets: - assets/demo.txt - assets/images/ # To add assets to your application, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.io/assets-and-images/#resolution-aware. # For details regarding adding assets from package dependencies, see # https://flutter.io/assets-and-images/#from-packages # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a # "family" key with the font family name, and a "fonts" key with a # list giving the asset and other descriptors for the font. For # example: # fonts: # - family: Schyler # fonts: # - asset: fonts/Schyler-Regular.ttf # - asset: fonts/Schyler-Italic.ttf # style: italic # - family: Trajan Pro # fonts: # - asset: fonts/TrajanPro.ttf # - asset: fonts/TrajanPro_Bold.ttf # weight: 700 # fonts: - family: fontdemo1 fonts: - asset: assets/fonts/NotoSerifTC-Bold.otf style: italic - family: fontdemo2 fonts: - asset: assets/fonts/ZCOOLKuaiLe-Regular.ttf weight: 500 - family: iconfont fonts: - asset: assets/fonts/iconfont.ttf weight: 700 # For details regarding fonts from package dependencies, # see https://flutter.io/custom-fonts/#from-packages
3.圖片及icon:這裏多了一個iconfont.cn的操作,大家可以瞭解一下
import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); //Stateless widgets 是不可變的, 這意味着它們的屬性不能改變 - 所有的值都是最終的. //Stateful widgets 持有的狀態可能在widget生命週期中發生變化. 實現一個 stateful widget 至少需要兩個類: class MyApp extends StatelessWidget { final textStyleAssetFont1 = const TextStyle( height: 0.5, ); @override Widget build(BuildContext context) { // final wordPair = new WordPair.random(); return new MaterialApp( title: 'Welcome to Flutter', debugShowCheckedModeBanner: false, theme: new ThemeData( primarySwatch: Colors.blue, // fontFamily: 'fontdemo1' ), home: new Scaffold( appBar: new AppBar( title: new Text('圖片加載'), ), body: new Center( child: new Column( crossAxisAlignment: CrossAxisAlignment.center, // mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ // CustomScrollView( // slivers: <Widget>[ // // ], // ), new Text( "", style: textStyleAssetFont1, ), new Image( image: AssetImage("assets/images/food01.jpeg"), fit: BoxFit.fill, width: 120.0, ), new Text( "", style: textStyleAssetFont1, ), new Image.asset( "assets/images/food02.jpeg", width: 120.0, fit: BoxFit.cover, ), new Text( "", style: textStyleAssetFont1, ), new Image( image: NetworkImage( "https://s1.51cto.com/images/20190423/1556012017949570.png"), width: 120.0, fit: BoxFit.contain, ), new Text( "", style: textStyleAssetFont1, ), new Image.network( "https://s1.51cto.com/images/20190423/1556012017949570.png", width: 120.0, fit: BoxFit.fill, ), new Text( "", style: textStyleAssetFont1, ), Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Icon( Icons.accessible, color: Colors.green, ), Icon( Icons.error, color: Colors.green, ), Icon( Icons.fingerprint, color: Colors.green, ), ], ), Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Icon( MyIcons.qq, color: Colors.red, ), Icon( MyIcons.wechat, color: Colors.green, ), ], ) ], ), ), ), ); } } class MyIcons { // book 圖標 static const IconData qq = const IconData(0xe606, fontFamily: 'iconfont', matchTextDirection: true); // 微信圖標 static const IconData wechat = const IconData(0xe607, fontFamily: 'iconfont', matchTextDirection: true); }
4.單選開關和複選框:
import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); //Stateless widgets 是不可變的, 這意味着它們的屬性不能改變 - 所有的值都是最終的. //Stateful widgets 持有的狀態可能在widget生命週期中發生變化. 實現一個 stateful widget 至少需要兩個類: class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new MaterialApp( title: "單選框和複選框", home: new Scaffold( appBar: new AppBar( title: new Text("單選框和複選框"), ), body: new Center( child: new SwitchAndCheckBoxTestRoute(), ), ), ); } } class SwitchAndCheckBoxTestRoute extends StatefulWidget { @override State<StatefulWidget> createState() { // TODO: implement createState return new _SwitchAndCheckBoxTestRoute(); } } class _SwitchAndCheckBoxTestRoute extends State<SwitchAndCheckBoxTestRoute> { bool _switchSelected = true; bool _checkboxSelected = true; @override Widget build(BuildContext context) { // TODO: implement build return new Column( children: <Widget>[ Switch( value: _switchSelected, activeColor: Colors.blue, inactiveThumbColor: Colors.lightBlueAccent, onChanged: (value) { setState(() { _switchSelected = value; }); }, ), Checkbox( value: _checkboxSelected, activeColor: Colors.red, onChanged: (value) { setState(() { _checkboxSelected = value; }); }, ), ], ); } }
5.輸入框和表單:這塊比較複雜,我在學習過程中遇到很多問題,找到了一些方案,最常見的就是佈局問題,鍵盤遮擋,目前找到的這個方案比較合適,希望能幫到你~
解決遮擋的基類:
/** * 作者:Created by H on 2019/1/23 11:08. * 介紹: 解決輸入框被遮擋問題 */ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; /// /// Helper class that ensures a Widget is visible when it has the focus /// For example, for a TextFormField when the keyboard is displayed /// /// How to use it: /// /// In the class that implements the Form, /// Instantiate a FocusNode /// FocusNode _focusNode = new FocusNode(); /// /// In the build(BuildContext context), wrap the TextFormField as follows: /// /// new EnsureVisibleWhenFocused( /// focusNode: _focusNode, /// child: new TextFormField( /// ... /// focusNode: _focusNode, /// ), /// ), /// /// Initial source code written by Collin Jackson. /// Extended (see highlighting) to cover the case when the keyboard is dismissed and the /// user clicks the TextFormField/TextField which still has the focus. /// class EnsureVisibleWhenFocused extends StatefulWidget { const EnsureVisibleWhenFocused({ Key key, @required this.child, @required this.focusNode, this.curve: Curves.ease, this.duration: const Duration(milliseconds: 100), }) : super(key: key); /// The node we will monitor to determine if the child is focused final FocusNode focusNode; /// The child widget that we are wrapping final Widget child; /// The curve we will use to scroll ourselves into view. /// /// Defaults to Curves.ease. final Curve curve; /// The duration we will use to scroll ourselves into view /// /// Defaults to 100 milliseconds. final Duration duration; @override _EnsureVisibleWhenFocusedState createState() => new _EnsureVisibleWhenFocusedState(); } /// /// We implement the WidgetsBindingObserver to be notified of any change to the window metrics /// class _EnsureVisibleWhenFocusedState extends State<EnsureVisibleWhenFocused> with WidgetsBindingObserver { @override void initState(){ super.initState(); widget.focusNode.addListener(_ensureVisible); WidgetsBinding.instance.addObserver(this); } @override void dispose(){ WidgetsBinding.instance.removeObserver(this); widget.focusNode.removeListener(_ensureVisible); super.dispose(); } /// /// This routine is invoked when the window metrics have changed. /// This happens when the keyboard is open or dismissed, among others. /// It is the opportunity to check if the field has the focus /// and to ensure it is fully visible in the viewport when /// the keyboard is displayed /// @override void didChangeMetrics(){ if (widget.focusNode.hasFocus){ _ensureVisible(); } } /// /// This routine waits for the keyboard to come into view. /// In order to prevent some issues if the Widget is dismissed in the /// middle of the loop, we need to check the "mounted" property /// /// This method was suggested by Peter Yuen (see discussion). /// Future<Null> _keyboardToggled() async { if (mounted){ EdgeInsets edgeInsets = MediaQuery.of(context).viewInsets; while (mounted && MediaQuery.of(context).viewInsets == edgeInsets) { await new Future.delayed(const Duration(milliseconds: 10)); } } return; } Future<Null> _ensureVisible() async { // Wait for the keyboard to come into view await Future.any([new Future.delayed(const Duration(milliseconds: 300)), _keyboardToggled()]); // No need to go any further if the node has not the focus if (!widget.focusNode.hasFocus){ return; } // Find the object which has the focus final RenderObject object = context.findRenderObject(); final RenderAbstractViewport viewport = RenderAbstractViewport.of(object); assert(viewport != null); // Get the Scrollable state (in order to retrieve its offset) ScrollableState scrollableState = Scrollable.of(context); assert(scrollableState != null); // Get its offset ScrollPosition position = scrollableState.position; double alignment; if (position.pixels > viewport.getOffsetToReveal(object, 0.0).offset) { // Move down to the top of the viewport alignment = 0.0; } else if (position.pixels < viewport.getOffsetToReveal(object, 1.0).offset){ // Move up to the bottom of the viewport alignment = 1.0; } else { // No scrolling is necessary to reveal the child return; } position.ensureVisible( object, alignment: alignment, duration: widget.duration, curve: widget.curve, ); } @override Widget build(BuildContext context) { return widget.child; } }
如何使用:
import 'package:flutter/material.dart'; import 'main15.dart'; void main() => runApp(new MyApp()); //Stateless widgets 是不可變的, 這意味着它們的屬性不能改變 - 所有的值都是最終的. //Stateful widgets 持有的狀態可能在widget生命週期中發生變化. 實現一個 stateful widget 至少需要兩個類: class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new MaterialApp( title: "輸入框及表單", home: new Scaffold( appBar: new AppBar( title: new Text("輸入框及表單"), ), body: new Center( // child: new FormTestRoute(), child: new TestPage(), ), ), ); } } class TestPage extends StatefulWidget { @override _TestPageState createState() => new _TestPageState(); } class _TestPageState extends State<TestPage> { final GlobalKey<FormState> _formKey = new GlobalKey<FormState>(); FocusNode _focusNodeFirstName = new FocusNode(); FocusNode _focusNodeLastName = new FocusNode(); FocusNode _focusNodeDescription = new FocusNode(); static final TextEditingController _firstNameController = new TextEditingController(); static final TextEditingController _lastNameController = new TextEditingController(); static final TextEditingController _pwdController = new TextEditingController(); static final TextEditingController _descriptionController = new TextEditingController(); @override Widget build(BuildContext context) { return new Scaffold( // appBar: new AppBar( // title: new Text('My Test Page'), // ), body: new SafeArea( top: false, bottom: false, child: new Form( key: _formKey, //設置globalKey,用於後面獲取FormState autovalidate: true, //開啓自動校驗 child: new SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: new Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ /* -- Something large -- */ Container( width: double.infinity, height: 150.0, color: Colors.red, ), /* -- First Name -- */ new EnsureVisibleWhenFocused( focusNode: _focusNodeFirstName, child: new TextFormField( decoration: const InputDecoration( border: const UnderlineInputBorder(), filled: true, icon: const Icon(Icons.person), labelText: "用戶名", hintText: "用戶名或郵箱", ), // 校驗用戶名 validator: (v) { return v.trim().length > 0 ? null : "用戶名不能爲空"; }, onSaved: (String value) { //TODO }, controller: _firstNameController, focusNode: _focusNodeFirstName, ), ), const SizedBox(height: 24.0), /* -- Last Name -- */ new EnsureVisibleWhenFocused( focusNode: _focusNodeLastName, child: new TextFormField( decoration: const InputDecoration( border: const UnderlineInputBorder(), filled: true, icon: const Icon(Icons.lock), labelText: "密碼", hintText: "您的登錄密碼", ), obscureText: true, //校驗密碼 validator: (v) { return v.trim().length > 5 ? null : "密碼不能少於6位"; }, onSaved: (String value) { //TODO }, // controller: _lastNameController, controller: _pwdController, focusNode: _focusNodeLastName, ), ), const SizedBox(height: 24.0), /* -- Some other fields -- */ new Container( width: double.infinity, height: 250.0, color: Colors.blue, ), /* -- Description -- */ new EnsureVisibleWhenFocused( focusNode: _focusNodeDescription, child: new TextFormField( decoration: const InputDecoration( border: const OutlineInputBorder(), hintText: '請介紹一下自己', labelText: '簡介', ), onSaved: (String value) { //TODO }, maxLines: 5, controller: _descriptionController, focusNode: _focusNodeDescription, ), ), const SizedBox(height: 24.0), /* -- Save Button -- */ new Center( child: new RaisedButton( child: const Text('確定'), onPressed: () { //TODO if ((_formKey.currentState as FormState).validate()) { //驗證通過提交數據 } }, ), ), const SizedBox(height: 24.0), ], ), ), ), ), ); } } //class FormTestRoute extends StatefulWidget { // @override // _FormTestRouteState createState() => new _FormTestRouteState(); //} // //class _FormTestRouteState extends State<FormTestRoute> { // TextEditingController _unameController = new TextEditingController(); // TextEditingController _pwdController = new TextEditingController(); // GlobalKey _formKey = new GlobalKey<FormState>(); // FocusNode _focusNode = new FocusNode(); // // @override // Widget build(BuildContext context) { // return Scaffold( //// title: "Form Test", // // body: Padding( // padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0), // child: Form( // key: _formKey, //設置globalKey,用於後面獲取FormState // autovalidate: true, //開啓自動校驗 // child: Column( // children: <Widget>[ // new EnsureVisibleWhenFocused( // focusNode: _focusNode, // child: new TextFormField( // autofocus: true, // controller: _unameController, // decoration: InputDecoration( // labelText: "用戶名", // hintText: "用戶名或郵箱", // icon: Icon(Icons.person)), // // 校驗用戶名 // validator: (v) { // return v.trim().length > 0 ? null : "用戶名不能爲空"; // }, // focusNode: _focusNode, // ), // ), // new EnsureVisibleWhenFocused( // focusNode: _focusNode, // child: new TextFormField( // controller: _pwdController, // decoration: InputDecoration( // labelText: "密碼", // hintText: "您的登錄密碼", // icon: Icon(Icons.lock)), // obscureText: true, // //校驗密碼 // validator: (v) { // return v.trim().length > 5 ? null : "密碼不能少於6位"; // }, // focusNode: _focusNode, // ), // ), //// TextFormField( //// autofocus: true, //// controller: _unameController, //// decoration: InputDecoration( //// labelText: "用戶名", //// hintText: "用戶名或郵箱", //// icon: Icon(Icons.person)), //// // 校驗用戶名 //// validator: (v) { //// return v.trim().length > 0 ? null : "用戶名不能爲空"; //// }, //// ), //// TextFormField( //// controller: _pwdController, //// decoration: InputDecoration( //// labelText: "密碼", //// hintText: "您的登錄密碼", //// icon: Icon(Icons.lock)), //// obscureText: true, //// //校驗密碼 //// validator: (v) { //// return v.trim().length > 5 ? null : "密碼不能少於6位"; //// }, //// ), // // 登錄按鈕 // Padding( // padding: const EdgeInsets.only(top: 28.0), // child: Row( // children: <Widget>[ // Expanded( // child: RaisedButton( // padding: EdgeInsets.all(15.0), // child: Text("登錄"), // color: Theme.of(context).primaryColor, // textColor: Colors.white, // onPressed: () { // //在這裏不能通過此方式獲取FormState,context不對 // //print(Form.of(context)); // // // 通過_formKey.currentState 獲取FormState後, // // 調用validate()方法校驗用戶名密碼是否合法,校驗 // // 通過後再提交數據。 // if ((_formKey.currentState as FormState).validate()) { // //驗證通過提交數據 // } // }, // ), // ), // ], // ), // ) // ], // ), // ), // ), // ); // } //}
需要注意的是自定義佈局的時候要保證開啓全局校驗,還有就是官方的PageScaffold目前沒找到,可能是官方廢棄了吧,後續再看,可以用Scaffold替換,另外還有自定義樣式的寫法BoxDecoration是關鍵,可以查查資料結合下面的代碼修改,基本可以搞定一般的不帶動畫的需求~
import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); //Stateless widgets 是不可變的, 這意味着它們的屬性不能改變 - 所有的值都是最終的. //Stateful widgets 持有的狀態可能在widget生命週期中發生變化. 實現一個 stateful widget 至少需要兩個類: class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new MaterialApp( title: "輸入框及表單", home: new Scaffold( appBar: new AppBar( title: new Text("輸入框及表單"), ), body: new Center( child: new FormTestRoute(), ), ), ); } } class FormTestRoute extends StatefulWidget { @override _FormTestRouteState createState() => new _FormTestRouteState(); } class _FormTestRouteState extends State<FormTestRoute> { TextEditingController _unameController = new TextEditingController(); TextEditingController _pwdController = new TextEditingController(); GlobalKey _formKey = new GlobalKey<FormState>(); FocusNode _focusNode = new FocusNode(); @override Widget build(BuildContext context) { return Scaffold( // title: "Form Test", body: Container( padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0), decoration: BoxDecoration( // 下滑線淺灰色,寬度1像素 border: Border( bottom: BorderSide(color: Colors.grey[200], width: 1.0))), child: Form( key: _formKey, //設置globalKey,用於後面獲取FormState autovalidate: true, //開啓自動校驗 child: Column( children: <Widget>[ TextFormField( autofocus: true, controller: _unameController, decoration: InputDecoration( labelText: "用戶名", hintText: "用戶名或郵箱", icon: Icon(Icons.person), border: InputBorder.none, //隱藏下劃線 ), // 校驗用戶名 validator: (v) { return v.trim().length > 0 ? null : "用戶名不能爲空"; }, ), TextFormField( controller: _pwdController, decoration: InputDecoration( labelText: "密碼", hintText: "您的登錄密碼", icon: Icon(Icons.lock)), obscureText: true, //校驗密碼 validator: (v) { return v.trim().length > 5 ? null : "密碼不能少於6位"; }, ), // 登錄按鈕 Padding( padding: const EdgeInsets.only(top: 28.0), child: Row( children: <Widget>[ Expanded( child: RaisedButton( padding: EdgeInsets.all(15.0), child: Text("登錄"), color: Theme.of(context).primaryColor, textColor: Colors.white, onPressed: () { //在這裏不能通過此方式獲取FormState,context不對 //print(Form.of(context)); // 通過_formKey.currentState 獲取FormState後, // 調用validate()方法校驗用戶名密碼是否合法,校驗 // 通過後再提交數據。 if ((_formKey.currentState as FormState).validate()) { //驗證通過提交數據 } }, ), ), ], ), ) ], ), ), ), ); } }
總結:因爲週末了,所以今天講的有點多,可以慢慢敲一遍,你會發現dart是真心強,用的舒服~擼起袖子,繼續更新ing~