//////////////////////////////////////////////////
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return const FlutterLayoutArticle([
Example1(),
Example2(),
Example3(),
Example4(),
Example5(),
Example6(),
Example7(),
Example8(),
Example9(),
Example10(),
Example11(),
Example12(),
Example13(),
Example14(),
Example15(),
Example16(),
Example17(),
Example18(),
Example19(),
Example20(),
Example21(),
Example22(),
Example23(),
Example24(),
Example25(),
Example26(),
Example27(),
Example28(),
Example29(),
//////////////////////////////////////////////////
abstract class Example extends StatelessWidget {
const Example({super.key});
String get code;
String get explanation;
//////////////////////////////////////////////////
class FlutterLayoutArticle extends StatefulWidget {
const FlutterLayoutArticle(
this.examples, {
super.key,
final List
examples;
@override
State createState() => _FlutterLayoutArticleState();
//////////////////////////////////////////////////
class _FlutterLayoutArticleState extends State {
late int count;
late Widget example;
late String code;
late String explanation;
@override
void initState() {
count = 1;
code = const Example1().code;
explanation = const Example1().explanation;
super.initState();
@override
void didUpdateWidget(FlutterLayoutArticle oldWidget) {
super.didUpdateWidget(oldWidget);
var example = widget.examples[count - 1];
code = example.code;
explanation = example.explanation;
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Layout Article',
home: SafeArea(
child: Material(
color: Colors.black,
child: FittedBox(
child: Container(
width: 400,
height: 670,
color: const Color(0xFFCCCCCC),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: ConstrainedBox(
constraints: const BoxConstraints.tightFor(
width: double.infinity, height: double.infinity),
child: widget.examples[count - 1])),
Container(
height: 50,
width: double.infinity,
color: Colors.black,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
for (int i = 0; i < widget.examples.length; i++)
Container(
width: 58,
padding:
const EdgeInsets.only(left: 4.0, right: 4.0),
child: button(i + 1),
Container(
height: 273,
color: Colors.grey[50],
child: Scrollbar(
child: SingleChildScrollView(
key: ValueKey(count),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Center(child: Text(code)),
const SizedBox(height: 15),
Text(
explanation,
style: TextStyle(
color: Colors.blue[900],
fontStyle: FontStyle.italic),
Widget button(int exampleNumber) {
return Button(
key: ValueKey('button$exampleNumber'),
isSelected: count == exampleNumber,
exampleNumber: exampleNumber,
onPressed: () {
showExample(
exampleNumber,
widget.examples[exampleNumber - 1].code,
widget.examples[exampleNumber - 1].explanation,
void showExample(int exampleNumber, String code, String explanation) {
setState(() {
count = exampleNumber;
this.code = code;
this.explanation = explanation;
//////////////////////////////////////////////////
class Button extends StatelessWidget {
final bool isSelected;
final int exampleNumber;
final VoidCallback onPressed;
const Button({
super.key,
required this.isSelected,
required this.exampleNumber,
required this.onPressed,
@override
Widget build(BuildContext context) {
return TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: isSelected ? Colors.grey : Colors.grey[800],
child: Text(exampleNumber.toString()),
onPressed: () {
Scrollable.ensureVisible(
context,
duration: const Duration(milliseconds: 350),
curve: Curves.easeOut,
alignment: 0.5,
onPressed();
//////////////////////////////////////////////////
class Example1 extends Example {
const Example1({super.key});
@override
final code = 'Container(color: red)';
@override
final explanation = 'The screen is the parent of the Container, '
'and it forces the Container to be exactly the same size as the screen.'
'\n\n'
'So the Container fills the screen and paints it red.';
@override
Widget build(BuildContext context) {
return Container(color: red);
//////////////////////////////////////////////////
class Example2 extends Example {
const Example2({super.key});
@override
final code = 'Container(width: 100, height: 100, color: red)';
@override
final String explanation =
'The red Container wants to be 100x100, but it can\'t, '
'because the screen forces it to be exactly the same size as the screen.'
'\n\n'
'So the Container fills the screen.';
@override
Widget build(BuildContext context) {
return Container(width: 100, height: 100, color: red);
//////////////////////////////////////////////////
class Example3 extends Example {
const Example3({super.key});
@override
final code = 'Center(\n'
' child: Container(width: 100, height: 100, color: red))';
@override
final String explanation =
'The screen forces the Center to be exactly the same size as the screen, '
'so the Center fills the screen.'
'\n\n'
'The Center tells the Container that it can be any size it wants, but not bigger than the screen.'
'Now the Container can indeed be 100x100.';
@override
Widget build(BuildContext context) {
return Center(
child: Container(width: 100, height: 100, color: red),
//////////////////////////////////////////////////
class Example4 extends Example {
const Example4({super.key});
@override
final code = 'Align(\n'
' alignment: Alignment.bottomRight,\n'
' child: Container(width: 100, height: 100, color: red))';
@override
final String explanation =
'This is different from the previous example in that it uses Align instead of Center.'
'\n\n'
'Align also tells the Container that it can be any size it wants, but if there is empty space it won\'t center the Container. '
'Instead, it aligns the Container to the bottom-right of the available space.';
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.bottomRight,
child: Container(width: 100, height: 100, color: red),
//////////////////////////////////////////////////
class Example5 extends Example {
const Example5({super.key});
@override
final code = 'Center(\n'
' child: Container(\n'
' color: red,\n'
' width: double.infinity,\n'
' height: double.infinity))';
@override
final String explanation =
'The screen forces the Center to be exactly the same size as the screen, '
'so the Center fills the screen.'
'\n\n'
'The Center tells the Container that it can be any size it wants, but not bigger than the screen.'
'The Container wants to be of infinite size, but since it can\'t be bigger than the screen, it just fills the screen.';
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: double.infinity, height: double.infinity, color: red),
//////////////////////////////////////////////////
class Example6 extends Example {
const Example6({super.key});
@override
final code = 'Center(child: Container(color: red))';
@override
final String explanation =
'The screen forces the Center to be exactly the same size as the screen, '
'so the Center fills the screen.'
'\n\n'
'The Center tells the Container that it can be any size it wants, but not bigger than the screen.'
'\n\n'
'Since the Container has no child and no fixed size, it decides it wants to be as big as possible, so it fills the whole screen.'
'\n\n'
'But why does the Container decide that? '
'Simply because that\'s a design decision by those who created the Container widget. '
'It could have been created differently, and you have to read the Container documentation to understand how it behaves, depending on the circumstances. ';
@override
Widget build(BuildContext context) {
return Center(
child: Container(color: red),
//////////////////////////////////////////////////
class Example7 extends Example {
const Example7({super.key});
@override
final code = 'Center(\n'
' child: Container(color: red\n'
' child: Container(color: green, width: 30, height: 30)))';
@override
final String explanation =
'The screen forces the Center to be exactly the same size as the screen, '
'so the Center fills the screen.'
'\n\n'
'The Center tells the red Container that it can be any size it wants, but not bigger than the screen.'
'Since the red Container has no size but has a child, it decides it wants to be the same size as its child.'
'\n\n'
'The red Container tells its child that it can be any size it wants, but not bigger than the screen.'
'\n\n'
'The child is a green Container that wants to be 30x30.'
'\n\n'
'Since the red `Container` has no size but has a child, it decides it wants to be the same size as its child. '
'The red color isn\'t visible, since the green Container entirely covers all of the red Container.';
@override
Widget build(BuildContext context) {
return Center(
child: Container(
color: red,
child: Container(color: green, width: 30, height: 30),
//////////////////////////////////////////////////
class Example8 extends Example {
const Example8({super.key});
@override
final code = 'Center(\n'
' child: Container(color: red\n'
' padding: const EdgeInsets.all(20.0),\n'
' child: Container(color: green, width: 30, height: 30)))';
@override
final String explanation =
'The red Container sizes itself to its children size, but it takes its own padding into consideration. '
'So it is also 30x30 plus padding. '
'The red color is visible because of the padding, and the green Container has the same size as in the previous example.';
@override
Widget build(BuildContext context) {
return Center(
child: Container(
padding: const EdgeInsets.all(20.0),
color: red,
child: Container(color: green, width: 30, height: 30),
//////////////////////////////////////////////////
class Example9 extends Example {
const Example9({super.key});
@override
final code = 'ConstrainedBox(\n'
' constraints: BoxConstraints(\n'
' minWidth: 70, minHeight: 70,\n'
' maxWidth: 150, maxHeight: 150),\n'
' child: Container(color: red, width: 10, height: 10)))';
@override
final String explanation =
'You might guess that the Container has to be between 70 and 150 pixels, but you would be wrong. '
'The ConstrainedBox only imposes ADDITIONAL constraints from those it receives from its parent.'
'\n\n'
'Here, the screen forces the ConstrainedBox to be exactly the same size as the screen, '
'so it tells its child Container to also assume the size of the screen, '
'thus ignoring its \'constraints\' parameter.';
@override
Widget build(BuildContext context) {
return ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 70,
minHeight: 70,
maxWidth: 150,
maxHeight: 150,
child: Container(color: red, width: 10, height: 10),
//////////////////////////////////////////////////
class Example10 extends Example {
const Example10({super.key});
@override
final code = 'Center(\n'
' child: ConstrainedBox(\n'
' constraints: BoxConstraints(\n'
' minWidth: 70, minHeight: 70,\n'
' maxWidth: 150, maxHeight: 150),\n'
' child: Container(color: red, width: 10, height: 10))))';
@override
final String explanation =
'Now, Center allows ConstrainedBox to be any size up to the screen size.'
'\n\n'
'The ConstrainedBox imposes ADDITIONAL constraints from its \'constraints\' parameter onto its child.'
'\n\n'
'The Container must be between 70 and 150 pixels. It wants to have 10 pixels, so it will end up having 70 (the MINIMUM).';
@override
Widget build(BuildContext context) {
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 70,
minHeight: 70,
maxWidth: 150,
maxHeight: 150,
child: Container(color: red, width: 10, height: 10),
//////////////////////////////////////////////////
class Example11 extends Example {
const Example11({super.key});
@override
final code = 'Center(\n'
' child: ConstrainedBox(\n'
' constraints: BoxConstraints(\n'
' minWidth: 70, minHeight: 70,\n'
' maxWidth: 150, maxHeight: 150),\n'
' child: Container(color: red, width: 1000, height: 1000))))';
@override
final String explanation =
'Center allows ConstrainedBox to be any size up to the screen size.'
'The ConstrainedBox imposes ADDITIONAL constraints from its \'constraints\' parameter onto its child'
'\n\n'
'The Container must be between 70 and 150 pixels. It wants to have 1000 pixels, so it ends up having 150 (the MAXIMUM).';
@override
Widget build(BuildContext context) {
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 70,
minHeight: 70,
maxWidth: 150,
maxHeight: 150,
child: Container(color: red, width: 1000, height: 1000),
//////////////////////////////////////////////////
class Example12 extends Example {
const Example12({super.key});
@override
final code = 'Center(\n'
' child: ConstrainedBox(\n'
' constraints: BoxConstraints(\n'
' minWidth: 70, minHeight: 70,\n'
' maxWidth: 150, maxHeight: 150),\n'
' child: Container(color: red, width: 100, height: 100))))';
@override
final String explanation =
'Center allows ConstrainedBox to be any size up to the screen size.'
'ConstrainedBox imposes ADDITIONAL constraints from its \'constraints\' parameter onto its child.'
'\n\n'
'The Container must be between 70 and 150 pixels. It wants to have 100 pixels, and that\'s the size it has, since that\'s between 70 and 150.';
@override
Widget build(BuildContext context) {
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 70,
minHeight: 70,
maxWidth: 150,
maxHeight: 150,
child: Container(color: red, width: 100, height: 100),
//////////////////////////////////////////////////
class Example13 extends Example {
const Example13({super.key});
@override
final code = 'UnconstrainedBox(\n'
' child: Container(color: red, width: 20, height: 50));';
@override
final String explanation =
'The screen forces the UnconstrainedBox to be exactly the same size as the screen.'
'However, the UnconstrainedBox lets its child Container be any size it wants.';
@override
Widget build(BuildContext context) {
return UnconstrainedBox(
child: Container(color: red, width: 20, height: 50),
//////////////////////////////////////////////////
class Example14 extends Example {
const Example14({super.key});
@override
final code = 'UnconstrainedBox(\n'
' child: Container(color: red, width: 4000, height: 50));';
@override
final String explanation =
'The screen forces the UnconstrainedBox to be exactly the same size as the screen, '
'and UnconstrainedBox lets its child Container be any size it wants.'
'\n\n'
'Unfortunately, in this case the Container has 4000 pixels of width and is too big to fit in the UnconstrainedBox, '
'so the UnconstrainedBox displays the much dreaded "overflow warning".';
@override
Widget build(BuildContext context) {
return UnconstrainedBox(
child: Container(color: red, width: 4000, height: 50),
//////////////////////////////////////////////////
class Example15 extends Example {
const Example15({super.key});
@override
final code = 'OverflowBox(\n'
' minWidth: 0.0,'
' minHeight: 0.0,'
' maxWidth: double.infinity,'
' maxHeight: double.infinity,'
' child: Container(color: red, width: 4000, height: 50));';
@override
final String explanation =
'The screen forces the OverflowBox to be exactly the same size as the screen, '
'and OverflowBox lets its child Container be any size it wants.'
'\n\n'
'OverflowBox is similar to UnconstrainedBox, and the difference is that it won\'t display any warnings if the child doesn\'t fit the space.'
'\n\n'
'In this case the Container is 4000 pixels wide, and is too big to fit in the OverflowBox, '
'but the OverflowBox simply shows as much as it can, with no warnings given.';
@override
Widget build(BuildContext context) {
return OverflowBox(
minWidth: 0.0,
minHeight: 0.0,
maxWidth: double.infinity,
maxHeight: double.infinity,
child: Container(color: red, width: 4000, height: 50),
//////////////////////////////////////////////////
class Example16 extends Example {
const Example16({super.key});
@override
final code = 'UnconstrainedBox(\n'
' child: Container(color: Colors.red, width: double.infinity, height: 100));';
@override
final String explanation =
'This won\'t render anything, and you\'ll see an error in the console.'
'\n\n'
'The UnconstrainedBox lets its child be any size it wants, '
'however its child is a Container with infinite size.'
'\n\n'
'Flutter can\'t render infinite sizes, so it throws an error with the following message: '
'"BoxConstraints forces an infinite width."';
@override
Widget build(BuildContext context) {
return UnconstrainedBox(
child: Container(color: Colors.red, width: double.infinity, height: 100),
//////////////////////////////////////////////////
class Example17 extends Example {
const Example17({super.key});
@override
final code = 'UnconstrainedBox(\n'
' child: LimitedBox(maxWidth: 100,\n'
' child: Container(color: Colors.red,\n'
' width: double.infinity, height: 100));';
@override
final String explanation = 'Here you won\'t get an error anymore, '
'because when the LimitedBox is given an infinite size by the UnconstrainedBox, '
'it passes a maximum width of 100 down to its child.'
'\n\n'
'If you swap the UnconstrainedBox for a Center widget, '
'the LimitedBox won\'t apply its limit anymore (since its limit is only applied when it gets infinite constraints), '
'and the width of the Container is allowed to grow past 100.'
'\n\n'
'This explains the difference between a LimitedBox and a ConstrainedBox.';
@override
Widget build(BuildContext context) {
return UnconstrainedBox(
child: LimitedBox(
maxWidth: 100,
child: Container(
color: Colors.red,
width: double.infinity,
height: 100,
//////////////////////////////////////////////////
class Example18 extends Example {
const Example18({super.key});
@override
final code = 'FittedBox(\n'
' child: Text(\'Some Example Text.\'));';
@override
final String explanation =
'The screen forces the FittedBox to be exactly the same size as the screen.'
'The Text has some natural width (also called its intrinsic width) that depends on the amount of text, its font size, and so on.'
'\n\n'
'The FittedBox lets the Text be any size it wants, '
'but after the Text tells its size to the FittedBox, '
'the FittedBox scales the Text until it fills all of the available width.';
@override
Widget build(BuildContext context) {
return const FittedBox(
child: Text('Some Example Text.'),
//////////////////////////////////////////////////
class Example19 extends Example {
const Example19({super.key});
@override
final code = 'Center(\n'
' child: FittedBox(\n'
' child: Text(\'Some Example Text.\')));';
@override
final String explanation =
'But what happens if you put the FittedBox inside of a Center widget? '
'The Center lets the FittedBox be any size it wants, up to the screen size.'
'\n\n'
'The FittedBox then sizes itself to the Text, and lets the Text be any size it wants.'
'\n\n'
'Since both FittedBox and the Text have the same size, no scaling happens.';
@override
Widget build(BuildContext context) {
return const Center(
child: FittedBox(
child: Text('Some Example Text.'),
////////////////////////////////////////////////////
class Example20 extends Example {
const Example20({super.key});
@override
final code = 'Center(\n'
' child: FittedBox(\n'
' child: Text(\'…\')));';
@override
final String explanation =
'However, what happens if FittedBox is inside of a Center widget, but the Text is too large to fit the screen?'
'\n\n'
'FittedBox tries to size itself to the Text, but it can\'t be bigger than the screen. '
'It then assumes the screen size, and resizes Text so that it fits the screen, too.';
@override
Widget build(BuildContext context) {
return const Center(
child: FittedBox(
child: Text(
'This is some very very very large text that is too big to fit a regular screen in a single line.'),
//////////////////////////////////////////////////
class Example21 extends Example {
const Example21({super.key});
@override
final code = 'Center(\n'
' child: Text(\'…\'));';
@override
final String explanation = 'If, however, you remove the FittedBox, '
'the Text gets its maximum width from the screen, '
'and breaks the line so that it fits the screen.';
@override
Widget build(BuildContext context) {
return const Center(
child: Text(
'This is some very very very large text that is too big to fit a regular screen in a single line.'),
//////////////////////////////////////////////////
class Example22 extends Example {
const Example22({super.key});
@override
final code = 'FittedBox(\n'
' child: Container(\n'
' height: 20.0, width: double.infinity));';
@override
final String explanation =
'FittedBox can only scale a widget that is BOUNDED (has non-infinite width and height).'
'Otherwise, it won\'t render anything, and you\'ll see an error in the console.';
@override
Widget build(BuildContext context) {
return FittedBox(
child: Container(
height: 20.0,
width: double.infinity,
color: Colors.red,
//////////////////////////////////////////////////
class Example23 extends Example {
const Example23({super.key});
@override
final code = 'Row(children:[\n'
' Container(color: red, child: Text(\'Hello!\'))\n'
' Container(color: green, child: Text(\'Goodbye!\'))]';
@override
final String explanation =
'The screen forces the Row to be exactly the same size as the screen.'
'\n\n'
'Just like an UnconstrainedBox, the Row won\'t impose any constraints onto its children, '
'and instead lets them be any size they want.'
'\n\n'
'The Row then puts them side-by-side, and any extra space remains empty.';
@override
Widget build(BuildContext context) {
return Row(
children: [
Container(color: red, child: const Text('Hello!', style: big)),
Container(color: green, child: const Text('Goodbye!', style: big)),
//////////////////////////////////////////////////
class Example24 extends Example {
const Example24({super.key});
@override
final code = 'Row(children:[\n'
' Container(color: red, child: Text(\'…\'))\n'
' Container(color: green, child: Text(\'Goodbye!\'))]';
@override
final String explanation =
'Since the Row won\'t impose any constraints onto its children, '
'it\'s quite possible that the children might be too big to fit the available width of the Row.'
'In this case, just like an UnconstrainedBox, the Row displays the "overflow warning".';
@override
Widget build(BuildContext context) {
return Row(
children: [
Container(
color: red,
child: const Text(
'This is a very long text that '
'won\'t fit the line.',
style: big,
Container(color: green, child: const Text('Goodbye!', style: big)),
//////////////////////////////////////////////////
class Example25 extends Example {
const Example25({super.key});
@override
final code = 'Row(children:[\n'
' Expanded(\n'
' child: Container(color: red, child: Text(\'…\')))\n'
' Container(color: green, child: Text(\'Goodbye!\'))]';
@override
final String explanation =
'When a Row\'s child is wrapped in an Expanded widget, the Row won\'t let this child define its own width anymore.'
'\n\n'
'Instead, it defines the Expanded width according to the other children, and only then the Expanded widget forces the original child to have the Expanded\'s width.'
'\n\n'
'In other words, once you use Expanded, the original child\'s width becomes irrelevant, and is ignored.';
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: Center(
child: Container(
color: red,
child: const Text(
'This is a very long text that won\'t fit the line.',
style: big,
Container(color: green, child: const Text('Goodbye!', style: big)),
//////////////////////////////////////////////////
class Example26 extends Example {
const Example26({super.key});
@override
final code = 'Row(children:[\n'
' Expanded(\n'
' child: Container(color: red, child: Text(\'…\')))\n'
' Expanded(\n'
' child: Container(color: green, child: Text(\'Goodbye!\'))]';
@override
final String explanation =
'If all of Row\'s children are wrapped in Expanded widgets, each Expanded has a size proportional to its flex parameter, '
'and only then each Expanded widget forces its child to have the Expanded\'s width.'
'\n\n'
'In other words, Expanded ignores the preffered width of its children.';
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: Container(
color: red,
child: const Text(
'This is a very long text that won\'t fit the line.',
style: big,
Expanded(
child: Container(
color: green,
child: const Text(
'Goodbye!',
style: big,
//////////////////////////////////////////////////
class Example27 extends Example {
const Example27({super.key});
@override
final code = 'Row(children:[\n'
' Flexible(\n'
' child: Container(color: red, child: Text(\'…\')))\n'
' Flexible(\n'
' child: Container(color: green, child: Text(\'Goodbye!\'))]';
@override
final String explanation =
'The only difference if you use Flexible instead of Expanded, '
'is that Flexible lets its child be SMALLER than the Flexible width, '
'while Expanded forces its child to have the same width of the Expanded.'
'\n\n'
'But both Expanded and Flexible ignore their children\'s width when sizing themselves.'
'\n\n'
'This means that it\'s IMPOSSIBLE to expand Row children proportionally to their sizes. '
'The Row either uses the exact child\'s width, or ignores it completely when you use Expanded or Flexible.';
@override
Widget build(BuildContext context) {
return Row(
children: [
Flexible(
child: Container(
color: red,
child: const Text(
'This is a very long text that won\'t fit the line.',
style: big,
Flexible(
child: Container(
color: green,
child: const Text(
'Goodbye!',
style: big,
//////////////////////////////////////////////////
class Example28 extends Example {
const Example28({super.key});
@override
final code = 'Scaffold(\n'
' body: Container(color: blue,\n'
' child: Column(\n'
' children: [\n'
' Text(\'Hello!\'),\n'
' Text(\'Goodbye!\')])))';
@override
final String explanation =
'The screen forces the Scaffold to be exactly the same size as the screen, '
'so the Scaffold fills the screen.'
'\n\n'
'The Scaffold tells the Container that it can be any size it wants, but not bigger than the screen.'
'\n\n'
'When a widget tells its child that it can be smaller than a certain size, '
'we say the widget supplies "loose" constraints to its child. More on that later.';
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: blue,
child: Column(
children: const [
Text('Hello!'),
Text('Goodbye!'),
//////////////////////////////////////////////////
class Example29 extends Example {
const Example29({super.key});
@override
final code = 'Scaffold(\n'
' body: Container(color: blue,\n'
' child: SizedBox.expand(\n'
' child: Column(\n'
' children: [\n'
' Text(\'Hello!\'),\n'
' Text(\'Goodbye!\')]))))';
@override
final String explanation =
'If you want the Scaffold\'s child to be exactly the same size as the Scaffold itself, '
'you can wrap its child with SizedBox.expand.'
'\n\n'
'When a widget tells its child that it must be of a certain size, '
'we say the widget supplies "tight" constraints to its child. More on that later.';
@override
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox.expand(
child: Container(
color: blue,
child: Column(
children: const [
Text('Hello!'),
Text('Goodbye!'),
//////////////////////////////////////////////////
如果你愿意的话,你还可以在
这个 Github 仓库中
获取其代码。
以下各节将介绍这些示例。
Align(
alignment: Alignment.bottomRight,
child: Container(width: 100, height: 100, color: red),
与上一个样例不同的是,我们使用了 Align
而不是 Center
。
Align
同样也告诉 Container
,你可以变成任意大小。但是,如果还留有空白空间的话,它不会居中 Container
。相反,它将会在允许的空间内,把 Container
放在右下角(bottomRight)。
但是,为什么 Container
做出了这个决定?非常简单,因为这个决定是由 Container
widget 的创建者决定的。可能会因创造者而异,而且你还得阅读
Container
文档
来理解不同场景下它的行为。
padding: const EdgeInsets.all(20.0),
color: red,
child: Container(color: green, width: 30, height: 30),
红色 Container
变为其子级的大小,但是它将其 padding 带入了约束的计算中。所以它有一个 30 x 30 的外边距。由于这个外边距,所以现在你能看见红色了。而绿色的 Container
则还是和之前一样。
maxWidth: double.infinity,
maxHeight: double.infinity,
child: Container(color: red, width: 4000, height: 50),
屏幕强制 OverflowBox
变得和屏幕一样大,并且 OverflowBox
允许其子容器设置为任意大小。
OverflowBox
与 UnconstrainedBox
类似,但不同的是,如果其子级超出该空间,它将不会显示任何警告。
在这种情况下,容器的宽度为 4000 像素,并且太大而无法容纳在 OverflowBox
中,但是 OverflowBox
会全部显示,而不会发出警告。
样例 16
UnconstrainedBox(
child: Container(color: Colors.red, width: double.infinity, height: 100),
这将不会渲染任何东西,而且你能在控制台看到错误信息。
UnconstrainedBox
让它的子级决定成为任何大小,但是其子级是一个具有无限大小的 Container
。
Flutter 无法渲染无限大的东西,所以它抛出以下错误:
BoxConstraints forces an infinite width.
(盒子约束强制使用了无限的宽度)
样例 17
child: FittedBox(
child: Text(
'This is some very very very large text that is too big to fit a regular screen in a single line.'),
然而,如果 FittedBox
位于 Center
中,但 Text
太大而超出屏幕,会发生什么?
FittedBox 会尝试根据 Text
大小调整大小,但不能大于屏幕大小。然后假定屏幕大小,并调整 Text
的大小以使其也适合屏幕。
样例 21
const Center(
child: Text(
'This is some very very very large text that is too big to fit a regular screen in a single line.'),
然而,如果你删除了 FittedBox
,
Text
则会从屏幕上获取其最大宽度,并在合适的地方换行。
样例 22
children: [
Container(color: red, child: const Text('Hello!', style: big)),
Container(color: green, child: const Text('Goodbye!', style: big)),
屏幕强制 Row
变得和屏幕一样大,所以 Row
充满屏幕。
和 UnconstrainedBox
一样,
Row
也不会对其子代施加任何约束,而是让它们成为所需的任意大小。
Row
然后将它们并排放置,任何多余的空间都将保持空白。
样例 24
如果你使用 Flexible
而不是 Expanded
的话,唯一的区别是,Flexible
会让其子级具有与
Flexible
相同或者更小的宽度。而 Expanded
将会强制其子级具有和
Expanded
相同的宽度。但无论是 Expanded
还是 Flexible
在它们决定子级大小时都会忽略其宽度。
BoxConstraints.tight(Size size)
: minWidth = size.width,
maxWidth = size.width,
minHeight = size.height,
maxHeight = size.height;
如果你重新阅读 样例 2,它告诉我们屏幕强制 Container
变得和屏幕一样大。为何屏幕能够做到这一点,原因就是给 Container
传递了严格约束。
一个 宽松 约束,换句话来说就是设置了最大宽度/高度,但是让允许其子 widget 获得比它更小的任意大小。换句话来说,宽松约束的最小宽度/高度为 0。
BoxConstraints.loose(Size size)
: minWidth = 0.0,
maxWidth = size.width,
minHeight = 0.0,
maxHeight = size.height;
如果你访问 样例 3,它将会告诉我们 Center
让红色的 Container
变得更小,但是不能超出屏幕。Center
能够做到这一点的原因就在于给 Container
的是一个宽松约束。总的来说,Center
起的作用就是从其父级(屏幕)那里获得的严格约束,为其子级(Container
)转换为宽松约束。
掌握通用布局是非常重要的,但这还不够。
应用一般规则时,每个 widget 都具有很大的自由度,所以没有办法只看 widget 的名称就知道可能它长什么样。
如果你尝试推测,可能就会猜错。除非你已阅读 widget 的文档或研究了其源代码,否则你无法确切知道 widget 的行为。
布局源代码通常很复杂,因此阅读文档是更好的选择。但是当你在研究布局源代码时,可以使用 IDE 的导航功能轻松找到它。
下面是一个例子:
Marcelo Glasberg 最初在 Medium 发表 Flutter: The Advanced Layout Rule Even Beginners Must Know 本文。我们十分喜欢这篇文章,在征得他的允许后发布在 flutter.dev。再次感谢你,Marcelo!
你可以找到 GitHub 以及 pub.dev 找到 Marcelo。
同时,还要感谢 Simon Lightfoot 创造了本文的标题图片。