Flutter Widget 最全使用介绍

Flutter widget 的设计思想跟 Android 略有不同,Flutter 中的 widget 可以用两条规则来约束:

  • 一切都是 widget
  • 每个 widget 只负责自己关注的部分

第一条意味着你所看到的东西都是由于 widget 构成,跟安卓不同的是,原本在安卓中一些参数相关的东西到了 Futter 中都被 widget 化,例如大小、背景、margin、padding 等等原本只需要一个参数设置的东西对应到 Flutter 中都映射成了 widget。
第二条的意思是,每个 widget 应该仅负责自己的职责所在,比如文本框 Text 组件,只负责如何显示一个文本,其他一概不管,不用考虑自己的大小、位置、margin 等等,这些由别的专门负责此功能的 widget 来控制。作为一个文本框就应该有文本框的觉悟。

举个栗子,我们希望显示一个文本时一个 Text 控件既可,但如果想控制它的大小、边距、位置这些属性就需要外面包一个 Container 来控制了。

关于如何安装 Flutter、创建项目等可以参照我的上一篇文章:
https://www.jianshu.com/p/a54090544662

两种 Widget

Widget 总体上分为两种:StatelessWidget 和 StatefulWidget。
StatelessWidget 表示不可变的 widget,例如一些固定的标题、Icon 等等,widget 的特征不会在运行时发生变化。
StatefulWidget 相反,其属性可能会在运行时发生变化,例如进度条、输入框等等。
在使用上,StatelessWidget 会通过 build 方法创建一个不可变的 widget,这样 widget 只需要绘制一次。
我们在使用时直接继承它然后实现 build 方法既可。

class StatelessText extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Text("Stateless");
  }
}

而 StatefulWidget 中需要包含一个 State 对象来表现不同的状态,首先使用 createState 方法创建一个 State 对象,再通过 State 中的 build 方法创建一个 widget,后面每次状态变化时都会调用 build 方法重新绘制一个 widget。我们可以使用 setState 方法来触发 widget 更新。
这种使用起来也稍微麻烦点:

class EnableButton extends StatefulWidget {
  final bool enable;
  EnableButton(this.enable);
  @override
  State<StatefulWidget> createState() {
    return ButtonState(enable);//返回一个 State 对象
  }
}

class ButtonState extends State<EnableButton> {
  bool enable;
  ButtonState(this.enable);
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
        onPressed: (){
          setState(() {
            enable = !enable;
          });
        },
        child: Text(enable ? "Enable" : "Disable",
            style: TextStyle(color: enable ? Colors.red : Colors.grey)));
  }
}

主要代码都在 ButtonState 中,我们需要在它里面通过不同的状态来返回不同的 Widget。

常用 Widget

下面介绍几种常用的 widget。

Text(文本框)

文本框是最常见的 widget,简单得很。

    Text text = Text("TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText",
        textAlign: TextAlign.start,
        textDirection: TextDirection.ltr,
        maxLines: 2,
        overflow: TextOverflow.ellipsis,
        style: TextStyle(
            fontSize: 18.0, fontWeight: FontWeight.bold, color: Colors.black));

常用参数:

  • textDirection:设置文本居中模式
  • textDirection:设置文本方向,默认为 ltr,对于一些习惯于从右至左阅读的国家来说可以设置为 rtl
  • maxLines:最大行数
  • overflow:超出文本如何处理,ellipsis 为超出部分使用 ... 显示

Image(图片)

Image 用于展示一张图片,图片源可以是文件、资源、内存及网络。通过不同的方法加载不同的图片源:

Image.network(url);
Image.asset(path);
Image.file(file);
Image.memory(bytes);
Image image = Image.asset("assets/valley.jpg",
    width: 250,
  height: 250,
  fit: BoxFit.contain);

width 和 height 是宽高,fit 是填充模式,填充模式分为如下几种类型:

  • contain:在不缩放的情况下保证至少有一个方向充满且图片完全能显示出来


  • cover:在不缩放的情况下保证至少一个方向上充满但不保证图片完全显示


  • fill:不考虑宽高比,只保证图片完全填满


  • fitHeight:保证高度填满,即使宽度溢出


  • fitWidth:保证宽度填满,即使高度溢出


  • none:不应用任何设置,按照图片原有尺寸居中


  • scaleDown:等同 none,但如果尺寸超出则缩放填满


Button(按钮)

Flutter 中有两种按钮:FlatButton 和 RaisedButton,除了 RaisedButton 多了个背景色外其它的都一样。我们看一下怎么使用:

FlatButton flatButton = FlatButton(
    onPressed: () => debugPrint("FlatButton Clicked"),
    child: Text("FlatButton"));
RaisedButton raisedButton = RaisedButton(
    onPressed: () => debugPrint("RaisedButton Clicked"),
    elevation: 10,
    color: Colors.yellow,
    textColor: Colors.black87,
    disabledColor: Colors.blue,
    focusColor: Colors.red,
    hoverColor: Colors.red,
    highlightColor: Colors.blue,
    splashColor: Colors.red,
    child: Text("RaisedButton Clicked"));

主要参数:

  • onPressed:点击事件
  • elevation:阴影高度
  • color:背景色
  • textColor:文本颜色
  • disabledColor:不可点击时的背景色
  • focusColor:按钮获取输入焦点时的颜色
  • hoverColor:鼠标悬停在按钮上时的颜色
  • highlightColor:按下的颜色
  • splashColor:点击时波纹颜色
  • child:按钮内容

除了上面的之外还有很多参数,这里就不详细介绍了。
需要注意的是,child 可以设置为 任意 widget,例如一张图片。

TextField(输入框)

TextField 也是个常用的控件,内置了丰富的参数。

  @override
  Widget build(BuildContext context) {
    ScrollController scrollController = new ScrollController();
    TextEditingController textEditingController = TextEditingController();
    TextField textField = TextField(
      controller: textEditingController,
      textAlign: TextAlign.start,
      minLines: 1,
      maxLines: 1,
      maxLength: 5,
      obscureText: true,
      autofocus: true,
      scrollController: scrollController,
      cursorColor: Colors.red,
      style: TextStyle(fontWeight: FontWeight.bold, color: Colors.blue),
      inputFormatters: [
        WhitelistingTextInputFormatter.digitsOnly,
        BlacklistingTextInputFormatter.singleLineFormatter
      ],
    );
    return Container(
      child: Column(
        children: <Widget>[
          textField,
          RaisedButton(
            onPressed: () => {debugPrint(textEditingController.text)},
            child: Text("Click"),
          )
        ],
      ),
    );
  }

常用参数:

  • controller:输入控制器
  • textAlign:对齐方式
  • minLines:最小行数
  • maxLines:最大行数
  • maxLength:最多可输入个数
  • obscureText:输入密码
  • autofocus:自动获取焦点
  • scrollController:滚动控制器
  • cursorColor:光标颜色
  • style:文本样式
  • inputFormatters:输入格式化,上述代码表示只允许输入数字及限制为单行

最后通过 textEditingController.text 即可获取输入的文本。
下面再介绍几个布局控件。

Container

这是个简单的布局控件,提供了大小、位置、背景等功能。

Container container = Container(
  width: 200,
  height: 100,
  padding: EdgeInsets.all(10.0),
  margin: EdgeInsets.only(top: 5.0, bottom: 5.0),
  decoration: BoxDecoration(color: Colors.blue,
      borderRadius: BorderRadius.circular(10.0)),
  child: Text("Text"),
);

主要参数:

  • width/height:宽高值
  • padding:内间距
  • margin:边距
  • decoration:可以设置一些可绘制的东西,例如圆角,背景色等等
  • child:子控件,也就是 Container 包装的控件

与 Container 对应的几个简单的布局还有 Padding,Center 等等。

Row/Column 及 Expanded

Row 和 Column 是线性布局,Row 表示水平布局,Column 表示垂直布局,控件将会按照一个方向上依次排序,类似于 Android 中的 LinearLayout。

var row = Row(
  mainAxisSize: MainAxisSize.max,
  mainAxisAlignment: MainAxisAlignment.center,
  crossAxisAlignment: CrossAxisAlignment.center,
  children: <Widget>[Text("Text")],
);

常用参数:

  • mainAxisSize:row 的宽度,max 表示尽可能的最大(一般等于屏幕宽度),min 表示尽可能小
  • mainAxisAlignment:水平方向上的对齐方式
  • crossAxisAlignment:竖直方向上的对齐方式,Row 的高度为最高子控件的高度

Column 与 Row 的参数一样,只不过控制的方向相反。
Expanded 表示将沾满改行所有剩余可用空间,如果该行有多个 Expanded,可以使用 flex 参数控制占用比例。

var row = Row(
  children: <Widget>[
    Expanded(flex: 1, child: Text("Text")),
    Expanded(flex: 2, child: Text("Text")),
    Text("Text"),
  ],
);

如上代码所示,两个 Expanded 将占用除了最后一个 Text 之外的所有空间,且两个 Expanded 宽度比例为 1:2。

Stack

Stack 布局支持控件堆叠,一个控件可以放在另一个控件的上面,也可以通过一些参数来调整控件的位置。

Stack stack = Stack(
  alignment: AlignmentDirectional.topCenter,
  children: <Widget>[
    Container(
      width: 300,
      height: 300,
      color: Colors.blue,
    ),
    Positioned(left: 50, top: 50, child: Text("Text")),
    Text("Text"),
  ],
);

Stack 大小同样等于最大控件的大小,所以上面代码中的 Stack 大小等于子控件 Container 大小。

常用参数:

  • alignment:子控件的对齐方式,除了 AlignmentDirectional 中内置的几种对其方式外我们还可以自己定义。规则是,Stack 中心点表示为 [0, 0],左上角 [-1, -1],右下角 [1, 1],所以 alignment 中参数的取值范围为 -1 到 1。举个栗子:假设 Stack 大小为 200 * 200,当我们设置 alignment 为 Alignment(-0.7, 0.3) 时表示,子控件的 x 坐标为 30,y 坐标也为 170.

我们还可以结合使用 Positioned 来控制子控件的位置,Positioned 中提供了 left/top/right/bottom 四个参数来控制子控件的位置。

如果觉得还不错的话,欢迎关注我的个人公众号,我会不定期发一些干货文章~