Flutter widget 的设计思想跟 Android 略有不同,Flutter 中的 widget 可以用两条规则来约束:
第一条意味着你所看到的东西都是由于 widget 构成,跟安卓不同的是,原本在安卓中一些参数相关的东西到了 Futter 中都被 widget 化,例如大小、背景、margin、padding 等等原本只需要一个参数设置的东西对应到 Flutter 中都映射成了 widget。
第二条的意思是,每个 widget 应该仅负责自己的职责所在,比如文本框 Text 组件,只负责如何显示一个文本,其他一概不管,不用考虑自己的大小、位置、margin 等等,这些由别的专门负责此功能的 widget 来控制。作为一个文本框就应该有文本框的觉悟。
举个栗子,我们希望显示一个文本时一个 Text 控件既可,但如果想控制它的大小、边距、位置这些属性就需要外面包一个 Container 来控制了。
关于如何安装 Flutter、创建项目等可以参照我的上一篇文章:
https://blog.csdn.net/u013872857/article/details/103016567
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 createState() {
return ButtonState(enable);//返回一个 State 对象
}
}
class ButtonState extends State {
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 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));
常用参数:
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 是填充模式,填充模式分为如下几种类型:
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"));
主要参数:
除了上面的之外还有很多参数,这里就不详细介绍了。
需要注意的是,child 可以设置为 任意 widget,例如一张图片。
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: [
textField,
RaisedButton(
onPressed: () => {debugPrint(textEditingController.text)},
child: Text("Click"),
)
],
),
);
}
常用参数:
最后通过 textEditingController.text 即可获取输入的文本。
下面再介绍几个布局控件。
这是个简单的布局控件,提供了大小、位置、背景等功能。
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"),
);
主要参数:
与 Container 对应的几个简单的布局还有 Padding,Center 等等。
Row 和 Column 是线性布局,Row 表示水平布局,Column 表示垂直布局,控件将会按照一个方向上依次排序,类似于 Android 中的 LinearLayout。
var row = Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [Text("Text")],
);
常用参数:
Column 与 Row 的参数一样,只不过控制的方向相反。
Expanded 表示将沾满改行所有剩余可用空间,如果该行有多个 Expanded,可以使用 flex 参数控制占用比例。
var row = Row(
children: [
Expanded(flex: 1, child: Text("Text")),
Expanded(flex: 2, child: Text("Text")),
Text("Text"),
],
);
如上代码所示,两个 Expanded 将占用除了最后一个 Text 之外的所有空间,且两个 Expanded 宽度比例为 1:2。
Stack 布局支持控件堆叠,一个控件可以放在另一个控件的上面,也可以通过一些参数来调整控件的位置。
Stack stack = Stack(
alignment: AlignmentDirectional.topCenter,
children: [
Container(
width: 300,
height: 300,
color: Colors.blue,
),
Positioned(left: 50, top: 50, child: Text("Text")),
Text("Text"),
],
);
Stack 大小同样等于最大控件的大小,所以上面代码中的 Stack 大小等于子控件 Container 大小。
常用参数:
我们还可以结合使用 Positioned 来控制子控件的位置,Positioned 中提供了 left/top/right/bottom 四个参数来控制子控件的位置。