Learn Flutter by creating your first Flutter app!
Tags: flutter, tutorial, mobile, learning
Published: 2024-05-05 16:49:55

In this article, I'm going to introduce you to your first Flutter app. We will explore the parts of a Flutter project, their roles, and we'll cover some fundamental concepts of state, including the differences between StatelessWidgets and StatefulWidgets.

If you haven't installed Flutter yet, here are some step-by-step videos that will guide you through the process of installing Flutter on [Mac](https://youtu.be/4pKUdxA49UY), [Windows](https://youtu.be/l2TvAVkCgIE), and [Linux](https://youtu.be/RFSF4t5FQhg).

> 📽 Video version available on [YouTube](https://youtu.be/tFbN5_a7_ho)

## Creating a basic Flutter app

Let's start by creating a basic application. You can do it from the menus of your favorite code editor, although I always prefer to do it from the terminal:

`flutter create flutter_test_app`

This command will create a basic counting app that, when executed, allows us to increment a numerical value by clicking on a button:

![Basic counter app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kue6tqpjrvw3c0n342sj.png)

The goal of this sample code is to give you a first introduction to Flutter. We are going to go little by little to understand this template that is already given to us.

### The pubspec.yaml Configuration File

The first thing we are going to look at is the pubspec.yaml file:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1712400771180/4d3f2380-70d9-492a-8c07-ab6d7ea12f1e.png align="center")

This file contains essential application metadata, enumerates all dependencies, and includes various configuration settings. If you open it, you will find comments for each section that explain their purpose. However, for clarity, we'll remove these comments to keep the file straightforward and briefly overview each segment:

```yaml
name: flutter_test_app
description: "You can add a description for your project here."
publish_to: 'none'
version: 1.0.0+1

environment:
   sdk: '>=3.2.6 <4.0.0'
```

Let's clarify the meaning of each parameter:

* **name**: This represents the project's name. It serves as an "internal" identifier and is not the name presented to your users.
    
* **description**: This field allows you to provide a brief outline of your project's purpose.
    
* **publish\_to**: This setting is primarily relevant for package development. Since this article focuses on basic concepts, we'll leave it unchanged.
    
* **version**: Here, you can specify your project's version using semantic versioning followed by an additional integer. This practice enables version control directly from this file, which then applies across platform-specific projects. The trailing integer typically corresponds to the *versionCode* in Android projects.
    
* **environment**: This specifies the compatible Dart SDK version range for your application. If in the future you want to use new versions of the Dart SDK you might be interested in modifying it, but for now, we leave it as is.
    

```yaml
dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^2.0.0
```

The next part focuses on the application's dependencies, which are categorized into two types: those incorporated into the app's final package (`dependencies`) and those used during development but not included in the final app package (`dev_dependencies`). To grasp this distinction, let's examine the [flutter\_lints](https://pub.dev/packages/flutter_lints) package as an example. This package aids in static code analysis, which is performed locally on your machine. There's no need for it to be part of the final app package distributed to users.

> After adding `flutter_lints` to your `dev_dependencies` and running `flutter pub get`, you can analyze your code according to the lint rules specified by `flutter_lints` using the command `flutter analyze`. This command checks your code for issues based on the linting rules defined by the package.

For finding new packages, Flutter developers often visit [https://pub.dev](https://pub.dev/). Say you want to do a network request to a remote server using the [http](https://pub.dev/packages/http) package, you can simply add it to your dependencies and then execute `flutter pub get` to fetch the package and make it ready to use.

Alternatively, you can use the command `flutter pub add http` to not only download but also automatically add the `http` package to your `dependencies`. To add a package to `dev_dependencies`, you would use `flutter pub add --dev package_name`.

Experimenting with these methods can help you determine the most comfortable way to manage packages in your Flutter application.

```yaml
flutter:
  uses-material-design: true
```

Towards the end of the file, there's a section labeled "flutter," featuring the `uses-material-design: true` setting. This particular setting informs Flutter that our application will utilize the [Material Design style](https://m3.material.io/), providing a suite of visual, interaction, and motion design guidelines developed by Google.

As you advance more into Flutter development, you'll encounter a range of additional configurations that can be applied within this file to further personalize themes and other aspects of your app.

Additionally, it's worth mentioning the `pubspec.lock` file, a crucial component of Flutter projects. This file is automatically generated by Flutter when you run commands like `flutter pub get` or `flutter pub add`. Its primary purpose is to record the exact versions of each dependency used in your project at the time these commands are executed. This ensures that your project remains consistent and stable, even if dependencies are updated in the future. By tracking these versions, the `pubspec.lock` file helps to prevent the "it works on my machine" problem by ensuring that every developer working on the project uses the same versions of dependencies, thus minimizing conflicts and compatibility issues.

### Platform-Specific Projects in Flutter

Within a Flutter project, beyond the `pubspec.yaml` and `pubspec.lock` files, you'll notice several directories named after platforms. These directories are `android`, `ios`, `macos`, `linux`, and `windows`. These aren't just folders; they're complete native projects for their respective platforms.

Flutter's strength lies in its ability to provide a multi-platform development framework, hiding the complexities of platform-specific implementation details. Yet, the existence of these platform-specific projects is crucial for Flutter to operate seamlessly across different environments.

There are instances when you'll need to dip into native development within these directories. This is often the case when certain functionality can only be achieved with platform-specific code. It's in these scenarios that modifications to the native parts of a project become necessary.

It's important to note that you're not required to maintain all these directories if your app doesn't target all supported platforms. For instance, if your focus is solely on Android and iOS, you can safely remove the `macos`, `linux`, and `windows` directories.

Conversely, should you decide to expand your app's availability to additional platforms not initially included in your project, Flutter simplifies this expansion. Using the `flutter create` command with the `--platforms` option, you can add the necessary platform projects. For example, if you start with a project only supporting Android and iOS and later decide to include support for macOS, Linux, and Windows, you can execute `flutter create --platforms=macos,linux,windows .` This command creates the required directories for the new platforms to be supported.

# Your app's source code: the lib directory

We have already seen the main files and directories of a Flutter project, although it is true that I have not listed them all, for now I have described the most relevant ones that you should know from the beginning. Now let's jump to the lib directory, the place where all the Dart code that makes up your application resides.

When you create a new project, Flutter automatically generates the `main.dart` file within the `lib` folder. This file contains the source code responsible for the counter app that you see if you run the project. If you enter, you will see some comments that explain each section. As before, we are going to eliminate these comments and we are going to explain each part little by little:

```dart
import 'package:flutter/material.dart';

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

The first line introduces an import of `material.dart`. This import is essential because, by default, we use Material widgets to construct the user interface.

Following that, we encounter the `main()` method. Every Dart application, Flutter included, requires an entry point, which is provided by the `main()` function. Within this function, we call `runApp()`, enabling the application to launch. We pass it an instance of `MyApp`, which is the next widget we come across in the file:

```dart
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}
```

This snippet introduces the `MyApp` widget, serving as the foundational element of your application. It's at the top of the widget hierarchy, essentially acting as the root from which all other widgets will branch out. Flutter apps are structured as a vast tree of widgets, where each widget may be a parent or a child to others. Here, `MyApp` stands as the initial node in this interconnected structure.

`MyApp` is defined as a class that extends `StatelessWidget`. `StatelessWidgets` are characterized by their lack of internal state—they don't manage any data that changes over time. Consequently, a `StatelessWidget` does not rebuild itself in response to internal data changes. Further details on this will be provided as we progress.

Every `StatelessWidget` must implement the `Widget build(BuildContext context)` method. This method is where the app's user interface is constructed. In this example, we create a `MaterialApp` widget within this method. `MaterialApp` facilitates the development of an app following Material Design guidelines, including aspects like the app's title and theme.

The `home` attribute specifies the widget that will be displayed when the app starts. Here, it's set to `MyHomePage`, which is the widget that comes next in the file:

```dart
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}
```

`MyHomePage` is a widget similar to `MyApp`, but it inherits from `StatefulWidget` instead of `StatelessWidget`. This distinction introduces two related classes: `MyHomePage` itself, which sets up the widget, and `_MyHomePageState`, a class that manages the widget's state, extending `State`.

> The reason `_MyHomePageState` starts with the underscore symbol (\_) is to indicate that this class must be private within this file.

In the state class, we're obliged to implement the `Widget build(BuildContext context)` method again. However, this time it's within the state class, where we define a widget tree that composes the counter interface:

* Initially, a `Scaffold` widget lays out the basic structure of our screen, considering elements like system navigation bars.
    
* An `AppBar` acts as the top navigation bar, where we specify a title and modify the background color using themes.
    
* The `Scaffold`'s body comprises a `Center` widget, which ensures its content is centered on the screen. Inside the `Center`, we place a `Column` for vertical arrangement of widgets. This column contains two `Text` widgets: one displays a static message, and the other shows the dynamic `_counter` value, styled with the current theme.
    
* The `floatingActionButton` property of the `Scaffold` employs a `FloatingActionButton` widget. Positioned at the bottom right, this button is tasked with increasing the counter each time it's pressed.
    

### Understanding State Management in Flutter

Now that we have seen roughly everything that is in the `main.dart` file, let's understand in a simple way how Flutter manages the state.

As I said before, the `_MyHomePageState` class is responsible for managing the state of the `MyHomePage` widget. In this case, we have an application with a numerical value that increments when a button is pressed. This is the state. Specifically, the variable that is defined at the beginning of the class:

```dart
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
```

When we invoke the `_incrementCounter()` method, it calls the `setState` method and within it the value of the `_counter` variable is incremented. By calling the `setState` method we are updating the state, telling Flutter that it has changed and causing the `build` method to run again, but this time with the updated state. Later, in the second Widget of type `Text`, the `_counter` variable is read to display the value on the screen.

This is a very simplified explanation of state management in Flutter, let's perform an experiment, add the following line just before build returns the `Scaffold`:

```dart
  @override
  Widget build(BuildContext context) {

    print('State refresh'); // <-- Add this new line

    return Scaffold(
```

Now run the application again and look at the output log. You will see the String "State refresh" printed when you run the application, but also when you modify the value of the counter.

As you can see, this is the simplest way to manage the state of your application in Flutter. In addition, you have also been able to observe the role of the build method and its importance when composing the interface based on state changes.

## Your first app in Flutter: key concepts

If this is your first contact with Flutter, it is possible that at this point you are a little saturated with so much information, don't worry, I am going to list the key points that we have seen throughout the article.

* A Flutter project contains a `pubspec.yaml` file. It defines several configuration parameters as well as the list of packages that are used by the project.
    
* A Flutter project can contain several subdirectories named after a platform such as `android` or `ios`, these are the native projects that Flutter uses to run on each platform.
    
* Inside the `lib` directory we find the source code of the project, this is where we will write our files in *Dart* code to create our application.
    
* At its core, a Flutter app is structured as an extensive hierarchy of widgets. Widgets can be of two types: `StatefulWidget`, which holds state (variables that can change over time), and `StatelessWidget`, which does not hold state.
    
* We can alter the state of a `StatefulWidget` by using the `setState` method.
    

These are broadly the main basic points that you should know if you are getting started in developing applications with Flutter.

I hope this article has been useful to you. Do not hesitate to follow this blog and my [YouTube channel](https://www.youtube.com/@DavidSerranoIO) if you want to continue learning about application development with Flutter, as well as stay up to date with news and other topics of interest about this magnificent framework.

Thanks for reading this far, happy coding!