flutter学习笔记(一)
flutter控件和布局
这篇博客记录一下flutter控件使用方法和常见控件库及布局方法。(吐槽一下:flutter加个边距或居中效果都得嵌套一层container之类的控件,这点不是很酷)。Flutter中一切都是widget.widget又分为无状态的控件(StatelessWidget)和有状态的控件(StatefulWidget)。
无状态的控件(StatelessWidget)
使用无状态的控件,需要继承StatelessWidget类,实现build方法,在build方法中返回一个布局.这个布局就是将来要展示在屏幕上的布局
常用的用来构建布局的类:
类名 | 使用示例 | 注释 |
---|---|---|
Text | new Text(‘Hello World’, style: new TextStyle(fontSize: 32.0)) | 创建一个显示文本的控件 |
Image | new Image.asset(‘images/myPic.jpg’, fit: BoxFit.cover) | 从images目录下读取一个图片显示出来 |
Icon | new Icon(Icons.star, color: Colors.red[500]) | 创建一个小图标 |
Center | new Center(child: new Text(‘Hello World’, style: new TextStyle(fontSize: 32.0)) | 让一个Text控件居中显示 |
Row | new Row(); | 创建一个行来使控件横向展示 |
Column | new Column() | 创建一个列,让子控件纵向显示 |
控件常用的属性
属性名 | 作用 | 注释 |
---|---|---|
child | 为当前的容器指定一个子控件 | 并不是每一个控件都有这个属性,container,Center,BoxDecoration等控件有这个属性 |
children | 为当前控件指定多个子控件 | 并不是每一个控件都有这个属性,ListView, GridView,Row,Column等控件有这个属性 |
mainAxisSize | 设置主轴方向的大小,如对Row来说主轴就是横向的,对column来说主轴就是纵向的 | 常用的值包括MainAxisSize.min, MainAxisSize.max等 |
crossAxisSize | 设置副轴方向大小,如对Row来说副轴就是纵向的,对column来说副轴就是横向的 | 常用的值包括MainAxisSize.min, MainAxisSize.max等 |
mainAxisAlignment | 设置主轴方向的对齐方式 | 常用的值包括MainAxisAlignment.spaceEvenly,MainAxisAlignment.start,MainAxisAlignment.end, MainAxisAlignment.center等 |
有状态的控件(StatefulWidget)
自定义有状态的控件需要继承StatefulWidget类,实现build方法,在build方法中返回所要展示的控件效果.控件的不同状态效果是通过state来保存的.当控件的state发生改变后,可以通过setState()方法通知控件状态已经发生改变,需要重新绘制,类似于Android中的invalidate().
这里记录一个笔者遇到的坑:
在写一个有点击效果的button时,在onPressed属性后面设置点击事件时写的方法名后面带了括号,结果导致按钮无法响应点击事件,错误代码如下
IconButton(
onPressed: _toggleFavorite(),
);
正确的代码如下
IconButton(
onPressed: _toggleFavorite,
);
StatefulWidget的状态管理
- 子控件自己管理
- 父控件管理
- 子控件和父控件混合管理
子控件自己管理
请查看代码中的注释
class TapBoxA extends StatefulWidget {//TapBox是一个子控件
@override
State<StatefulWidget> createState() => _TapBoxAState();
}
class _TapBoxAState extends State<TapBoxA> {//TapBox用这个类来管理自己的状态
bool _isActive = false;
@override
Widget build(BuildContext context) {
return new GestureDetector(
onTap: _handleTap,
child: new Container(
child: new Center(
child: new Text(_isActive ? "激活" : '未激活',
style: new TextStyle(fontSize: 32.0, color: Colors.white),
),
),
width: 200,
height: 200,
decoration: new BoxDecoration(
color: _isActive ? Colors.lightGreen : Colors.grey,
),
),
);
}
void _handleTap() {
setState(() {//当状态发生变化时调用setState()来刷新控件状态
_isActive = !_isActive;
});
}
}
父控件管理
可以通过注释中的数字来查看调用链条
class ParentWidget extends StatefulWidget {//父控件
@override
State<StatefulWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {//父控件状态管理类
bool _active = false;
_handleTapboxChanged(bool newValue) {
setState(() {//父控件通过setState方法刷新界面,调用后会重走build()方法
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
return Container(
child: new TapBoxB(
active: _active,
onChanged: _handleTapboxChanged,//3 父控件收到点击事件后调用_handleTapboxChanged方法来处理
));
}
}
class TapBoxB extends StatelessWidget {//子控件,因为不需要自己管理状态所以继承StatelessWidget
bool active;
ValueChanged<bool> onChanged;
TapBoxB({Key key, this.active, this.onChanged}) : super(key: key);
@override
Widget build(BuildContext context) {
return new GestureDetector(
onTap: _handleTap,//1 设置点击事件
child: new Container(
child: new Center(
child: new Text(
active ? 'Active' : 'Inactive',
style: new TextStyle(fontSize: 32.0, color: Colors.white),
),
),
width: 200.0,
height: 200.0,
decoration: new BoxDecoration(
color: active ? Colors.lightGreen[700] : Colors.grey[600],
),
),
);
}
void _handleTap() {
onChanged(!active);//2 发生点击事件时通过onChanged方法传递给父控件
}
}
混合管理
请阅读代码中的注释,根据注释中的数字来梳理调用链条
class ParentWidget extends StatefulWidget {//父控件
@override
State<StatefulWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {//父控件状态管理类
bool _active = false;
@override
Widget build(BuildContext context) {
return new Container(
child: new TapBoxC(active: _active, onChanged: _handleTapboxCChanged),//2 父控件收到点击事件后,通过_handleTapboxCChanged处理
);
}
void _handleTapboxCChanged(bool value) {
setState(() {//3 设置_active状态,重走build方法
_active = value;
});
}
}
class TapBoxC extends StatefulWidget {//子控件
bool active;
ValueChanged<bool> onChanged;
TapBoxC({Key key, this.active, this.onChanged}) : super(key: key);
@override
State<StatefulWidget> createState() => _TapBoxCState();
}
class _TapBoxCState extends State<TapBoxC> {//子控件状态管理类
bool _highlight = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: _handleTapDown,//处理按下事件,最先发生
onTapUp: _handleTapUp,//处理手指抬起事件,随后发生
onTap: _handleTap,//处理点击事件,最后发生
onTapCancel: _handleTapCancel,
child: new Container(
child: new Center(
child: new Text(widget.active ? 'active' : 'inactive',
style: new TextStyle(fontSize: 32.0, color: Colors.white)),
),
width: 200,
height: 200,
decoration: new BoxDecoration(
color: widget.active ? Colors.lightGreen : Colors.grey,
border: _highlight
? new Border.all(color: Colors.teal, width: 10)
: null),
),
);
}
void _handleTapDown(TapDownDetails details) {
setState(() {//手指按下,_highlight 设置为true,build()方法中new Border显示
_highlight = true;
});
}
void _handleTapUp(TapUpDetails details) {
setState(() {//手指抬起,_highlight 设置为false,build()方法中new Border隐藏
_highlight = false;
});
}
void _handleTap() {
widget.onChanged(!widget.active);//1 将点击事件通过onChanged传递给父控件处理,同时将当前控件的active状态作为参数传递出去
}
void _handleTapCancel() {
setState(() {
_highlight = false;
});
}
}