[譯]Flutter for Android Developers - Others

寫在前面

這是該系列的最後一章,都是一些不是很複雜可是在Android很經常使用的功能在Flutter中對應的解決方案。 前幾章見:html

[譯]Flutter for Android Developers - Viewsgit

[譯]Flutter for Android Developers - Intentsgithub

[譯]Flutter for Android Developers - Async UI數組

[譯]Flutter for Android Developers - Gesture Detection架構

Activity和Fragment

在Flutter中Activity和Fragment等價於什麼

  • in Androidapp

    • Activity和Fragment都是一個界面的抽象,最大的區別在於Fragment在Activity的基礎上更好的實現了模塊化,而且針對大屏的設備能實現更靈活的界面設計。
  • in Flutter框架

    • 在Flutter中Activity和Fragment都用Widget來表示。

監聽生命週期事件

  • in Androidless

    • 咱們能夠重寫Activity或者Fragment的生命週期回調方法來監聽它們的生命週期並作相應的處理。
  • in Flutteride

    • 咱們能夠利用WidgetsBindingObserver來監聽Widget的生命週期,具體怎麼使用後面有例子說明。

在Flutter中咱們能監聽的生命週期有如下幾種:模塊化

  • resumed - 應用程序處於可見狀態,而且能夠響應用戶的輸入事件。它至關於Android中Activity的onResume。
  • inactive - 應用程序處於閒置狀態而且沒有收到用戶的輸入事件。這個狀態對Android來講是沒用的,它只用於iOS。
  • paused - 應用程序處於不可見狀態,而且不可以響應用戶的輸入事件。它至關於Android中Activity的onPause。
  • suspending - 應用程序將立刻被掛起。這個狀態對iOS來講沒用。

下面的例子展現如何經過WidgetsBindingObserver來監聽一個Widget的生命週期:

import 'package:flutter/widgets.dart';

class LifecycleWatcher extends StatefulWidget {
  @override
  _LifecycleWatcherState createState() => new _LifecycleWatcherState();
}

class _LifecycleWatcherState extends State<LifecycleWatcher> with WidgetsBindingObserver {
  AppLifecycleState _lastLifecyleState;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    setState(() {
      _lastLifecyleState = state;
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_lastLifecyleState == null)
      return new Text('This widget has not observed any lifecycle changes.', textDirection: TextDirection.ltr);
    return new Text('The most recent lifecycle state this widget observed was: $_lastLifecyleState.',
        textDirection: TextDirection.ltr);
  }
}

void main() {
  runApp(new Center(child: new LifecycleWatcher()));
}
複製代碼

首先經過mixin(讀做mix in,詳情參閱mixin)的方式擴展_LifecycleWatcherState類的功能,即在定義_LifecycleWatcherState類時使用關鍵字with來聲明_LifecycleWatcherState類須要擴展WidgetsBindingObserver中的功能。這裏的with其實能夠簡單類比到Java中的implements關鍵字,目的都是爲了不多繼承帶來的問題,但又同時想利用多繼承的優勢。

接着來看initState和dispose方法,它們是State類中提供的方法。它們其實自己也是兩個生命週期的回調。 系統在State對象被建立好以後而且其對應的Widget已經被插入到Widget Tree中時調用initState方法。因此咱們能夠在initState方法中完成一些初始化的工做。好比這個例子咱們在initState中就經過WidgetsBinding.instance獲取到WidgetsBinding實例後調用其addObserver方法來註冊一個WidgetsBindingObserver。 系統在State對象對應的Widget從Widget Tree中永久的刪除後調用dispose方法。一個State對象調用dispose方法以後它被認爲處於一個unmounted狀態,這時候State對象的mounted屬性值返回false。在這個時候去調用State的setState方法將觸發一個錯誤。一個處於unmounted狀態的State對象無法再回到remounted狀態。因此咱們能夠在dispose方法中完成一些資源的釋放工做,好比這個例子中咱們就經過WidgetsBinding.instance獲取到WidgetsBinding實例後調用其removeObserver方法來註銷以前註冊的WidgetsBindingObserver。

如今咱們已經在initState中註冊好了WidgetsBindingObserver,因此在Widget的生命週期發生變化時系統就會調用WidgetsBindingObserver的didChangeAppLifecycleState方法來通知咱們,所以只要重寫這個方法來實現咱們在收到生命週期狀態改變的通知時須要處理的邏輯就能夠了。在這裏就是簡單的保存狀態,而且經過setState方法觸發界面刷新。

小結: 在Flutter中除了State自己提供的生命週期回調方法initState和dispose外,還能夠經過WidgetsBindingObserver類來幫助咱們實現Widget生命週期的監聽。具體使用方式是經過with關鍵字擴展WidgetsBindingObserver類來爲咱們定義的組件類提供監聽生命週期的能力。

佈局

LinearLayout等價於什麼

  • in Android

    • LinearLayout用於將咱們的組件水平或垂直的線性排列。
  • in Flutter

    • 咱們用Row Widget或者Column Widget實現與LinearLayout相同的效果。

下面的代碼段展現了Row和Column的簡單使用:

override
Widget build(BuildContext context) {
  return new Row(
    mainAxisAlignment: MainAxisAlignment.center,
    children: <Widget>[
      new Text('Row One'),
      new Text('Row Two'),
      new Text('Row Three'),
      new Text('Row Four'),
    ],
  );
}
複製代碼
override
Widget build(BuildContext context) {
  return new Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: <Widget>[
      new Text('Column One'),
      new Text('Column Two'),
      new Text('Column Three'),
      new Text('Column Four'),
    ],
  );
}
複製代碼

Row和Column的使用比較簡單,這裏的代碼段分別經過Row和Column實現了四個Text在水平方向和垂直方向上的簡單排列。 Row和Column的使用基本相同,可是有一些參數在它們中表示的意義是有所區別的,好比這裏的mainAxisAlignment,它表示的是主軸的對齊方式,針對Row而言,主軸是水平軸。而針對Column而言,主軸是垂直軸。關於Flutter中佈局的更多信息能夠參閱官方文檔。這篇官方文檔對Flutter中的不少佈局都作了說明,簡單明瞭,若是你對Flutter中的佈局比較疑惑,看完以後應該能解開你大部分的謎團。

小結: 在Flutter中咱們使用Row和Column來實現Android的LinearLayout佈局。

RelativeLayout等價於什麼

  • in Android

    • RelativeLayout用於將咱們的組件按照他們彼此之間的相互關係進行排列。
  • in Flutter

    • 咱們能夠結合Column,Row和Stack實現與RelativeLayout相同的效果。

在StackOverflow上有一個在Flutter中實現RelativeLayout的例子[https://stackoverflow.com/questions/44396075/equivalent-of-relativelayout-in -flutter](https://stackoverflow.com/questions/44396075/equivalent-of-relativelayout-in -flutter),代碼不算複雜,主要是利用自定義Widget,Widget佈局參數和佈局的嵌套來實現RelativeLayout。

小結: 在Flutter中咱們使用Column,Row和Stack等Widget的組合來實現RelativeLayout。

ScrollView等價於什麼

  • in Android
    • ScrollView做爲一個容納組件的容器,可以在要展現的內容超出屏幕範圍時實現滾動效果。
  • in Flutter
    • 最簡單的實現方式就是使用ListView Widget。
@override
Widget build(BuildContext context) {
  return new ListView(
    children: <Widget>[
      new Text('Row One'),
      new Text('Row Two'),
      new Text('Row Three'),
      new Text('Row Four'),
    ],
  );
}
複製代碼

上面的代碼片斷使用ListView來實現Android中ScrollView的效果。將四個Text Widget組成一個數組賦值給ListView的children參數,實現了四個Text的垂直排列,而且當內容超出屏幕範圍時經過ListView提供了滾動效果。

小結: 在Flutter中可使用ListView Widget來實現Android中ScrollView的效果。

ListView & Adapter

在Flutter中簡單使用ListView來渲染一個列表

  • in Android

    • 咱們一般構造一個Adapter,並將它傳遞給ListView,由ListView來渲染Adapter返回的每一行內容。可是咱們必須本身管理ListView中的列表項,包括複用和及時的釋放等,不然會帶來意想不到的內存問題。
  • in Flutter

    • 因爲Flutter的Widget被設計爲不可變的,因此咱們只要傳遞一個用於表示每行列表項的Widget數組給ListView就好了,Flutter將完成剩下的工做並保證整個滑動的流暢性。

其實在本文前面的一節使用ListView實現ScrollView的時候已經看到了如何簡單的使用ListView,無非就是傳遞給ListView的children參數一個表示列表項的數組。下面展現一個完整的例子:

import 'package:flutter/material.dart';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sample App"),
      ),
      body: new ListView(children: _getListData()),
    );
  }

  _getListData() {
    List<Widget> widgets = [];
    for (int i = 0; i < 100; i++) {
      widgets.add(new Padding(padding: new EdgeInsets.all(10.0), child: new Text("Row $i")));
    }
    return widgets;
  }
}
複製代碼

這個例子與前面實現ScrollView的例子很類似,經過_getListData方法返回一個Widget的數組傳遞給ListView的children參數。這種使用ListView的方式實際上是比較低效的,由於ListView的children參數被咱們賦值爲一個表示列表項的數組,在本例中這個數組中就表示100個Widget,也就是說系統確實會爲ListView生成100個表示其列表項的Widget。這有點相似在Android中不使用ViewHolder直接使用Adapter。在後面咱們會講到更高效的使用ListView的方法。

小結: 在Flutter中要使用ListView來渲染一個列表最簡單直接的方式就是構造一個ListView,而且傳遞給它一個描述每一個列表項的Widget數組。剩下的工做交給Flutter就能夠了。

處理列表項的點擊事件

  • in Android

    • 咱們能夠經過ListView的onItemClickListener方法得知用戶點擊的列表項是哪個。
  • in Flutter

    • 由於咱們傳遞給ListView的就是包含全部列表項Widget的數組,因此咱們能夠直接處理數組中的每一個列表項Widget的點擊事件來實現與onItemClickListener相似的效果。

下面看一個簡單的例子:

import 'package:flutter/material.dart';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sample App"),
      ),
      body: new ListView(children: _getListData()),
    );
  }

  _getListData() {
    List<Widget> widgets = [];
    for (int i = 0; i < 100; i++) {
      widgets.add(new GestureDetector(
        child: new Padding(
            padding: new EdgeInsets.all(10.0),
            child: new Text("Row $i")),
        onTap: () {
          print('row tapped');
        },
      ));
    }
    return widgets;
  }
}
複製代碼

這個例子在前一個簡單列表項展現的例子基礎上作了一點點的改動實現列表項點擊事件的監聽處理。 在_getListData方法中,構造每個列表項時使用GestureDetector來包裹Text爲其實現onTap點擊事件的監聽(關於事件的監聽處理可參閱FFAD-Gesture Detection)。也就是說這裏構造的100個列表項在構造的時候同時就爲它們設置好了各自的點擊事件監聽的處理邏輯。在點擊ListView中每個列表項時將直接調用它們本身的事件處理方法。

小結: 在Flutter中處理ListView列表項的點擊事件時,能夠爲每一個列表項設置本身的事件監聽,由它們本身來監聽處理本身的點擊事件。

怎樣動態更新ListView

  • in Android

    • 咱們能夠更新Adapter中的數據內容並調用其notifyDataSetChanged方法來實現數據運行時的動態更新。
  • in Flutter

    • 與全部Flutter中的Widget更新同樣,咱們依然經過修改State來實現Widget的更新。即在setState中去更新傳遞給ListView的列表項數組。

可是當咱們只是簡單的在setState方法中往列表項數組中添加或者刪除列表項時,會發現界面並無刷新。這是由於setState方法的調用會致使Flutter的渲染引擎開始遍歷全部的Widgets去確認它們是否有變化,只有有變化的Widget纔會被Flutter從新渲染刷新。當遍歷到ListView的children時會經過==operator方法去比較先後兩個ListView的children,該方法的比較邏輯相似Java中直接使用等號比較,比較的是對象引用,因此若是直接向老的列表項數組插入或者刪除列表項,數組自己的引用是沒有改變的,Flutter會認爲先後兩個ListView的children是相同的沒有發生變化。因此致使界面沒有刷新。

爲了可以實現ListView的更新咱們須要在setState中建立一個新的列表項數組實例,而且將列表項數據從老的數組拷貝到新的數組中。而後再在新的數組中添加或者刪除列表項,而不是像上面說的那樣直接更新老的列表項數組。下面是一個實現動態更新的例子:

import 'package:flutter/material.dart';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List widgets = [];

  @override
  void initState() {
    super.initState();
    for (int i = 0; i < 100; i++) {
      widgets.add(getRow(i));
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sample App"),
      ),
      body: new ListView(children: widgets),
    );
  }

  Widget getRow(int i) {
    return new GestureDetector(
      child: new Padding(
          padding: new EdgeInsets.all(10.0),
          child: new Text("Row $i")),
      onTap: () {
        setState(() {
          widgets = new List.from(widgets);
          widgets.add(getRow(widgets.length + 1));
          print('row $i');
        });
      },
    );
  }
}
複製代碼

上面的例子實現每點擊列表項就在ListView中增長一條記錄的效果。 在initState方法中初始化存放列表項的數組widgets。而後監聽每一個列表項的點擊事件,在監聽處理函數中經過調用setState來觸發界面更新。 特別注意的是在setState方法內部首先是基於舊的widgets構造一個新的widgets數組實例,而後再往新構造的widgets數組中添加一條記錄以此來實現有效的動態更新。

到目前爲止咱們使用ListView的方式都很簡單,首先咱們構造列表項數組,而後將列表項數組傳遞給ListView的children參數來展現列表項數組中的Widget。然而當咱們列表項的數量很是龐大時,建議利用ListView.Builder來提升ListView的效率。由於它可以像Android中的RecyclerView同樣幫助咱們自動的重用的釋放列表項。

下面的例子展現了怎樣經過ListView.builder來高效使用ListView:

import 'package:flutter/material.dart';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List widgets = [];

  @override
  void initState() {
    super.initState();
    for (int i = 0; i < 100; i++) {
      widgets.add(getRow(i));
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("Sample App"),
        ),
        body: new ListView.builder(
            itemCount: widgets.length,
            itemBuilder: (BuildContext context, int position) {
              return getRow(position);
            }));
  }

  Widget getRow(int i) {
    return new GestureDetector(
      child: new Padding(
          padding: new EdgeInsets.all(10.0),
          child: new Text("Row $i")),
      onTap: () {
        setState(() {
          widgets.add(getRow(widgets.length + 1));
          print('row $i');
        });
      },
    );
  }
}
複製代碼

與以前咱們直接構造ListView不一樣的是這裏咱們建立了一個ListView.builder,並傳遞給它兩個參數,itemCount表示列表項的長度,itemBuilder是一個方法,它很像Android中Adapter的getView方法,itemBuilder方法有一個表示列表項位置的參數position,而後返回這個位置對應的列表項。

另外這裏看到咱們並無像以前使用ListView那樣直接對ListView的children賦值,也就是說使用ListView.builder的方式來構造ListView時,ListView的children並非在構造時靜態寫死的,而是在運行時動態更新的。也正是由於這個緣由,因此這個例子中實現動態更新時setState方法中並不須要從新構造列表項數組了。

其實再細心點你會發現利用ListView.builder的方式來構造ListView不少地方跟Android中ListView和Adapter的搭配很像。好比itemCount參數和itemBuilder參數,itemCount很像Adapter的getCount方法,itemBuilder很像Adapter的getView方法。再好比widgets數組,你會發現這個例子中widgets數組中的元素其實徹底能夠不是Widget類型,它們能夠是任何表示數據內容的實體類型,就相似Adapter中承載的數據集同樣。每個列表項由itemBuilder方法動態構造出來,而widgets數組這裏的做用無非就是提供每一個列表項要承載的內容數據。

小結: 在Flutter中建議經過ListView.builder來使用ListView。由於經過ListView.builder的方式系統可以幫助咱們自動完成列表項的釋放或重用等工做,它是一個更加高效的選擇。 ListView的動態更新關鍵在於其children是否有變化,當直接構造ListView時因爲咱們靜態的將一個表示列表項的數組賦值給其children,因此在setState時咱們須要手動去從新建立列表項數組,以保證先後的對象引用不一樣。當使用ListView.builder的方式實現ListView時其children是在運行時動態生成的,因此在setState時咱們無需從新構造列表項數組。其實這時候是否從新構造一個數組已經不重要了,由於列表項數組已經不是表示列表項了,它內部的元素是表示列表項須要承載的內容而已。

關於Text

Text樣式

  • in Android

    • 咱們能夠建立一個字體資源文件,而後把它傳遞給TextView的FontFamily參數來實現自定義TextView的字體。
  • in Flutter

    • Text Widget等價於Android中的TextView。咱們能夠經過TextStyle類來實現自定義字體。

首先咱們要在項目中建立字體文件(最好是建立一個assets目錄,將字體文件放在該目錄中)。而後在pubspec.yaml中聲明咱們須要使用的字體:

fonts:
 - family: MyCustomFont
 fonts:
 - asset: fonts/MyCustomFont.ttf
 - style: italic
複製代碼

上面須要注意的是第2行family的配置,後面Text Widget中使用字體的時候就是經過這裏配置的family name來指定。 接下來就能夠在Text中使用字體了:

@override
Widget build(BuildContext context) {
  return new Scaffold(
    appBar: new AppBar(
      title: new Text("Sample App"),
    ),
    body: new Center(
      child: new Text(
        'This is a custom font text',
        style: new TextStyle(fontFamily: 'MyCustomFont'),
      ),
    ),
  );
}
複製代碼

上面的代碼片斷在構造Text時除了要顯示的內容外還傳入了一個style參數,該參數是一個TextStyle類型,構造TextStyle類型時經過指定前面配置的family name來指定咱們要使用的字體。

除了自定義字體外咱們還能夠爲Text Widget自定義不少樣式。構造TextStyle時提供了不少參數供咱們使用,好比:

  • color
  • decoration
  • decorationColor
  • decorationStyle
  • fontFamily
  • fontSize
  • fontStyle
  • fontWeight
  • hashCode
  • height
  • inherit
  • letterSpacing
  • textBaseline
  • wordSpacing

小結: 在Flutter中咱們經過構造一個TextStyle對象來描述一個樣式,並將其傳遞給Text Widget將咱們指定的樣式應用到該Text Widget上。

關於TextField

實現輸入框的hint

  • in Android

    • hint就是EditText的一個屬性,咱們直接設置該屬性來實現輸入框在無輸入狀態時顯示的默認文本。
  • in Flutter

    • TextField Widget用於表示一個讓用戶輸入文本的控件,咱們能夠經過InputDecoration類簡單的實現Android中hint的效果。
body: new Center(
  child: new TextField(
    decoration: new InputDecoration(hintText: "This is a hint"),
  )
)
複製代碼

這個例子構造了一個InputDecoration對象,在構造時能夠傳入一個hintText參數,該參數則表示輸入框無輸入狀態時顯示的默認內容。最後將構造好的InputDecoration對象傳遞給TextField的decoration參數使得TextField具備hint功能。

小結: 在Flutter中經過TextField來表示一個讓用戶輸入文本的控件。首先構造一個InputDecoration對象來描述hint的內容,接着在構造TextField時傳遞該InputDecoration對象來實現TextField的hint功能。

提示輸入內容錯誤

就像咱們實現hint同樣,一樣經過InputDecoration來實現提示輸入內容錯誤的效果,在構造InputDecoration時傳入另外一個errorText參數,該參數描述的一個文本信息會被當作錯誤信息展現給用戶。利用該參數咱們就能夠實如今用戶輸入內容錯誤時提示用戶的效果,以下例:

import 'package:flutter/material.dart';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  String _errorText;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sample App"),
      ),
      body: new Center(
        child: new TextField(
          onSubmitted: (String text) {
            setState(() {
              if (!isEmail(text)) {
                _errorText = 'Error: This is not an email';
              } else {
                _errorText = null;
              }
            });
          },
          decoration: new InputDecoration(hintText: "This is a hint", errorText: _getErrorText()),
        ),
      ),
    );
  }

  _getErrorText() {
    return _errorText;
  }

  bool isEmail(String em) {
    String emailRegexp =
        r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';

    RegExp regExp = new RegExp(p);

    return regExp.hasMatch(em);
  }
}
複製代碼

這個例子在用戶完成輸入時去驗證用戶的輸入內容是不是一個郵箱地址格式,若不是則提示用戶。

首先仍是經過TextField來實現一個輸入框,傳遞一個方法給其onSubmitted參數,該方法會在用戶完成內容輸入時回調(好比按下軟鍵盤上的回車鍵)。咱們在這個回調中經過調用setState來觸發界面更新。在setState中咱們更新的是_errorText成員變量,它由用戶輸入的內容決定。 同時還傳遞給TextField一個InputDecoration對象,InputDecoration對象在構造時除了以前用過的hintText參數外咱們還將_errorText的值傳遞給其errorText參數。_errorText是在setState方法中動態改變的,當用戶輸入的內容驗證是一個郵件地址時,_errorText被賦值爲null,此時TextField不會顯示錯誤提示。當用戶輸入的內容驗證錯誤時則_errorText被賦值爲Error: This is not an email,此時TextField會將該文本做文錯誤提示展現給用戶。

這個例子是經過TextField的onSubmitted回調來觸發檢查的,咱們還可使用TextField的另外一個回調onChanged來觸發檢查,onChanged會在輸入內容發生改變時便當即回調。關於TextField的更多信息能夠參閱官方文檔。另外構造InputDecoration時可傳入的參數也還有不少,關於InputDecoration的更多信息能夠參閱官方文檔

小結: InputDecoration正如其名字同樣,是一個裝飾類。利用InputDecoration類能夠爲TextField定製不少裝飾效果。

Flutter插件

經常使用插件

更多插件查閱pub.dartlang.org/packages

自定義插件

若是在開發社區或者Flutter框架都沒有提供咱們想要的功能的插件,咱們能夠自定義插件。關於自定義插件的詳情能夠參閱官方文檔

簡而言之Flutter的插件架構有點相似在Android中使用Event Bus:觸發一個消息給接收者,讓接收者處理以後返回一個結果給咱們。在這裏接收者指的就是iOS層或者Android層。

在Flutter中使用NDK

咱們能夠經過自定義一個插件來實如今Flutter應用中調用native libraries的功能。 咱們自定義的插件首先須要和Android層通訊,在Android層能夠調用native方法。一旦調用完成,再發送一個消息回Flutter層去處理。

小結: 在Flutter中咱們能夠經過使用現有的插件來實現不少與平臺層通訊的功能(Android層或者iOS層)。當現有的插件沒法知足咱們的需求時咱們也能夠定義本身的插件。

相關文章
相關標籤/搜索