Flutter 中拥有 30 多种预定义的布局 widget,常用的有 Container、Padding、Center、Flex、Row、Colum、ListView、GridView 。用一个思维导图列出它们的特性和使用。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Container 的基本使用'),
),
body: Container (
constraints: BoxConstraints.expand(
height: Theme.of(context).textTheme.headline4!.fontSize! * 1.1 + 200,
),
decoration: BoxDecoration(
border: Border.all(width: 2.0,color: Colors.red),
color: Colors.grey,
borderRadius: BorderRadius.all(Radius.circular(20.0)),
image: DecorationImage(
image: AssetImage("images/avatar.png"),
centerSlice: Rect.fromLTRB(270.0, 180.0, 1360.0, 730.0)
),
),
padding: EdgeInsets.all(8.0),
alignment: Alignment.center,
child: Text('Hello Kevin',
style: Theme.of(context).textTheme.displayMedium!.copyWith(color: Colors.black)),
transform: Matrix4.rotationZ(0.3),
)
)
);
}
}
一个 Widget,会给其子 Widget 添加指定的填充,示意图如下:
class MyHomePage extends StatelessWidget {
...
body: new PaddingWidget(),
...
}
//Padding布局
class PaddingWidget extends StatelessWidget{
@override
Widget build(BuildContext context){
return Padding(
//设置左上右下内边距为4,10,6,8
padding:EdgeInsets.fromLTRB(4, 10, 6, 8),
child: Text('My name is Knight'),
);
}
}
将其子 widget 居中显示在自身内部的 widget,示意图:
//Center
class CenterWidget extends StatelessWidget{
@override
Widget build(BuildContext context){
return Container(
width:200,//宽
height:200,//高
child: Center(
child: Text("My name is Knight"),
),
decoration: BoxDecoration(//设置边框
//背景色
color:Colors.redAccent,
//圆角
borderRadius: BorderRadius.circular(6),
),
);
}
}
可以允许其子 Widget 简单的堆叠在一起,层叠布局,示意图:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('层叠布局'),
),
body: Center(
child: Stack(
alignment: const Alignment(0.6, 0.6),
children: <Widget>[
CircleAvatar(
backgroundImage: AssetImage('images/avatar.png'),
radius: 100.0,
),
Container(
decoration: BoxDecoration(
color: Colors.black45
),
child: Text(
'KevinDev',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
)
],
),
)
)
);
}
}
在垂直方向上排列子Widget,示意图如下:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('线性布局'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
color: Colors.blue,
child: FlutterLogo(
size: 50.0,
),
),
Container(
color: Colors.pink,
child: FlutterLogo(
size: 50.0,
),
),
Container(
color: Colors.purple,
child: FlutterLogo(
size: 50.0,
),
),
],
),
)
)
);
}
}
MainAxisAlignment.start
这是默认值:垂直方向顶部对齐MainAxisAlignment.end
:垂直方向底部对齐MainAxisAlignment.center
:垂直方向居中对齐MainAxisAlignment.spaceBetween
:垂直方向平分剩余空间MainAxisAlignment.spaceAround
:放置控件后,剩余空间平分成n份,n是子widget的数量,然后把其中一份空间分成2份,放在第一个child的前面,和最后一个child的后面,也就是子widget的之前之后之间均匀分割空闲的一半空间MainAxisAlignment.spaceEvenly
:放置控件后,把剩余空间平分n+1份,然后平分所有的空间,在子widget之前之后之间均匀的分割空闲的空间CrossAxisAlignment.center
这是默认值,水平居中CrossAxisAlignment.end
:水平方向右侧对齐CrossAxisAlignment.start
:水平方向左侧对齐CrossAxisAlignment.stretch
:水平方向拉伸子child填充满布局CrossAxisAlignment.baseline
:和textBaseline一起使用import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('线性布局'),
),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
color: Colors.blue,
child: FlutterLogo(
size: 50.0,
),
),
Container(
color: Colors.pink,
child: FlutterLogo(
size: 50.0,
),
),
Container(
color: Colors.purple,
child: FlutterLogo(
size: 50.0,
),
),
],
),
)
)
);
}
}
MainAxisAlignment.start
:这是默认值,水平方向顶部对齐MainAxisAlignment.center
:水平方向居中对齐MainAxisAlignment.end
:水平方向底部对齐MainAxisAlignment.spaceBetween
:水平方向上平分剩余空间MainAxisAlignment.spaceAround
:放置控件后,剩余空间平分成n份,n是子widget的数量,然后把其中一份空间分成2份,放在第一个child的前面,和最后一个child的后面,也就是子widget的之前之后之间均匀分割空闲的一半空间MainAxisAlignment.spaceEvenly
:放置控件后,把剩余空间平分n+1份,然后平分所有的空间,在子widget之前之后之间均匀的分割空闲的空间CrossAxisAlignment.center
:这是默认,垂直居中CrossAxisAlignment.end
:垂直方向右侧对齐CrossAxisAlignment.start
:垂直方向左侧对齐CrossAxisAlignment.stretch
:垂直方向拉伸子child填充满布局CrossAxisAlignment.baseline
:和textBaseline一起使用Expanded 组件可以使 Row、Column、Fiex 等子组件在其主轴上方向展开并填充可用的空间,这里注意:Expanded 组件必须用在 Row、Column、Fiex 内,并且从Expanded到封装它的Row、Column、Flex的路径必须只包括StatelessWidgets或者StatefulWidgets(不能是其他类型的组件,像RenderObjectWidget,它是渲染对象,不再改变尺寸,因此Expanded不能放进RenderObjectWidget),示意图如下:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('弹性布局'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Flex(
direction: Axis.horizontal,
children: <Widget>[
Expanded(
flex: 1,
child: Container(
color: Colors.pink,
child: FlutterLogo(
size: 50.0,
),
),
),
Expanded(
flex: 1,
child: Container(
color: Colors.green,
child: FlutterLogo(
size: 50.0,
),
),
),
],
),
Padding(
padding: EdgeInsets.only(top: 20.0),
child: SizedBox(
height: 100.0,
child: Flex(
direction: Axis.vertical,
children: <Widget>[
Expanded(
flex: 2,
child: Container(
height: 30.0,
color: Colors.blue,
),
),
Spacer(
flex: 1,
),
Expanded(
flex: 1,
child: Container(
height: 30.0,
color: Colors.green,
),
),
],
),
),
)
],
),
)
)
);
}
}
我相信这个布局在平时开发会经常用到,这是可滚动的列表控件,ListView是最常用的滚动widget,它在滚动方向上一个接一个地显示它的孩子。在纵轴上,孩子没被要求填充ListView,并且内置ListTitle,示意图如下:
class MyHomePage extends StatelessWidget {
....
body: new ListViewWidget(
new List<String>.generate(1000,(i){
return 'Item &i';
}),
),
...
}
//ListView
class ListViewWidget extends StatelessWidget {
final List<String> items;
ListViewWidget(this.items);
@override
Widget build(BuildContext context) {
return new ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return new ListTile(
title: new Text('This is $index'),
);
},
);
}
}
下面设置水平的ListView:
class MyHomePage extends StatelessWidget {
....
body: new ListViewWidget(
new List<String>.generate(1000, (i) {
return 'Item &i';
}),
),
...
}
Widget build(BuildContext context) {
return new ListView.builder(
itemCount: items.length,
//设置水平方向
scrollDirection:Axis.horizontal,
//竖直时:确定每一个item的高度
//水平时:确定每一个item的宽度 得要设置 不然不显示
itemExtent: 110.0,
itemBuilder: (context, index) {
return new ListTile(
title: new Text('This is $index'),
);
},
);
GridView是一个网格布局的列组件。GridView继承至CustomScrollView,示意图如下:
//GridView
class GridViewWidget extends StatelessWidget{
@override
Widget build(BuildContext context){
return new GridView.count(
crossAxisCount: 3, //3列
children: List.generate(40,
(i){
return Card(
child: Center(
child:Text('This is $i'),
),
);
})
);
}
}