Designing an app with Flutter is quite simple, and the process is easy to understand even if you are a beginner.
This guide will help you build your first Flutter app. It will teach you some of the core UI and design elements that come together to make an app, like font, theme, orientation, snackbar, and drawer.
Getting Started With Flutter
Getting started with Flutter doesn’t require you to have extensive app-developing experience. There are several Flutter tutorials to guide you through the complete process of creating an app here.
What makes Flutter programming even simpler is that all apps are written in the Dart language. So you also don’t need to know the OS-specific programming languages like Swift or Java.
To get started, you will have to download and install the Flutter SDK. Next, download and install Android Studio.
If you don’t have Android Studio, you can use any other IDE to develop a Flutter app. We are using Android Studio because it has many features that help you develop Flutter apps faster.
Once it's installed, you are ready to start your project.
Build Your First Flutter App
After getting started with Flutter and setting up the SDK and IDE, you have to create a project.
Open Android Studio and create a new Flutter project.
.png)
.png)
.png)
Next, you have to give your project a name, select its Flutter SDK path, set the location of the project, and click Next. Now enter the project name. We would like to name it my design.
After you add everything and press Next and then Finish, Android Studio will set you up with some starter code and launch the project. Here's what the starter code looks like:
1 |
// Copyright 2018 The Flutter team. All rights reserved. |
2 |
// Use of this source code is governed by a BSD-style license that can be |
3 |
// found in the LICENSE file. |
4 |
|
5 |
import 'package:flutter/material.dart'; |
6 |
|
7 |
void main() => runApp(MyApp()); |
8 |
|
9 |
class MyApp extends StatelessWidget { |
10 |
@override |
11 |
Widget build(BuildContext context) { |
12 |
return MaterialApp( |
13 |
title: 'Welcome to My Design', |
14 |
home: Scaffold( |
15 |
appBar: AppBar( |
16 |
title: Text('Welcome to Flutter'), |
17 |
), |
18 |
body: Center( |
19 |
child: Text('Hello World'), |
20 |
), |
21 |
), |
22 |
); |
23 |
} |
24 |
} |
Now you can run the project to see how the app looks.
.png)
.png)
.png)
Great! This was the first step to building your first Flutter app. So far it is a simple app with a top bar and a button in the middle.
The Material Design Package
Now, if we go back to the top of the code, we will see this line:
1 |
import 'package:flutter/material.dart'; |
The starter app uses Material Design, which is a design system created by Google. With Flutter, it is easy to create a Material Design app—we get to tap into a lot of pre-built material components and widgets.
To use Material Design in Flutter, import the material.dart
package. These widgets take care of everything from font to alignment to theme. Of course, you don't have to use Material Design—the Flutter framework is designed in such a way that it creates layers of objects, and every part of the framework level is optional and replaceable.
Let's customize our app. We'll start with the text widgets.
1 |
import 'package:flutter/material.dart'; |
2 |
|
3 |
void main() => runApp(MyApp()); |
4 |
|
5 |
class MyApp extends StatelessWidget { |
6 |
@override |
7 |
Widget build(BuildContext context) { |
8 |
return MaterialApp( |
9 |
debugShowCheckedModeBanner: false, |
10 |
home: Scaffold( |
11 |
body: BodyWidget(), |
12 |
), |
13 |
); |
14 |
} |
15 |
} |
16 |
|
17 |
class BodyWidget extends StatelessWidget { |
18 |
|
19 |
|
20 |
@override |
21 |
Widget build(BuildContext context) { |
22 |
return Align( |
23 |
alignment: Alignment.center, |
24 |
child: Text( |
25 |
'Hello, Max! How are you?', |
26 |
textAlign: TextAlign.center, |
27 |
overflow: TextOverflow.ellipsis, |
28 |
style: const TextStyle(fontWeight: FontWeight.bold), |
29 |
), |
30 |
); |
31 |
} |
32 |
|
33 |
} |
The result will look like this:
.png)
.png)
.png)
The text widget will display the text in the default font, which will break into lines based on the layout. However, you can style the text using the Text.rich
constructor. It will show a paragraph with differently styled text.
.png)
.png)
.png)
Here is the rich text code for reference:
1 |
import 'package:flutter/material.dart'; |
2 |
|
3 |
void main() => runApp(MyApp()); |
4 |
|
5 |
class MyApp extends StatelessWidget { |
6 |
@override |
7 |
Widget build(BuildContext context) { |
8 |
return MaterialApp( |
9 |
debugShowCheckedModeBanner: false, |
10 |
home: Scaffold( |
11 |
body: BodyWidget(), |
12 |
), |
13 |
); |
14 |
} |
15 |
} |
16 |
|
17 |
class BodyWidget extends StatelessWidget { |
18 |
|
19 |
|
20 |
@override |
21 |
Widget build(BuildContext context) { |
22 |
return Align( |
23 |
alignment: Alignment.center, |
24 |
child: Text.rich( |
25 |
TextSpan( |
26 |
text: 'Hello', // default text style |
27 |
children: <TextSpan>[ |
28 |
TextSpan(text: ' beautiful ', style: TextStyle(fontStyle: FontStyle.italic)), |
29 |
TextSpan(text: 'world', style: TextStyle(fontWeight: FontWeight.bold)), |
30 |
], |
31 |
), |
32 |
), |
33 |
); |
34 |
} |
35 |
|
36 |
} |
(An extra tip: If you want to make your text react to touch events, use a Gesture Detector widget and wrap the text in that. I'll explain this in a follow-up post.)
Design Elements for a Flutter App
Here are the major design elements you might want to use in your first Flutter app:
Themes
The Flutter UI is all about allowing people to create apps that are appealing and attractive. For starters, Flutter will give your app the default theme if no other theme is selected.
Now, you can play with the code and test everything that Flutter can do.
Here is an example:
.png)
.png)
.png)
And its code should look like this:
1 |
import 'package:flutter/material.dart'; |
2 |
import 'package:flutter/foundation.dart'; |
3 |
|
4 |
|
5 |
void main() => runApp(MyApp()); |
6 |
|
7 |
|
8 |
class MyApp extends StatelessWidget { |
9 |
@override |
10 |
Widget build(BuildContext context) { |
11 |
final appName = 'Custom Themes'; |
12 |
|
13 |
return MaterialApp( |
14 |
title: appName, |
15 |
theme: ThemeData( |
16 |
// Define the default brightness and colors. |
17 |
brightness: Brightness.dark, |
18 |
primaryColor: Colors.lightBlue[800], |
19 |
accentColor: Colors.cyan[600], |
20 |
|
21 |
// Define the default font family. |
22 |
fontFamily: 'Georgia', |
23 |
|
24 |
// Define the default TextTheme. Use this to specify the default |
25 |
// text styling for headlines, titles, bodies of text, and more. |
26 |
textTheme: TextTheme( |
27 |
headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold), |
28 |
headline6: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic), |
29 |
bodyText2: TextStyle(fontSize: 14.0, fontFamily: 'Hind'), |
30 |
), |
31 |
), |
32 |
home: MyHomePage( |
33 |
title: appName, |
34 |
), |
35 |
); |
36 |
} |
37 |
} |
38 |
|
39 |
class MyHomePage extends StatelessWidget { |
40 |
final String title; |
41 |
|
42 |
MyHomePage({Key? key, required this.title}) : super(key: key); |
43 |
|
44 |
@override |
45 |
Widget build(BuildContext context) { |
46 |
return Scaffold( |
47 |
appBar: AppBar( |
48 |
title: Text(title), |
49 |
), |
50 |
body: Center( |
51 |
child: Container( |
52 |
color: Theme.of(context).accentColor, |
53 |
child: Text( |
54 |
'Text with a background color', |
55 |
style: Theme.of(context).textTheme.headline6, |
56 |
), |
57 |
), |
58 |
), |
59 |
|
60 |
); |
61 |
} |
62 |
} |
Here is another example of how you can change the color of the background and add different elements to improve your app:
.png)
.png)
.png)
Here is how its code goes:
1 |
import 'package:flutter/material.dart'; |
2 |
import 'package:flutter/foundation.dart'; |
3 |
|
4 |
|
5 |
void main() => runApp(MyApp()); |
6 |
|
7 |
|
8 |
class MyApp extends StatelessWidget { |
9 |
@override |
10 |
Widget build(BuildContext context) { |
11 |
final appName = 'Custom Themes'; |
12 |
|
13 |
return MaterialApp( |
14 |
title: appName, |
15 |
|
16 |
home: MyHomePage( |
17 |
title: appName, |
18 |
), |
19 |
); |
20 |
} |
21 |
} |
22 |
|
23 |
class MyHomePage extends StatelessWidget { |
24 |
final String title; |
25 |
|
26 |
MyHomePage({Key? key, required this.title}) : super(key: key); |
27 |
|
28 |
@override |
29 |
Widget build(BuildContext context) { |
30 |
return Scaffold( |
31 |
appBar: AppBar( |
32 |
title: Text(title), |
33 |
), |
34 |
body: Center( |
35 |
child: Theme( |
36 |
// Create a unique theme with "ThemeData" |
37 |
data: ThemeData( |
38 |
accentColor: Colors.yellow, |
39 |
), |
40 |
child: FloatingActionButton( |
41 |
onPressed: () {}, |
42 |
child: Icon(Icons.add), |
43 |
), |
44 |
), |
45 |
), |
46 |
|
47 |
); |
48 |
} |
49 |
} |
Fonts
Flutter offers several custom fonts that you can apply to your app. These fonts can be applied to the entire app or to a specific widget.
To use custom fonts, you can import them into Flutter. Use this code to import the font of your choice:
1 |
awesome_app/ |
2 |
fonts/ |
3 |
Font-Regular.ttf |
4 |
Font-Italic.ttf |
5 |
Font-Regular.ttf |
6 |
Font-Bold.ttf |
After importing the fonts, you have to declare the font in the pubspec by including a font definition in the pubspec.yaml file.
1 |
flutter: |
2 |
fonts: |
3 |
- family: Raleway |
4 |
fonts: |
5 |
- asset: fonts/Raleway-Regular.ttf |
6 |
- asset: fonts/Raleway-Italic.ttf |
7 |
style: italic |
8 |
- family: RobotoMono |
9 |
fonts: |
10 |
- asset: fonts/RobotoMono-Regular.ttf |
11 |
- asset: fonts/RobotoMono-Bold.ttf |
12 |
weight: 700 |
You can download other free fonts from Google.
After this, you have two options:
- set a font as the default
- use a font in a specific widget
In the following code, you can see examples of how to do each of these options.
1 |
void main() => runApp(MyApp()); |
2 |
|
3 |
class MyApp extends StatelessWidget { |
4 |
@override |
5 |
Widget build(BuildContext context) { |
6 |
return MaterialApp( |
7 |
title: 'Custom Fonts', |
8 |
// Set Raleway as the default app font. |
9 |
theme: ThemeData(fontFamily: 'Raleway'), |
10 |
home: MyHomePage(), |
11 |
); |
12 |
} |
13 |
} |
14 |
|
15 |
class MyHomePage extends StatelessWidget { |
16 |
@override |
17 |
Widget build(BuildContext context) { |
18 |
return Scaffold( |
19 |
// The AppBar uses the app-default Raleway font. |
20 |
appBar: AppBar(title: Text('Custom Fonts')), |
21 |
body: Center( |
22 |
// This Text widget uses the RobotoMono font. |
23 |
child: Text( |
24 |
'Roboto Mono sample', |
25 |
style: TextStyle(fontFamily: 'RobotoMono'), |
26 |
), |
27 |
), |
28 |
); |
29 |
} |
30 |
} |
Here's what your app will look like:
.png)
.png)
.png)
This will apply the Roboto Mono font only to the text widget, while Raleway stays the default font of the app.
User Interface and Overall Layout
Flutter allows you to build different layouts according to the orientation of users’ devices. For example, you can build a grid view list with two columns in portrait mode and three in landscape mode.
First, create a grid with two columns.
1 |
GridView.count( |
2 |
// A list with 2 columns |
3 |
crossAxisCount: 2, |
4 |
// ... |
5 |
); |
Now, use the OrientationBuilder
widget to adapt the layout based on the device’s orientation.
1 |
OrientationBuilder( |
2 |
builder: (context, orientation) { |
3 |
return GridView.count( |
4 |
// Create a grid with 2 columns in portrait mode, |
5 |
// or 3 columns in landscape mode. |
6 |
crossAxisCount: orientation == Orientation.portrait ? 2 : 3, |
7 |
); |
8 |
}, |
9 |
); |
The layout will now have three columns in the horizontal orientation as compared to two in the vertical orientation. This is the complete code:
1 |
void main() { |
2 |
runApp(MyApp()); |
3 |
} |
4 |
|
5 |
class MyApp extends StatelessWidget { |
6 |
@override |
7 |
Widget build(BuildContext context) { |
8 |
final appTitle = 'Orientation Demo'; |
9 |
|
10 |
return MaterialApp( |
11 |
title: appTitle, |
12 |
home: OrientationList( |
13 |
title: appTitle, |
14 |
), |
15 |
); |
16 |
} |
17 |
} |
18 |
|
19 |
class OrientationList extends StatelessWidget { |
20 |
final String title; |
21 |
|
22 |
OrientationList({Key? key, required this.title}) : super(key: key); |
23 |
|
24 |
@override |
25 |
Widget build(BuildContext context) { |
26 |
return Scaffold( |
27 |
appBar: AppBar(title: Text(title)), |
28 |
body: OrientationBuilder( |
29 |
builder: (context, orientation) { |
30 |
return GridView.count( |
31 |
// Create a grid with 2 columns in portrait mode, or 3 columns in |
32 |
// landscape mode. |
33 |
crossAxisCount: orientation == Orientation.portrait ? 2 : 3, |
34 |
// Generate 100 widgets that display their index in the List. |
35 |
children: List.generate(100, (index) { |
36 |
return Center( |
37 |
child: Text( |
38 |
'Item $index', |
39 |
style: Theme.of(context).textTheme.bodyText1, |
40 |
), |
41 |
); |
42 |
}), |
43 |
); |
44 |
}, |
45 |
), |
46 |
); |
47 |
} |
48 |
} |
And here is what your app will look like:
.png)
.png)
.png)
Adding a Drawer
You can also add drawers to display additional information on your application.
The drawer is added by using a drawer widget, combined with a scaffold. The drawer is wrapped in the scaffold widget.
.png)
.png)
.png)
For an example of how to add a drawer in the scaffold and add content to the drawer, look at the following code:
1 |
void main() => runApp(MyApp()); |
2 |
|
3 |
class MyApp extends StatelessWidget { |
4 |
final appTitle = 'Drawer Demo'; |
5 |
|
6 |
@override |
7 |
Widget build(BuildContext context) { |
8 |
return MaterialApp( |
9 |
title: appTitle, |
10 |
home: Scaffold( |
11 |
body: MyHomePage(title: appTitle)), |
12 |
); |
13 |
} |
14 |
} |
15 |
|
16 |
class MyHomePage extends StatelessWidget { |
17 |
final String title; |
18 |
|
19 |
MyHomePage({Key? key, required this.title}) : super(key: key); |
20 |
|
21 |
@override |
22 |
Widget build(BuildContext context) { |
23 |
return Scaffold( |
24 |
appBar: AppBar(title: Text(title)), |
25 |
body: Center(child: Text('My Page!')), |
26 |
drawer: Drawer( |
27 |
// Add a ListView to the drawer. This ensures the user can scroll |
28 |
// through the options in the drawer if there isn't enough vertical |
29 |
// space to fit everything. |
30 |
child: ListView( |
31 |
// Important: Remove any padding from the ListView. |
32 |
padding: EdgeInsets.zero, |
33 |
children: <Widget>[ |
34 |
DrawerHeader( |
35 |
decoration: BoxDecoration( |
36 |
color: Colors.blue, |
37 |
), |
38 |
child: Text('Drawer Header'), |
39 |
), |
40 |
ListTile( |
41 |
title: Text('Item 1'), |
42 |
onTap: () { |
43 |
// Update the state of the app |
44 |
// ... |
45 |
// Then close the drawer |
46 |
Navigator.pop(context); |
47 |
}, |
48 |
), |
49 |
ListTile( |
50 |
title: Text('Item 2'), |
51 |
onTap: () { |
52 |
// Update the state of the app |
53 |
// ... |
54 |
// Then close the drawer |
55 |
Navigator.pop(context); |
56 |
}, |
57 |
), |
58 |
], |
59 |
), |
60 |
), |
61 |
); |
62 |
} |
63 |
} |
The above code will achieve this look:
.png)
.png)
.png)
Conclusion
Flutter has simplified the process of app development with its widgets and its easy-to-use interface. There are endless possibilities when it comes to adding and modifying elements for your Flutter app. This guide has covered some key elements that are an integral part of every app's design.
So experiment a lot because only testing and practicing will turn you into a great app developer.