Flutter入門學習記錄【四】 表單 交互 異步 Widgets

https://flutterchina.club/widgets/input/
Flutter入門學習記錄【四】

表單

  1. Form
final _formKey = GlobalKey<FormState>();
//
          Form(
            key: _formKey,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                TextFormField(
                  decoration: const InputDecoration(
                    hintText: 'Enter your email',
                  ),
                  validator: (value) {
                    if (value.isEmpty) {
                      return 'Please enter some text';
                    }
                    return null;
                  },
                ),
                TextFormField(
                  decoration: const InputDecoration(
                    hintText: 'Enter your email password',

                  ),
                  validator: (value) {
                    if (value.isEmpty) {
                      return 'Please enter your password';
                    }
                    return null;
                  },
                ),
                Padding(
                  padding: const EdgeInsets.symmetric(vertical: 16.0),
                  child: RaisedButton(
                    onPressed: () {
                      // Validate will return true if the form is valid, or false if
                      // the form is invalid.
                      if (_formKey.currentState.validate()) {
                        // Process data.
                      }
                      
                    },
                    child: Text('Submit'),
                  ),
                ),
              ],
            ),
          )

點擊按鈕,autovalidate爲false的情況,執行_formKey.currentState.validate()會對上邊的TextFormField的內容進行驗證,如果不符合驗證條件就會顯示錯誤的提示文字


參數解析

  const Form({
    Key key,
    @required this.child,
    this.autovalidate = false,
    this.onWillPop,
    this.onChanged,
  })
  /// If true, form fields will validate and update their error text
  /// immediately after every change. Otherwise, you must call
  /// [FormState.validate] to validate.
//默認是false,你必須手動調用FormState的vallidate方法纔會執行判斷,如果爲true,獲取焦點就會自動做判斷的
  final bool autovalidate;
  /// Called when one of the form fields changes.
  ///任一一個FormField發生改變的時候都會回調這裏,好像沒啥用
  /// In addition to this callback being invoked, all the form fields themselves
  /// will rebuild.
  final VoidCallback onChanged;

  /// Enables the form to veto attempts by the user to dismiss the [ModalRoute]
  /// that contains the form.
  ///簡單點說,就是執行頁面後退功能的時候也就是pop當前頁面的時候,根據返回結果來決定是否 阻止後退,爲false阻止,爲true不阻止
  /// If the callback returns a Future that resolves to false, the form's route
  /// will not be popped.
  final WillPopCallback onWillPop;
//比如
            onWillPop: (){
              return Future.value(false);
            }

TextFormField

這個和普通的TextFiled參數差不多
有些區別主要就是回調了,比如

    ValueChanged<String> onChanged,
    GestureTapCallback onTap,
    VoidCallback onEditingComplete,
    ValueChanged<String> onFieldSubmitted,
    FormFieldSetter<String> onSaved,
    FormFieldValidator<String> validator,
    List<TextInputFormatter> inputFormatters,

RawKeyboardListener

交互

  1. LongPressDraggable
    長按可以拖動
  const LongPressDraggable({
    Key key,
    @required Widget child,//默認顯示的widget
    @required Widget feedback,//長按以後跟隨手指移動的widget
    T data,//到時候傳給一個DragTarget的控件,用來判斷是否接收
    Axis axis,
    Widget childWhenDragging,//可有可無,長按觸發以後用來替換默認的child的
    Offset feedbackOffset = Offset.zero,
    DragAnchor dragAnchor = DragAnchor.child,//錨點
    int maxSimultaneousDrags,
    VoidCallback onDragStarted,
    DraggableCanceledCallback onDraggableCanceled,//同下,也有對應的數據
    DragEndCallback onDragEnd,//這個回調裏會有偏移量,速率數據
    VoidCallback onDragCompleted,
    this.hapticFeedbackOnStart = true,
    bool ignoringFeedbackSemantics = true,
  }) 
  1. 關聯的DragTarget
  const DragTarget({
    Key key,
    @required this.builder,
    this.onWillAccept,// 回調裏返回的參數就是上邊那個LongPressDraggable裏data,當那個拖動到這個控件範圍的時候,返回true表示接收,會存到build的回調參數candidateData,返回false會存到build的回調參數rejectedData裏
    this.onAccept,//鬆開手指,上邊那個onWillAccept返回true的話會走這裏,否則走下邊那個
    this.onLeave,//另外手指從target範圍移出去以後也會走這裏
  })

簡單測試demo

 var _dragResult="drag to here please";

                       DragTarget(
                builder: (BuildContext context, List<String> candidateData,
                    List<dynamic> rejectedData) {
                  print(
                      'builder==========${candidateData.length}==========${rejectedData.length}');
                  if (candidateData.length > 0) {
                    _dragResult="you'are accepted,please release your finger";
                  }else if(rejectedData.length>0){
                    _dragResult="you'are rejected,please release your finger";
                  }
                  return Container(
                    width: 240,
                    height: 100,
                    alignment: Alignment.center,
                    child: Text(_dragResult,style: _defaultStyle,),
                    decoration: BoxDecoration(
                        border: Border.all(color: Colors.deepPurpleAccent)),
                  );
                },
                onWillAccept: (willAccept) {
                  print('onWillAccept=========$willAccept');
                  return willAccept == "test";
                },
                onAccept: (accept) {
                  print('accept==========$accept');
                  _dragResult="accept:$accept";
                },
                onLeave: (leave) {
                  print('leave============$leave');
                  _dragResult="leave:$leave";
                },
              ),

LongPressDraggable觸發以後DragTarget就會執行一次builder,完事手指移動到DragTarget範圍的時候會走onWillAccept方法,返回true的話會繼續走builder,當手指移出DragTarget範圍的時候會執行onLeave,完事繼續builder

沒搞懂rejectedData這個集合啥時候會有數據

  1. GestureDetector
    想給widget添加觸摸事件可以用這個,我一般也就加個點擊事件,用onTap即可

  1. Dismissible
    可以拖動的控件,最多拖動距離是控件自身的寬或者高
  const Dismissible({
    @required Key key,
    @required this.child,
    this.background,
    this.secondaryBackground,
    this.confirmDismiss,//根據返回的結果來決定是否dismiss
    this.onResize,//確定dismiss以後會回調這裏N次
    this.onDismissed,//控件徹底消失以後回調
    this.direction = DismissDirection.horizontal,//可以滑動的方向
    this.resizeDuration = const Duration(milliseconds: 300),
    this.dismissThresholds = const <DismissDirection, double>{},//閥值默認0.4也就是拖動40%的距離
    this.movementDuration = const Duration(milliseconds: 200),
    this.crossAxisEndOffset = 0.0,//偏移量,高度或者寬度【根據主軸來定】的多少倍
    this.dragStartBehavior = DragStartBehavior.start,
  })

  /// A widget that is stacked behind the child. If secondaryBackground is also
  /// specified then this widget only appears when the child has been dragged
  /// down or to the right.
//往右往上滑動的時候底層顯示的widget,如果secondary沒設置,往左往下也是這個
  final Widget background;

  /// A widget that is stacked behind the child and is exposed when the child
  /// has been dragged up or to the left. It may only be specified when background
  /// has also been specified.
//往左往下滑動的時候顯示的wiget,如果設置了這個,上邊那個background必須設置
//assert(secondaryBackground == null || background != null),
  final Widget secondaryBackground;

  /// Defines the end offset across the main axis after the card is dismissed.
  ///滑到最後橫軸的偏移量,
  /// If non-zero value is given then widget moves in cross direction depending on whether
  /// it is positive or negative.
  final double crossAxisEndOffset;

demo

              Text("place holder start",style: _defaultStyle,),
              Dismissible(
                key: Key("dismiss"),
                child: Text("Dismissible",style: TextStyle(color: Colors.deepPurple, fontSize: 25,
backgroundColor: Colors.lightBlue)),
                confirmDismiss: (dismissDirection) {
                  print('confirmDismiss===========${dismissDirection}');
                  return Future.value(true);
                },
                onDismissed: (dismissDirection) {
                  print('onDismissed===========${dismissDirection}');
                },
                crossAxisEndOffset: 2,
                background: Text("background",style: _defaultStyle,),
                secondaryBackground: Text("secondaryBackground",style: _defaultStyle,),
              ),
              Text("place holder end",style: _defaultStyle,),

看圖可以發現:
background或者secondaryBackground的寬高由child來決定的,圖上明顯少了個字母d
crossAxisEndOffset:我們這裏是水平拖動,這個偏移是垂直方向的,child高度的2倍,如果是負的,是往上偏移



異步 Widgets

  1. FutureBuilder
  const FutureBuilder({
    Key key,
    this.future,//一個future到時候返回的數據類型也是T
    this.initialData,//初始化數據,泛型T
    @required this.builder,//構建widget
  })

demo
就是個textview,初始化顯示loading,過5秒顯示success

          FutureBuilder(
            builder: (context, snapshot) {
              print('build=========${snapshot.connectionState}===${snapshot.data}');
//當然可以根據連接狀態返回不同的widget
              return Text("${snapshot.data}");
            },
            initialData: "loading",
            future: Future.delayed(Duration(seconds: 5), () {//延遲5秒模擬異步操作
              return "success";
            }),
          ),
  1. StreamBuilder
    看起來和上邊那個差不多,參數都差不多
          StreamBuilder(builder: (context,  snapshot){
            print('build=========${snapshot.connectionState}===${snapshot.data}');
            if (snapshot.hasError)
              return Text('Error: ${snapshot.error}');
            switch (snapshot.connectionState) {
              case ConnectionState.none: return Text('Select lot');
              case ConnectionState.waiting: return Text('Awaiting bids...');
              case ConnectionState.active: return Text('\$${snapshot.data}');
              case ConnectionState.done: return Text('\$${snapshot.data} (closed)');
            }
            return null; // unreachable
          },stream: _createStream(),),

  Stream<int> _createStream() {
    return Stream.periodic(Duration(seconds: 4),(computationCount){
      return computationCount;
    });
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章