This article seeks to explain the difference between padding and margins in a Flutter application layout. In this article, while covering the box model, we will build a demo Flutter application with intermediate to advanced layout examples and demonstrate how to implement, set, and dynamically change the margins and padding of widgets. We will also learn how to pad and decorate boxes in a Flutter application.
What is a Flutter Widget
Flutter is an open-source framework developed by Google for building beautiful, natively compiled cross-platform applications from a single code base. Flutter applications by design are made up of various widgets which are either visible or invisible. Widgets are the basic building blocks of a Flutter application. It determines what each element on the screen of a flutter application should look like. Generally, the code structure of a Flutter application is a tree of widgets. Flutter widgets belong to the following categories:
- Basics: These are widgets that are a basic requirement for every Flutter application.
- Layouts: Widgets under this category are responsible for Flutter application layouts, they help to set the application screen in order.
- Cupertino: These are widgets designed for IOS applications.
- Interaction Models: These are the widgets in charge of events such as touch and manage routes to different views.
- Material Components: Widgets under this category follow the Google material design pattern.
- Async: Async widgets provide asynchronous functionality in a Flutter application.
- Scrolling: These widgets make other widgets which are not scrollable by default scrollable.
- Styling: Widgets under this category manage responsiveness, styles, and sizes in a Flutter application.
- Text: These widgets manage text characters.
- Inputs: These widgets manage input functionality in a Flutter application.
- Painting and effects: These sets of widgets apply visual effects to their child widgets without changing their shape or layouts.
- Animation and Motion: These sets of widgets manage animation and motion in a Flutter application.
- Assets, Images, Icons: These sets of widgets manage assets, images and icons in a Flutter application.
- Accessibility widgets: These sets of Flutter widgets make an application accessible.
Widget State
Generally, Flutter widgets are divided into two broad categories: stateless and stateful widgets.
The state of a widget in a Flutter application refers to its ability to display on the screen, and be altered during which application is in use. A widget is said to be stateful when it changes dynamically during runtime while a stateless widget doesn’t change dynamically.
If your post includes code examples, format them using available extensions (from the Google Doc menu; e.g., “Code Blocks” and “Code Blocks II”). You could also write your work in markdown and then convert it to Google Docs using an available extension, instead. Another option would be to copy your code directly from your IDE and paste it into the Google Doc.
Flutter Layouts
Now that we have an understanding of widgets and their state, let’s focus on layout widgets and explore the padding and margin in laying out a Flutter application UI. There are two important widgets to consider when laying out a Flutter application viz: Container and Padding widgets.
Container Widget
Every displayed widget in a Flutter application has the following content: padding, border, and margin. All of these together constitute the box model. The container widget makes manipulating the box model easy and manageable. A container that stores a widget has a margin separating widgets from another, padding which encloses the child (content) and a border of different shapes of choice. Basically, a container is like a box containing items, as the name implies. Now, let’s illustrate all we’ve said so far with a simple text app.
The app below renders a text to the screen and an appBar.
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( home: Scaffold( appBar: AppBar( title: const Text("Flutter Layout Guide"), backgroundColor: Colors.green, ), body: Container( decoration: const BoxDecoration( color: Colors.blue, ), child: const Text( "Exploring Flutter Container class!", style: TextStyle(fontSize: 20)), ), ), ); } }
As with every Flutter application, you first have to import the flutter/material.dart package into your application. Your app should look exactly like the one in the image below:
The code snippet above renders the appBar with text and a text container without whitespace, and it doesn’t look really nice, to make it look a lot nicer we need to introduce margin property.
Margin Property
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( home: Scaffold( appBar: AppBar( title: const Text("Flutter Layout Guide"), backgroundColor: Colors.green, ), body: Container( decoration: const BoxDecoration( color: Colors.blue, ), margin: const EdgeInsets.all(20), child: const Text( "Exploring Flutter Container class!", style: TextStyle(fontSize: 20)), ), ), ); } }
The margin property accepts a value of EdgeInsets, which is a subclass of EdgeInsetsGeometry. The margin property is used to create empty space to surround an element.
EdgetInsets sets margin and padding through the top, left, right, and bottom parameters. It has the following properties:
- EdgetInsets.all: This set one value for all required parameters
- EdgetInsets.zero: This set zero for required parameters
- EdgetInsets.only: This set the values for the specified parameters left, top, bottom, and right direction
- EdgetInsets.LTRB: This set the values for directions as specified and this order left, top, right, bottom
- EdgetInsets.symmetric: Set horizontal or vertical direction
In the code above we used the EdgetInsets.all() to specify the margin value for parameters. It provides an empty space around the container.
Padding Property
The padding property is used to create empty space inside the decoration. Padding can be added to a widget using the EdgetInsets constructor as with margin.
- EdgetInsets.all
- EdgetInsets.symmetric
- EdgetInsets.LTRB
- EdgetInsets.only
- EdgetInsets.all
The code snippet below add padding to the UI
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
This adds horizontal and vertical padding of 20 to our container.
Implementing a UI Layout
Now let’s build out a UI from the things we’ve learned so far using margin and padding properties. In this tutorial we are using Android studio, visit the link to download if you don’t have it already. I assume this is not your first attempt with Flutter and so you should have Flutter installed in your computer already. Otherwise, you visit this link for the Flutter installation guide.
Follow the steps below so you can get started:
- Create a Flutter project on your Android studio
On your Android studio click on file
Select new and new Flutter project
The path should show Flutter SDK selected
Click next
Change project name field to your project name
Click finish and voila, you’re set to start building your first Flutter UI - Locate the lib folder in your project structure
Open the main.dart file, where you’ll write your Flutter code.
Now we are ready to start writing code, are you ready? Let’s go!
Write the following code in your main.dart file:
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( theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomepage(title: "Flutter Margin and Padding Demo"), ); } }
The main () method is the entry point into every Flutter application. The code above represents a basic anatomy of a Flutter application, the extra addition in the code is MyHomepage widget that takes title as an argument.
class MyHomepage extends StatefulWidget{ const MyHomepage({Key? key, required this.title}):super(key: key); final String title; @override State<MyHomepage> createState()=> _MyHomePageState(); }
We create MyHomepage class (widget) using the extend keyword and make the features available in the StatefulWidget class accessible in the MyHomepage widget. MyHomepage constructor takes title as a parameter which is of type String. The createState() method, as the name implies creates mutable state for our widget (MyHomepage) at every point in our application, it is advisable to always override this method to be able to return a new instance of the state of the subclass. Now let’s begin to flesh out our application.
Add the following code:
class _MyHomePageState extends State<MyHomepage>{ @override Widget build(BuildContext context){ return Scaffold( appBar: AppBar( title: Text(widget.title), ),
Now we have our application scaffold with an app bar showing the title “Flutter Margin and Padding Demo
”. Next we need to add a Column widget, this helps us set arrays of vertical widgets (elements) to build a Flutter app. Next, add the following code to the scaffold:
body: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[]
We have a Column widget expecting a list of children widget to help us flesh our application. The first widget we are adding is a container widget, remember the box model we talked about earlier, this is where we’d have a full implementation of it. Add the code below into the list of children widget [].
Container( margin: const EdgeInsets.only(bottom: 18), alignment: const Alignment(-0.8, 1.0), child: RichText( text: const TextSpan( text: "Hello there!", style: TextStyle( color: Colors.redAccent, fontSize: 26, fontWeight: FontWeight.bold ), children: [ TextSpan( text: "\nWelcome back", style: TextStyle( fontSize: 23, color: Colors.black, fontWeight: FontWeight.normal ) ), ] ), ), ),
This lays out the first container element of our demo application, we used the RichText widget to create a paragraph of text, our text has an EdgetInsets.only bottom (remember we’ve explained this earlier) margin of 18. Meaning the text has an empty space of 18 pixels from the bottom. The next thing we need to add to our Column widget immediately after the Container widget is the Padding widget. To do that add the following code:
const Padding( padding: EdgeInsets.all(16.0), child: TextField( obscureText: false, decoration: InputDecoration( border: OutlineInputBorder( borderSide: BorderSide.none, borderRadius: BorderRadius.all(Radius.circular(50)) ), hintText: "Email", contentPadding: EdgeInsets.only(left: 30), fillColor: Color.fromARGB(84, 100, 117, 135), filled: true, ), ), ),
We’re building out our demo login UI, the code above specifies a text field, we used the hintText to specify the kind of text we’re expecting in the input field. To complete our login UI, we’ll require an email, and password as input. We have an input field for email now and we also need to implement an input field for password. To implement a password input field, add the following code to your code immediately after the first padding widget:
const Padding( padding: EdgeInsets.all(16.0), child: TextField( obscureText: true, decoration: InputDecoration( border: OutlineInputBorder( borderSide: BorderSide.none, borderRadius: BorderRadius.all(Radius.circular(50)) ), hintText: "Password", contentPadding: EdgeInsets.only(left: 30), fillColor: Color.fromARGB(84, 100, 117, 135), filled: true, ), ), ),
The next step is to add a forgot password text after the password text field. To achieve that, add the code below to your code:
Container( alignment: Alignment.centerRight, width: MediaQuery.of(context).size.width * 0.85, padding: const EdgeInsets.only(left: 10), child: const Text("Forgot Password?"), ),
Now we have the following app bar, two text fields (Email and Password), and a forgot password option. We need a login button. To implement that, we need another container widget. The code below helps us implement the login button.
Container( margin: const EdgeInsets.only(top: 90, bottom: 10), width: MediaQuery.of(context).size.width * 0.92, padding: const EdgeInsets.all(16), decoration: const BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(30)), color: Color.fromARGB(255, 77, 73, 73), ), child: const Text( "LogIn", style: TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, ), ),
We’ve finally implemented a simple login UI interface using the knowledge from the Padding and Margin widgets. If you noticed, we introduced the mediaQuery element to the forgot password and the login container elements, this helps us to dynamically resize each component to fit screen size. To enable users to login with other options like Google and Github accounts we need to implement that with code, but first we need to add an icon dependency to our project. To do that, click on the pubspec.yaml file and add this dependency font_awesome_flutter: ^10.1.0
under dependencies, click on pub get to update and install the dependency.
Now that the font awesome icon dependency has been added to our application, we need to create container widgets for Google signin and Github signin respectively.
Container( margin: const EdgeInsets.all(20), decoration: const BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(30)), color: Color.fromARGB(84, 100, 117, 135), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: const[ Padding( padding: EdgeInsets.only(left: 25.0, right: 45), child: Icon( FontAwesomeIcons.google, size: 22.0, ), ), Expanded(child: Padding( padding: EdgeInsets.all(16), child: Text( "Sign in with Google", style: TextStyle( fontSize: 18 ), ), )) ], ), ),
This first container manages the Google sign in button. The next container will manage the Github sign in button.
Container( margin: const EdgeInsets.all(20), decoration: const BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(30)), color: Color.fromARGB(84, 100, 117, 135), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: const [ Padding( padding: EdgeInsets.only(left: 25.0, right: 45), child: Icon( FontAwesomeIcons.github, size: 22.0, ), ), Expanded(child: Padding( padding: EdgeInsets.all(16), child: Text( "Sign in with Github", style: TextStyle( fontSize: 18 ), ), ) ) ], ), ),
The next thing is to create a “don’t have an account and sign up here” option, considering this is a first time user.
We will do this with a single container. The following code snippet implements these options:
Container( margin: const EdgeInsets.all(20.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: const [ Text("Don't have an account?"), SizedBox( width: 5, ), Text( "Sign up here", style: TextStyle( color: Colors.blue ), ) ], ), ) ], ), ); } }
We’ve finally implemented the complete login UI interface with Flutter. Your app should look like the one below.
Conclusion
In this tutorial we have an overview of the Flutter widget, state, margin, padding and their properties. We also explore the container widget and finally we are able to build a simple login UI. Here is the complete code:
import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget{ const MyApp({Key? key}): super(key: key); @override Widget build(BuildContext context){ return MaterialApp( theme: ThemeData( primarySwatch: Colors.green, ), home: const MyHomepage(title: "Flutter Margin and Padding Demo"), ); } } class MyHomepage extends StatefulWidget{ const MyHomepage( {Key? key,required this.title}):super(key: key); final String title; @override State<MyHomepage> createState()=> _MyHomePageState(); } class _MyHomePageState extends State<MyHomepage>{ @override Widget build(BuildContext context){ return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Container( margin: const EdgeInsets.only(bottom: 18), alignment: const Alignment(-0.8, 1.0), child: RichText( text: const TextSpan( text: "Hello there!", style: TextStyle( color: Colors.redAccent, fontSize: 26, fontWeight: FontWeight.bold ), children: [ TextSpan( text: "\nWelcome back", style: TextStyle( fontSize: 23, color: Colors.black, fontWeight: FontWeight.normal ) ), ] ), ), ), const Padding( padding: EdgeInsets.all(16.0), child: TextField( obscureText: false, decoration: InputDecoration( border: OutlineInputBorder( borderSide: BorderSide.none, borderRadius: BorderRadius.all(Radius.circular(50)) ), hintText: "Email", contentPadding: EdgeInsets.only(left: 30), fillColor: Color.fromARGB(84, 100, 117, 135), filled: true, ), ), ), const Padding( padding: EdgeInsets.all(16.0), child: TextField( obscureText: true, decoration: InputDecoration( border: OutlineInputBorder( borderSide: BorderSide.none, borderRadius: BorderRadius.all(Radius.circular(50)) ), hintText: "Password", contentPadding: EdgeInsets.only(left: 30), fillColor: Color.fromARGB(84, 100, 117, 135), filled: true, ), ), ), Container( alignment: Alignment.centerRight, width: MediaQuery.of(context).size.width * 0.85, padding: const EdgeInsets.only(left: 10), child: const Text("Forgot Password?"), ), Container( margin: const EdgeInsets.only(top: 90, bottom: 10), width: MediaQuery.of(context).size.width * 0.92, padding: const EdgeInsets.all(16), decoration: const BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(30)), color: Color.fromARGB(255, 77, 73, 73), ), child: const Text( "LogIn", style: TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, ), ), Container( margin: const EdgeInsets.all(20), decoration: const BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(30)), color: Color.fromARGB(84, 100, 117, 135), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: const[ Padding( padding: EdgeInsets.only(left: 25.0, right: 45), child: Icon( FontAwesomeIcons.google, size: 22.0, ), ), Expanded(child: Padding( padding: EdgeInsets.all(16), child: Text( "Sign in with Google", style: TextStyle( fontSize: 18 ), ), )) ], ), ), Container( margin: const EdgeInsets.all(20), decoration: const BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(30)), color: Color.fromARGB(84, 100, 117, 135), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: const [ Padding( padding: EdgeInsets.only(left: 25.0, right: 45), child: Icon( FontAwesomeIcons.github, size: 22.0, ), ), Expanded(child: Padding( padding: EdgeInsets.all(16), child: Text( "Sign in with Github", style: TextStyle( fontSize: 18 ), ), ) ) ], ), ), Container( margin: const EdgeInsets.all(20.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: const [ Text("Don't have an account?"), SizedBox( width: 5, ), Text( "Sign up here", style: TextStyle( color: Colors.blue ), ) ], ), ) ], ), ); } }