Flutter 3.41 is live! Check out theFlutter 3.41 blog post!
Build a Flutter layout
Learn how to build a layout in Flutter.
- How to lay out widgets next to each other.
- How to add space between widgets.
- How adding and nesting widgets results in a Flutter layout.
This tutorial explains how to design and build layouts in Flutter.
If you use the example code provided, you can build the following app.

The finished app.
Photo byDino Reichmuth onUnsplash. Text bySwitzerland Tourism.
To get a better overview of the layout mechanism, start withFlutter's approach to layout.
Diagram the layout
#In this section, consider what type of user experience you want for your app users.
Consider how to position the components of your user interface. A layout consists of the total end result of these positionings. Consider planning your layout to speed up your coding. Using visual cues to know where something goes on screen can be a great help.
Use whichever method you prefer, like an interface design tool or a pencil and a sheet of paper. Figure out where you want to place elements on your screen before writing code. It's the programming version of the adage: "Measure twice, cut once."
Ask these questions to break the layout down to its basic elements.
- Can you identify the rows and columns?
- Does the layout include a grid?
- Are there overlapping elements?
- Does the UI need tabs?
- What do you need to align, pad, or border?
Identify the larger elements. In this example, you arrange the image, title,buttons, and description into a column.
Major elements in the layout: image, row, row, and text block
Diagram each row.
Row 1, theTitle section, has three children:a column of text, a star icon, and a number.Its first child, the column, contains two lines of text.That first column might need more space.
Title section with text blocks and an icon
Row 2, theButton section, has three children: each child containsa column which then contains an icon and text.
The Button section with three labeled buttons
After diagramming the layout, consider how you would code it.
Would you write all the code in one class? Or, would you create one class for each part of the layout?
To follow Flutter best practices, create one class, or Widget, to contain each part of your layout. When Flutter needs to re-render part of a UI, it updates the smallest part that changes. This is why Flutter makes "everything a widget". If only the text changes in aText widget, Flutter redraws only that text. Flutter changes the least amount of the UI possible in response to user input.
For this tutorial, write each element you have identified as its own widget.
Create the app base code
#In this section, shell out the basic Flutter app code to start your app.
Replace the contents of
lib/main.dartwith the following code. This app uses a parameter for the app title and the title shown on the app'sappBar. This decision simplifies the code.dartimport'package:flutter/material.dart';voidmain()=>runApp(constMyApp());classMyAppextendsStatelessWidget{constMyApp({super.key});@overrideWidgetbuild(BuildContextcontext){constStringappTitle='Flutter layout demo';returnMaterialApp(title:appTitle,home:Scaffold(appBar:AppBar(title:constText(appTitle)),body:constCenter(child:Text('Hello World'),),),);}}
Add the Title section
# In this section, create aTitleSection widget that resembles the following layout.
The Title section as sketch and prototype UI
Add theTitleSection Widget
#Add the following code after theMyApp class.
classTitleSectionextendsStatelessWidget{constTitleSection({super.key,requiredthis.name,requiredthis.location});finalStringname;finalStringlocation;@overrideWidgetbuild(BuildContextcontext){returnPadding(padding:constEdgeInsets.all(32),child:Row(children:[Expanded(/*1*/child:Column(crossAxisAlignment:CrossAxisAlignment.start,children:[/*2*/Padding(padding:constEdgeInsets.only(bottom:8),child:Text(name,style:constTextStyle(fontWeight:FontWeight.bold),),),Text(location,style:TextStyle(color:Colors.grey[500])),],),),/*3*/Icon(Icons.star,color:Colors.red[500]),constText('41'),],),);}}- To use all remaining free space in the row, use the
Expandedwidget to stretch theColumnwidget. To place the column at the start of the row, set thecrossAxisAlignmentproperty toCrossAxisAlignment.start. - To add space between the rows of text, put those rows in a
Paddingwidget. - The title row ends with a red star icon and the text
41. The entire row falls inside aPaddingwidget and pads each edge by 32 pixels.
Change the app body to a scrolling view
# In thebody property, replace theCenter widget with aSingleChildScrollView widget. Within theSingleChildScrollView widget, replace theText widget with aColumn widget.
body:constCenter(child:Text('Hello World'),body:constSingleChildScrollView(child:Column(children:[These code updates change the app in the following ways.
- A
SingleChildScrollViewwidget can scroll. This allows elements that don't fit on the current screen to display. - A
Columnwidget displays any elements within itschildrenproperty in the order listed. The first element listed in thechildrenlist displays at the top of the list. Elements in thechildrenlist display in array order on the screen from top to bottom.
Update the app to display the title section
# Add theTitleSection widget as the first element in thechildren list. This places it at the top of the screen. Pass the provided name and location to theTitleSection constructor.
children:[TitleSection(name:'Oeschinen Lake Campground',location:'Kandersteg, Switzerland',),],- When pasting code into your app, indentation can become skewed. To fix this in your Flutter editor, useautomatic reformatting support.
- To accelerate your development, try Flutter'shot reload feature.
- If you have problems, compare your code to
lib/main.dart.
Add the Button section
#In this section, add the buttons that will add functionality to your app.
TheButton section contains three columns that use the same layout: an icon over a row of text.
The Button section as sketch and prototype UI
Plan to distribute these columns in one row so each takes the same amount of space. Paint all text and icons with the primary color.
Add theButtonSection widget
# Add the following code after theTitleSection widget to contain the code to build the row of buttons.
classButtonSectionextendsStatelessWidget{constButtonSection({super.key});@overrideWidgetbuild(BuildContextcontext){finalColorcolor=Theme.of(context).primaryColor;// ···}}Create a widget to make buttons
# As the code for each column could use the same syntax, create a widget namedButtonWithText. The widget's constructor accepts a color, icon data, and a label for the button. Using these values, the widget builds aColumn with anIcon and a stylizedText widget as its children. To help separate these children, aPadding widget theText widget is wrapped with aPadding widget.
Add the following code after theButtonSection class.
classButtonSectionextendsStatelessWidget{constButtonSection({super.key});// ···}classButtonWithTextextendsStatelessWidget{constButtonWithText({super.key,requiredthis.color,requiredthis.icon,requiredthis.label,});finalColorcolor;finalIconDataicon;finalStringlabel;@overrideWidgetbuild(BuildContextcontext){returnColumn(mainAxisSize:MainAxisSize.min,mainAxisAlignment:MainAxisAlignment.center,children:[Icon(icon,color:color),Padding(padding:constEdgeInsets.only(top:8),child:Text(label,style:TextStyle(fontSize:12,fontWeight:FontWeight.w400,color:color,),),),],);}}Position the buttons with aRow widget
#Add the following code into theButtonSection widget.
- Add three instances of the
ButtonWithTextwidget, once for each button. - Pass the color,
Icon, and text for that specific button. - Align the columns along the main axis with the
MainAxisAlignment.spaceEvenlyvalue. The main axis for aRowwidget is horizontal and the main axis for aColumnwidget is vertical. This value, then, tells Flutter to arrange the free space in equal amounts before, between, and after each column along theRow.
classButtonSectionextendsStatelessWidget{constButtonSection({super.key});@overrideWidgetbuild(BuildContextcontext){finalColorcolor=Theme.of(context).primaryColor;returnSizedBox(child:Row(mainAxisAlignment:MainAxisAlignment.spaceEvenly,children:[ButtonWithText(color:color,icon:Icons.call,label:'CALL'),ButtonWithText(color:color,icon:Icons.near_me,label:'ROUTE'),ButtonWithText(color:color,icon:Icons.share,label:'SHARE'),],),);}}classButtonWithTextextendsStatelessWidget{constButtonWithText({super.key,requiredthis.color,requiredthis.icon,requiredthis.label,});finalColorcolor;finalIconDataicon;finalStringlabel;@overrideWidgetbuild(BuildContextcontext){returnColumn(// ···);}}Update the app to display the button section
#Add the button section to thechildren list.
TitleSection(name:'Oeschinen Lake Campground',location:'Kandersteg, Switzerland',),ButtonSection(),],Add the Text section
#In this section, add the text description to this app.
The text block as sketch and prototype UI
Add theTextSection widget
#Add the following code as a separate widget after theButtonSection widget.
classTextSectionextendsStatelessWidget{constTextSection({super.key,requiredthis.description});finalStringdescription;@overrideWidgetbuild(BuildContextcontext){returnPadding(padding:constEdgeInsets.all(32),child:Text(description,softWrap:true),);}} By settingsoftWrap totrue, text lines fill the column width before wrapping at a word boundary.
Update the app to display the text section
# Add a newTextSection widget as a child after theButtonSection. When adding theTextSection widget, set itsdescription property to the text of the location description.
location:'Kandersteg, Switzerland',),ButtonSection(),TextSection(description:'Lake Oeschinen lies at the foot of the Blüemlisalp in the''Bernese Alps. Situated 1,578 meters above sea level, it''is one of the larger Alpine Lakes. A gondola ride from''Kandersteg, followed by a half-hour walk through pastures''and pine forest, leads you to the lake, which warms to 20''degrees Celsius in the summer. Activities enjoyed here''include rowing, and riding the summer toboggan run.',),],Add the Image section
#In this section, add the image file to complete your layout.
Configure your app to use supplied images
#To configure your app to reference images, modify itspubspec.yaml file.
Create an
imagesdirectory at the top of the project.Download the
lake.jpgimage and add it to the newimagesdirectory.NoteTo include images, add an
assetstag to thepubspec.yamlfile at the root directory of your app. When you addassets, it serves as the set of pointers to the images available to your code.pubspec.yamlyamlflutter:uses-material-design:trueassets:-images/lake.jpg
Text in thepubspec.yaml respects whitespace and text case. Write the changes to the file as given in the previous example.
This change might require you to restart the running program to display the image.
Create theImageSection widget
#Define the followingImageSection widget after the other declarations.
classImageSectionextendsStatelessWidget{constImageSection({super.key,requiredthis.image});finalStringimage;@overrideWidgetbuild(BuildContextcontext){returnImage.asset(image,width:600,height:240,fit:BoxFit.cover);}} TheBoxFit.cover value tells Flutter to display the image with two constraints. First, display the image as small as possible. Second, cover all the space that the layout allotted, called the render box.
Update the app to display the image section
# Add anImageSection widget as the first child in thechildren list. Set theimage property to the path of the image you added inConfigure your app to use supplied images.
children:[ImageSection(image:'images/lake.jpg',),TitleSection(name:'Oeschinen Lake Campground',location:'Kandersteg, Switzerland',Congratulations
#That's it! When you hot reload the app, your app should look like this.

The finished app
Resources
#You can access the resources used in this tutorial from these locations:
Dart code:main.dart
Image:ch-photo
Pubspec:pubspec.yaml
Next Steps
#To add interactivity to this layout, follow theinteractivity tutorial.
Unless stated otherwise, the documentation on this site reflects Flutter 3.38.6. Page last updated on 2026-01-03.View source orreport an issue.