Get Started with Flutter | Building your First App

Subscribe to my newsletter and never miss my upcoming articles

Listen to this article

March is coming and with that, the next big Flutter event is also close. Scheduled on March 3rd of this year some big rumors have already surfaced up, like Flutter for web might get a stable release and many more. It's no better time to learn Flutter!

What would be better than to start than building our first app? So let's get started right away!

Development Environment Setup

For this tutorial series, I am using Visual Studio Code as my Editor. You can use any editor of your choice. Make sure Flutter SDK is installed on your machine. For detailed installation instructions refer to my previous post. In addition, you must have Android Studio installed to run the app on Emulator or any Android device. If you are using macOS you can also configure X-Code to run your Flutter apps on an iOS simulator or actual iOS devices.

Our Demo App

Here, we will be building a demo single page application to get quickly familiar with Flutter and some of its widgets. Our Demo Application will look like as below,

flutter.gif

This is a very simple single page/screen app that we will build to display some text on the App Bar and a button. On the button click, we will change the background color.

Creating a new App

To create a new app navigate to your desired location on your terminal and use the command below,

flutter create your_project_name

Alternatively, you can create a new Flutter project from your Android Studio or VS Code command palette.

The Project Directory Structure

Let's have a quick look at our project directory structure before starting our first app.

>android
>build
>ios
>lib 
   -> main.dart
>test
>.gitignore
>pubspec.lock
>pubspec.yaml
>README.md

The 'android' Folder

In this folder, all the Project files for the android app reside. You can make changes, add the required permissions and native android code here.

The 'build' Folder

This folder holds all the built and generated outputs like app bundles, apk's and other relevant files and folders.

The 'ios' Folder

In this folder, all the Project files for the iOS app reside. Similar to the android folder you can add required permissions and add native iOS code here.

The 'lib' folder

This is the folder where all the magic happens. In your lib folder, you can already see that you have a main.dart file. All your dart code is written in this folder which compiles to native platform code (native android and iOS) during the build process.

The 'test' folder

In this folder, you can write unit tests for your flutter app.

The '.gitignore' file

I assume you are pretty familiar with this file if you are using GIT as your version control system for your projects. This holds all the files and folder names that need not be added to the GIT.

The 'pubspec.lock' and 'pubspec.yaml' files

These files hold all the required packages names, their versions, link to your assets, dependencies, your app name, app version, dependencies of your app, etc.

The 'README' markdown file

This is a markdown file that contains all the basic information and description about the app.

With this file structure out of the way let's get started with our first demo application.

Running apps on emulators/real devices

To run your app create or open an existing android emulator or iOS simulator. Open your lib folder and main.dart file and press F5 to run your app. If you have flutter and dart extension installed in your VS code editor then hover over your void main() function in your main.dart file and you will see a Run|Debug option.

On running the app you will see a basic flutter app that is created automatically. main.dart file is the entry point of your flutter application. The execution of any Flutter application starts from the void main() {...} function in this file. Now let's clear everything and start our app from scratch.

Building the app

With your main.dart file cleared. Let's start building the app right away.

Importing Packages

Before anything else, we need to import the required packages. In this app we require the material package offered by flutter. All the widgets and functionality is built on top of this package. We can import this package using the import statement as below,

import 'package:flutter/material.dart';

If your app requires any other packages the import statement will be similar.

Creating a Widget

Everything in a Flutter app is a Widget! So, whatever we need to display has to be a widget. There are two types of widgets that are provided by the material.dart package on top of which we build our own custom widgets. Those two are Stateless Widgets and Stateful Widgets.

We will discuss state, stateful widgets, and stateless widgets in much detail and depth in my next upcoming blogs in this series.

The IDE or Editor which you are using will help you build your own custom widgets if you have Flutter and Dart plugin installed. Simply type stateless or stateful and your IDE will give suggestions accordingly.

Here, we will create a stateless widget with the name MyApp. The widget can have any name of your choice.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

As you can already see that we are extending the StatelessWidget offered by the material package to create our own custom stateless widget. @override is a decorator which is also provided by the material package.

Widget is an abstract class and we are overriding the build method. This method requires a context which is of type BuildContext which is provided by Flutter. Inside this method, we return the Widget that we need to build/display on the screen.

So here we will return a MaterialApp which is also a built-in widget provided by Flutter's material.dart package. This widget accepts multiple arguments in its constructor. You can find more details by hovering over the Material App or Command + Click/ Ctrl+Click on the MaterialApp widget. Official documentation can be found here.

We need to define 3 parameters to our MaterialApp, i.e, title, theme, and home. title simply accepts a string. I will provide it with First App Demo as value. theme expects a ThemeData datatype value which is a built-in type in the Flutter material package. So, initialize it with,

ThemeData(primarySwatch: Colors.amber,),

ThemeData accepts multiple values to configure app Default theme, primarySwatch is a required parameter that accepts a value of type MaterialColor. This is also a built-in type and provides a color palette to Flutter to build our app theme. I have provided Colors.amber which is a nice shade of orange and yellow.

Now, we need to configure home which accepts a Widget type. The Widget that you provide here will be rendered onto the screen. For now, we will initialize it with an empty container like this, home: Container(),. We will replace this soon with our custom widget. Our widget looks like this now,

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'First App Demo',
      theme: ThemeData(
        primarySwatch: Colors.amber,
      ),
      home: Container(),
    );
  }
}

The main(){} function

To run our app we need the main function. The main function can be written using general syntax (){...} or by defining it as an arrow function ()=>. Both the syntaxes are as below,

void main() {
  runApp(MyApp());
}

or,

void main() =>runApp(MyApp());

Both, the syntaxes will work fine. The shorthand arrow function is more elegant though if we need to return a single value which is the case here. Now, run your app with F5 or Run|Debug and you will see a blank screen.

Creating a StatefulWidget

Instead of displaying an empty container that will render nothing but a black screen in the end let's build something more classy!

To create a StatefulWidget use your IDE or Editor auto-complete feature by simply typing stateful and press tab. Here, I am creating a Stateful widget with the name DummyWidget as below,

class DummyWidget extends StatefulWidget {
  @override
  _DummyWidgetState createState() => _DummyWidgetState();
}

class _DummyWidgetState extends State<DummyWidget> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

You can clearly see that, StatefulWidget is a bit different than StatelessWidget. For now, all I will say is that Stateful Widgets can react to changes in data internally and rebuild themselves to reflect the changes whereas Stateless widgets don't rebuild themselves if data changes within them.

I will discuss this in much detail in my upcoming posts.

We need some type of internal data so that we can understand Stateful Widgets. Here, I will take a boolean value named _isGreen which I will initialize false by default. I am using _ in front of the variable that makes it a private variable in Dart and accessible only inside this class.

Now, instead of returning an empty container again from the Widget build(BuildContext context) {...} method, we will return something interesting!

We will return a Scaffold which is also a widget provided by the material package. Note that every time you want to render a new/different screen you need to provide a Scaffold. Scaffold accepts several parameters in its constructor.

Here, we will provide it with appBar and body to see some content on the screen. The DummyWidget will look like this,

class _DummyWidgetState extends State<DummyWidget> {
  bool _isGreen = false;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: _isGreen ? Colors.green : Colors.red,
      appBar: AppBar(
        title: Text('Your First App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            FlatButton(
              onPressed: () {
                setState(() {
                  _isGreen = !_isGreen;
                });
              },
              child: Text(_isGreen ? 'TURN RED' : 'TURN GREEN'),
            ),
          ],
        ),
      ),
    );
  }
}

Let's visualize what is happening in our Stateful DummyWidget. We have a private variable of type bool named _isGreen which is false by default. Instead of returning a container, we are returning Scaffold widget and we have provided it with 2 parameters namely appBar and body.

The appBar is accepts a value of type PreferredSizeWidget which in this case is AppBar(). AppBar() also accepts many parameters for configuration but we have used only one here to configure the title that is displayed on the AppBar. The title accepts a type Widget so we are using inbuilt Text() widget.

The body also accepts a type Widget so here we are using built-in Column() widget. Column extends from top to bottom of the screen by default and wraps itself around its child widgets. Column can hold multiple widgets as its children.

Here, we have a single child of Column() widget that is a FlatButton() which is also a widget provided by the material package. Flatbutton() has an onPressed and a child parameter. onPressed accepts a function that can be anonymous or a named one. The content that you write inside this function defines what will happen when this button is pressed.

Inside this anonymous function, we are using setState(() {...}); which triggers the Widget rebuild. If we change some internal value as in our case we change the boolean value _isGreen inside the set state. On every button click this value changes and triggers the build process of this stateful widget.

In the end we provide Text('...') widget as a child to the FlatButton(...). This specifies what is displayed inside the button.

Lastly, in our stateless Widget MyApp instead of returning an empty container we return our custom stateful widget DummyWidget. With this in your running application, you can already see your custom widget on your screen. Now, our finished code in main.dart file looks as below,

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'First App Demo',
      theme: ThemeData(
        primarySwatch: Colors.amber,
      ),
      home: DummyWidget(),
    );
  }
}

class DummyWidget extends StatefulWidget {
  @override
  _DummyWidgetState createState() => _DummyWidgetState();
}

class _DummyWidgetState extends State<DummyWidget> {
  bool _isGreen = false;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: _isGreen ? Colors.green : Colors.red,
      appBar: AppBar(
        title: Text('Your First App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            FlatButton(
              onPressed: () {
                setState(() {
                  _isGreen = !_isGreen;
                });
              },
              child: Text(_isGreen ? 'TURN RED' : 'TURN GREEN'),
            ),
          ],
        ),
      ),
    );
  }
}

Conclusion

Yupp! I know that feeling. You have built your first app using Flutter and so little code. It's time to play with that button and change your app colors. Go ahead! and ram that button๐Ÿ˜….

I hope you liked this post. If you have reached this far then any reaction ๐Ÿ‘๐Ÿบ๐ŸŽ‰๐Ÿ‘ on the post would be awesome. Stay tuned for the next upcoming post in this series where we will discuss some Dart fundamentals in detail. Thanks for reading it till the end. Happy Coding๐Ÿ‘จ๐Ÿฝโ€๐Ÿ’ป Fellas!

Anand Baraik's photo

A good read! :)

Sourav Kumar's photo

such a great information, this is very informative and interesting.