Padding组件
在HTML中,常见的布局标签都有padding属性,但是在Flutter中,很多的widget是没有padding属性的。这时我们就可以使用padding组件来处理容器与子元素之间的间距。
Padding组件有两个属性:
先来看下面一段代码:
class HomeContent extends StatelessWidget {
List<Widget> _getChildren(){
var children = listData.map((value){
return Image.network(value["imgUrl", fit: BoxFit.cover,);
});
return children.toList();
}
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 3,
children: this._getChildren(),
);
}
}
这段代码的运行效果如下:
可以看到,GridView的子元素是Image。现在我们想给图片组件加一些内边距,但是Image组件是没有padding属性的,这个时候该怎么办呢?我们可以使用Padding组件。代码如下:
class HomeContent extends StatelessWidget {
List<Widget> _getChildren() {
var children = listData.map((value) {
return Padding(//Padding组件
child: Image.network(
value["imgUrl"],
fit: BoxFit.cover,
),
padding: EdgeInsets.fromLTRB(10, 10, 0, 0),
);
});
return children.toList();
}
@override
Widget build(BuildContext context) {
return Padding(//Padding组件
padding: EdgeInsets.fromLTRB(0, 0, 10, 10),
child: GridView.count(
crossAxisCount: 3,
children: this._getChildren(),
),
);
}
}
效果如下:
对于一些没有padding属性的组件,比如Image,我们可以在其外部包一个Padding组件,来实现内边距padding的效果。
如何自定义一个组件
自定义一个组件无非就是三步:
下面是自定义一个名为IconContainer的组件:
class IconContainer extends StatelessWidget {
//声明属性
Color bgColor = Colors.yellow;
double iconSize = 16;
IconData iconname = Icons.home;
//构造方法(使用大括号包起来的都是可选命名参数)
IconContainer({this.bgColor, this.iconname, this.iconSize});
//build方法
@override
Widget build(BuildContext context) {
return Container(
height: 100,
width: 100,
color: this.bgColor,
child: Center(
child: Icon(this.iconname, size: this.iconSize, color: Colors.white,),
),
);
}
}
Row 水平布局组件
Row组件有如下三个属性:
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 300,
height: 500,
color: Colors.pink,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, //调整主轴的排列方式(常用)
crossAxisAlignment: CrossAxisAlignment.center, //调整交叉轴的排列方式(用的比较少)
children: <Widget>[
IconContainer(Colors.green, Icons.date_range, 30),
IconContainer(Colors.red, Icons.edit_location, 35),
IconContainer(Colors.yellow, Icons.fast_rewind, 40),
],
),
);
}
}
效果如下:
Column 垂直布局组件
Colume组件与Row组件的使用方式一模一样:
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 300,
height: 500,
color: Colors.pink,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, //调整主轴的排列方式(常用)
crossAxisAlignment: CrossAxisAlignment.end, //调整交叉轴的排列方式(用的比较少)
children: <Widget>[
IconContainer(Colors.green, Icons.date_range, 30),
IconContainer(Colors.red, Icons.edit_location, 35),
IconContainer(Colors.yellow, Icons.fast_rewind, 40),
],
),
);
}
}
效果如下:
Expanded 组件
Expanden组件可以用在Row和Column布局中,有如下两个属性:
Row(
children: <Widget>[
Expanded(
flex: 1,
child: IconContainer(Colors.pink, Icons.fast_rewind, 30),
),
Expanded(
flex: 2,
child: IconContainer(Colors.orange, Icons.hdr_strong, 40),
),
Expanded(
flex: 1,
child: IconContainer(Colors.blue, Icons.screen_lock_rotation, 50),
),
],
);
效果如下:
此外,我们也可以使用Expanded组件实现宽度或者高度的自适应。如下所示:
Row(
children: <Widget>[
Expanded(
flex: 1,
child: IconContainer(Colors.pink, Icons.fast_rewind, 30),
),
IconContainer(Colors.orange, Icons.hdr_strong, 40),
],
);
效果如下:
其他
可以使用SizedBox组件来定义组件的间隔。
Stack
Stack是堆的意思,有如下两个参数:
Stack组件可以单独使用。当其子元素只有一个,或者只有少数个元素并且这些子元素的布局是统一的,此时就可以 单独使用Stack进行布局。如下所示:
Container(
height: 400,
width: 300,
color: Colors.yellow,
child: Stack(
alignment: Alignment(0, 0),//这里的效果是,使children里面d额所有元素都居中展示
children: <Widget>[
Image.network(
"http://pic27.nipic.com/20130325/11918471_071536564166_2.jpg"),
Text(
"666888",
style: TextStyle(color: Colors.white, fontSize: 20),
),
],
),
);
效果如下:
但一个容器中有多个元素,而这些元素各有各自不同的布局的时候,可以使用Stack结合Align或者Stack结合Positioned来实现。
Stack & Align
Container(
height: 400,
width: 300,
color: Colors.yellow,
child: Stack(
alignment: Alignment(0, 0),
children: <Widget>[
Align(
alignment: Alignment.topLeft,
child: IconContainer(Colors.red, Icons.aspect_ratio, 30),
),
Align(
alignment: Alignment.topRight,
child: IconContainer(Colors.blue, Icons.sd_card, 40),
),
Align(
child: IconContainer(Colors.green, Icons.data_usage, 50),
),
],
),
);
通过Stack结合ALign可以控制Stack组件内部的每个元素的具体位置。Stack的alignment可以控制所有子元素的布局,Align的alignment属性可以控制某个具体子元素的布局,如果两者同时存在,则以Align的alignment属性为准。上述代码的效果如下:
Stack & Positioned
Container(
height: 200,
width: 300,
color: Colors.yellow,
child: Stack(
children: <Widget>[
Align(
alignment: Alignment.center,
child: Container(
child: Image.network(
"http://pic27.nipic.com/20130325/11918471_071536564166_2.jpg",
fit: BoxFit.cover,
),
),
),
Positioned(
left: 10,
bottom: 10,
right: 10,
child: Text(
"http://pic27.nipic.com/20130325/11918471_071536564166_2.jpg",
style: TextStyle(color: Colors.white, fontSize: 18),
),
)
],
),
);
效果图如下:
AspectRatio
AspectRatio的作用是根据设置调整子元素child的宽高比。
AspectRatio首先会在布局限制条件允许的范围内尽可能地扩展,Widget的宽度(高度)由高度(宽度)和比率决定,类似于BoxFit中的contain,按照固定比率去尽量占满区域。
AspectRatio有如下两个属性:
如下代码:
Container(
width: 300,
child: AspectRatio(
aspectRatio: 2,
child: Container(
color: Colors.red,
),
),
);
展示效果为:
Card
Card是卡片组件块,内容可以由大多数类型的Widget构成。Card具有圆角和阴影,这让它看起来具有立体感。
Card有如下三个属性:
如下面代码:
ListView(
children: <Widget>[
Card(
margin: EdgeInsets.all(10),
child: Column(
children: <Widget>[
ListTile(
title: Text(
"张三",
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
subtitle: Text("高级软件工程师"),
),
ListTile(
title: Text("电话:13241668866"),
),
ListTile(
title: Text("地址:北京市海淀区"),
)
],
),
),
Card(
margin: EdgeInsets.all(10),
child: Column(
children: <Widget>[
ListTile(
title: Text(
"李四",
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
subtitle: Text("高级软件工程师"),
),
ListTile(
title: Text("电话:13241668866"),
),
ListTile(
title: Text("地址:北京市海淀区"),
)
],
),
),
Card(
margin: EdgeInsets.all(10),
child: Column(
children: <Widget>[
ListTile(
title: Text(
"王武",
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
subtitle: Text("高级软件工程师"),
),
ListTile(
title: Text("电话:13241668866"),
),
ListTile(
title: Text("地址:北京市海淀区"),
)
],
),
)
],
);
效果如下:
下面再来看一个例子,代码如下:
class HomeContent extends StatelessWidget {
Widget _getItem(context, index) {
Map itemdata = listData[index];
return Card(
margin: EdgeInsets.all(10),
child: Column(
children: <Widget>[
AspectRatio(
aspectRatio: 2,
child: Image.network(
itemdata["imgUrl"],
fit: BoxFit.cover,
),
),
ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(itemdata["imgUrl"]),
),
title: Text(itemdata["title"]),
subtitle: Text(itemdata["detail"]),
)
],
),
);
}
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: listData.length,
itemBuilder: this._getItem,
);
}
}
效果如下:
关于上面这个例子,有如下几点说明。
图片的圆角,我们可以使用ClipOval组件,也可以使用该例中的CircleAvator组件。
可以使用AspectRatio组件来实现组件固定的宽高比。
Wrap
Wrap可以实现流布局,单行的Wrap跟Row表现几乎一致,单列的Wrap跟Column表现几乎一致。但是Row与Column都是单行单列的,Wrap则突破了这个限制,mainAxis上空间不足时,则向crossAxis上去扩展展示。
Wrap有如下常用属性:
class HomeContent extends StatelessWidget {
List<Widget> _getChildren() {
var children = listData.map((value) {
return MyButton(value["title"]);
});
return children.toList();
}
@override
Widget build(BuildContext context) {
return Wrap(
children: this._getChildren(),
alignment: WrapAlignment.start, //主轴方向上的对齐方式
spacing: 30, //主轴方向上的间距
runSpacing: 10, //交叉轴方向上的间距
);
}
}
class MyButton extends StatelessWidget {
final String titleStr;
MyButton(this.titleStr);
@override
Widget build(BuildContext context) {
return RaisedButton(
child: Text(this.titleStr),
onPressed: () {
print(Text(this.titleStr));
},
);
}
}
显示效果如下:
以上。