[译]Flutter for Android Developers - Others

写在前面

这是该系列的最后一章,都是一些不是很复杂但是在Android很常用的功能在Flutter中对应的解决方案。 前几章见:

[译]Flutter for Android Developers - Views

[译]Flutter for Android Developers - Intents

[译]Flutter for Android Developers - Async UI

[译]Flutter for Android Developers - Gesture Detection

Activity和Fragment

在Flutter中Activity和Fragment等价于什么

  • in Android

    • Activity和Fragment都是一个界面的抽象,最大的区别在于Fragment在Activity的基础上更好的实现了模块化,并且针对大屏的设备能实现更灵活的界面设计。
  • in Flutter

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

监听生命周期事件

  • in Android

    • 我们可以重写Activity或者Fragment的生命周期回调方法来监听它们的生命周期并做相应的处理。
  • in Flutter

    • 我们可以利用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层)。当现有的插件无法满足我们的需求时我们也可以定义自己的插件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值