1. Row
Row 是一个小部件,它在一行上显示其子小部件。另一个变体是 Column,它在列上显示其子小部件。
要使 Row 的子部件展开以填充可用的水平空间,可以将其包装在一个 Expanded 对象中。
Row 将其子项放在一行并且不能滚动。如果想要一个类似的和可滚动的容器,应该考虑使用 ListView。
Row 构造函数:
Row(
{Key key,
List<Widget> children: const <Widget>[],
MainAxisAlignment mainAxisAlignment: MainAxisAlignment.start,
MainAxisSize mainAxisSize: MainAxisSize.max,
CrossAxisAlignment crossAxisAlignment: CrossAxisAlignment.center,
TextDirection textDirection,
VerticalDirection verticalDirection: VerticalDirection.down,
TextBaseline textBaseline: TextBaseline.alphabetic
}
)
2. children
children 属性用于定义 Row 的子部件列表。
可以向子项添加子窗口小部件,或从子项中删除窗口小部件,但必须遵循“添加/删除子项”部分中提到的规则。
List<Widget> children: const <Widget>[]
让我们来看看下面示例,一个带有四个子小部件的 Row:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'yiibai.com',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flutter Row Example")
),
body: Center(
child: Row (
children: [
ElevatedButton(child: Text("BTN 1"), onPressed:(){}),
Icon(Icons.ac_unit, size: 64, color: Colors.blue),
ElevatedButton(
child: Text("Button 2"),
onPressed:(){},
style: ButtonStyle(
minimumSize: MaterialStateProperty.all(Size.square(100))
)
),
ElevatedButton(child: Text("BTN 3"), onPressed:(){}),
]
)
),
);
}
}
效果如下所示:
一些flex > 0
的子widget
可以扩展其宽度以水平填充剩余空间,例如Expanded,Spacer等。它们通常用于调整Row的子widget之间的距离。下面是一个例子:
Row (
children: [
ElevatedButton(child: Text("BTN 1"), onPressed:(){}),
Expanded(
flex: 1,
child: Icon(Icons.ac_unit, size: 64, color: Colors.blue),
),
ElevatedButton(
child: Text("Button 2"),
onPressed:(){},
style: ButtonStyle(
minimumSize: MaterialStateProperty.all(Size.square(100))
)
),
Spacer(
flex: 2
),
ElevatedButton(child: Text("BTN 3"), onPressed:(){}),
]
)
效果如下所示:
3. 添加/删除子项
可以将一些子小部件添加到 Row 或从 Row 中删除其中一些。执行以下操作可能会导致意外结果:
注:错误代码,不会起作用
class SomeWidgetState extends State<SomeWidget> {
List<Widget> _children;
void initState() {
_children = [];
}
void someHandler() {
setState(() {
_children.add(newWidget);
});
}
Widget build(BuildContext context) {
// Reusing `List<Widget> _children` here is problematic.
return Row(children: this._children);
}
}
要解决上述问题,需要遵守以下规则:
- 子小部件需要明确分配一个 Key 值,这有助于 Flutter 在小部件数量变化时识别旧的或新的子小部件。
- 如果某个子部件发生变化,或者子部件的数量发生变化,则需要为 Row.children 创建一个新的 List 对象。
注:修改后的正确代码
class SomeWidgetState extends State<SomeWidget> {
List<Widget> _children;
void initState() {
this._children = [];
}
// Add or remove some children..
void someHandler() {
setState(() {
// The key here allows Flutter to reuse the underlying render
// objects even if the children list is recreated.
this._children.add(newWidget(key: ...));
});
}
Widget build(BuildContext context) {
// Always create a new list of children as a Widget is immutable.
var newChildren = List.from(this._children);
this._children = newChildren;
return Row(children: this._children);
}
}
运行效果如下:
以上示例的完整代码:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'yiibai.com',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return MyHomePageState();
}
}
class MyHomePageState extends State<MyHomePage> {
List<Widget> _children = [];
int idx = 0;
@override
void initState() {
super.initState();
this._children = [
ElevatedButton(
key: Key(this.idx.toString()),
child: Text("Btn " + idx.toString()),
onPressed: (){}
)
];
}
void addChildHandler() {
this.idx++;
this.setState(() {
var newChild = ElevatedButton(
key: Key(this.idx.toString()),
child: Text("Btn " + idx.toString()),
onPressed: (){}
);
this._children.add(newChild);
});
}
@override
Widget build(BuildContext context) {
// Create new List object:
this._children = this._children == null? [] : List.from(this._children);
return Scaffold(
appBar: AppBar(
title: Text("Flutter Row Example")
),
body: Center(
child: Row (
children: this._children
)
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
this.addChildHandler();
}
),
);
}
}
4. mainAxisAlignment
mainAxisAlignment 属性用于指定子部件在主轴上的排列方式。对于 Row,主轴是主要的水平轴。
MainAxisAlignment mainAxisAlignment: MainAxisAlignment.start
// MainAxisAlignment enum:
MainAxisAlignment.start
MainAxisAlignment.center
MainAxisAlignment.end
MainAxisAlignment.spaceBetween
MainAxisAlignment.spaceAround
MainAxisAlignment.spaceEvenly
MainAxisAlignment.start
在 textDirection = TextDirection.ltr
(默认)和 mainAxisAlignment = MainAxisAlignment.start
的情况下,Row 的子部件将从左到右并排放置。
MainAxisAlignment.start示例代码 -
Row (
mainAxisAlignment: MainAxisAlignment.start,
children: [
ElevatedButton(child: Text("Button 1"), onPressed:(){}),
ElevatedButton(
child: Text("Button 2"),
onPressed:(){},
style: ButtonStyle(
minimumSize: MaterialStateProperty.all(Size.square(100))
)
),
ElevatedButton(child: Text("Very Long Button 3"), onPressed:(){})
]
)
MainAxisAlignment.center
mainAxisAlignment: MainAxisAlignment.center
MainAxisAlignment.end
在 textDirection = TextDirection.ltr
(默认)和 mainAxisAlignment = MainAxisAlignment.end
的情况下,Row 的子小部件将从右到左并排放置。
mainAxisAlignment: MainAxisAlignment.end
MainAxisAlignment.spaceBetween
mainAxisAlignment: MainAxisAlignment.spaceBetween
MainAxisAlignment.spaceEvenly
mainAxisAlignment: MainAxisAlignment.spaceEvenly
MainAxisAlignment.spaceAround
mainAxisAlignment: MainAxisAlignment.spaceAround
5. mainAxisSize
mainAxisSize
属性指定 Row 应占用多少垂直空间。它的默认值是 MainAxisSize.max
那么意味着 Row 尝试占据尽可能多的水平空间。
如果有一个带有flex > 0 && fit != FlexFit.loose
的子部件,无论 mainAxisSize
的值是多少,Row 都会尝试占用尽可能多的空间。另外,如果 mainAxisSize = MainAxisSize.min
,Row 将有足够的宽度供其所有子小部件使用。
MainAxisSize mainAxisSize: MainAxisSize.max
// MainAxisSize enum:
MainAxisSize.max
MainAxisSize.min
效果如下:
6. crossAxisAlignment
crossAxisAlignment
属性用于指定子小部件在交叉轴上的排列方式。至于Row,横轴就是纵轴。
CrossAxisAlignment crossAxisAlignment: CrossAxisAlignment.center
// CrossAxisAlignment enum:
CrossAxisAlignment.start
CrossAxisAlignment.end
CrossAxisAlignment.center
CrossAxisAlignment.baseline
CrossAxisAlignment.stretch
CrossAxisAlignment.start
如果 verticalDirection = VerticalDirection.down
(默认) 和 crossAxisAlignment = CrossAxisAlignment.start
,Row 的子小部件将放置在靠近 Row 的顶部边缘。
CrossAxisAlignment.start示例代码
Row (
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ElevatedButton(child: Text("Button 1"), onPressed:(){}),
ElevatedButton(
child: Text("Button 2"),
onPressed:(){},
style: ButtonStyle(
minimumSize: MaterialStateProperty.all(Size.square(100))
)
),
ElevatedButton(child: Text("Very Long Button 3"), onPressed:(){})
]
)
CrossAxisAlignment.center
crossAxisAlignment: CrossAxisAlignment.center
CrossAxisAlignment.end
如果 verticalDirection = VerticalDirection.down
(默认)和 crossAxisAlignment = CrossAxisAlignment.end
,Row 的子小部件将放置在靠近 Row 的底部边缘。
crossAxisAlignment: CrossAxisAlignment.end
CrossAxisAlignment.stretch
crossAxisAlignment: CrossAxisAlignment.stretch
CrossAxisAlignment.baseline
crossAxisAlignment: CrossAxisAlignment.baseline
示例:
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic
7. textDirection
textDirection
属性指定 Row 的子部件如何在主轴(水平轴)上排列,以及如何解释单词“start”和“end”。
TextDirection textDirection
// TextDirection enum:
TextDirection.ltr (Left to Right) (Default)
TextDirection.rtl (Right to Left)
如果 textDirection = TextDirection.ltr
(默认),单词“start”将对应于“left”,单词“end”将对应于“right”。
相反,在 textDirection = TextDirection.rtl
的情况下,单词“start”将对应于“right”,而单词“end”将对应于“left”。
8. verticalDirection
VerticalDirection 属性指定 Row 的子小部件如何在横轴(垂直轴)上排列以及如何解释单词“start”和“end”。
VerticalDirection verticalDirection: VerticalDirection.down
// VerticalDirection enum:
VerticalDirection.down (Default)
VerticalDirection.up
如果verticalDirection = VerticalDirection.down
(默认),单词“start”将对应于“top”,单词“end”将对应于“bottom”。
相反,如果verticalDirection = VerticalDirection.up
,则“start”一词将对应于“bottom”,而“end”一词将对应于“top”。
9. textBaseline
如果根据基线对齐子小部件,则 textBaseline 属性指定将使用哪种基线。
TextBaseline textBaseline: TextBaseline.alphabetic
// TextBaseline enum:
TextBaseline.alphabetic (Default)
TextBaseline.ideographic
示例代码:
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic