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 四个参数来控制子控件的位置。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270