[译]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层)。当现有的插件没法知足咱们的需求时咱们也能够定义本身的插件。

相关文章
相关标签/搜索