Introduction

Welcome to the exciting world of Flutter app development! In this chapter, we’ll guide you through the fundamental steps of creating, understanding, and running your very first Flutter application. Having successfully set up your development environment in the previous chapter, you’re now ready to put it to use. We’ll explore the basic project structure, dissect the core main.dart file, and introduce you to the concept of widgets, which are the building blocks of every Flutter UI. By the end of this chapter, you’ll have a running Flutter app and a foundational understanding of how it works.

Main Explanation

Creating Your First Flutter Project

The first step to building any Flutter application is to create a new project. Flutter provides a command-line interface (CLI) tool that makes this process straightforward.

  1. Open your terminal or command prompt.

  2. Navigate to the directory where you want to create your project.

  3. Run the following command:

    flutter create my_first_app
    

    Replace my_first_app with your desired project name. It’s recommended to use lowercase with underscores for project names.

  4. Navigate into your new project directory:

    cd my_first_app
    

Flutter will generate a complete project template, including platform-specific code for Android and iOS, and a basic “counter” application in the lib folder.

Exploring the Project Structure

A newly created Flutter project comes with a well-organized directory structure:

  • lib/: This is where your primary Dart code resides. main.dart is the entry point of your application.
  • android/: Contains the Android-specific project files. You’ll interact with this for platform-specific configurations, permissions, etc.
  • ios/: Similar to android/, this holds the iOS-specific project files (Xcode project).
  • web/: Contains files for web deployment.
  • windows/, macos/, linux/: Platform-specific files for desktop applications.
  • test/: For writing automated tests for your application.
  • pubspec.yaml: This is the project’s configuration file. It declares dependencies (packages your app uses), assets (images, fonts), and other metadata. Think of it like package.json in Node.js or build.gradle in Android.
  • .gitignore: Specifies files and directories that Git should ignore.

Dissecting lib/main.dart

The main.dart file is the heart of your Flutter application. Let’s break down its key components, using the default counter app as an example:

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

void main() { // 2
  runApp(const MyApp()); // 3
}

class MyApp extends StatelessWidget { // 4
  const MyApp({super.key});

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

class MyHomePage extends StatefulWidget { // 8
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> { // 9
  int _counter = 0; // 10

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

  @override
  Widget build(BuildContext context) { // 12
    return Scaffold( // 13
      appBar: AppBar( // 14
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center( // 15
        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( // 16
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}
  1. import 'package:flutter/material.dart';: Imports the Material Design library, which provides a rich set of widgets that implement Google’s Material Design guidelines. For most UI development, this is your go-to import.
  2. void main() { ... }: This is the entry point of any Dart program. When your app starts, the main() function is the first code that executes.
  3. runApp(const MyApp());: The runApp() function takes a Widget as an argument and makes it the root of your widget tree. MyApp is our top-level widget. The const keyword is used for compile-time constants, which Flutter optimizes for performance, especially for widgets that don’t change.
  4. class MyApp extends StatelessWidget { ... }: MyApp is a StatelessWidget. Stateless widgets are immutable; their properties cannot change once they are created. They are useful for parts of the UI that don’t need to manage internal state, like a static logo or a text label.
  5. @override Widget build(BuildContext context) { ... }: Every widget must implement a build method. This method describes the part of the user interface represented by the widget. The BuildContext argument provides information about the widget’s location in the widget tree.
  6. MaterialApp(...): This is a crucial widget for any Material Design app. It sets up the basic structure for a Material Design application, including navigation, theming, and internationalization.
  7. home: const MyHomePage(...): The home property of MaterialApp specifies the default route or the initial screen of your app. Here, it’s set to MyHomePage.
  8. class MyHomePage extends StatefulWidget { ... }: MyHomePage is a StatefulWidget. Stateful widgets are dynamic; they can change their internal state over time. They are used when parts of the UI need to be updated based on user interaction or external data, like a counter, a checkbox, or a text input field.
  9. class _MyHomePageState extends State<MyHomePage> { ... }: A StatefulWidget has a corresponding State class. The State object holds the mutable state of the widget and is where the build method for the stateful widget is implemented. The underscore _ before MyHomePageState makes it private to the main.dart file.
  10. int _counter = 0;: This is the mutable state variable for our counter.
  11. void _incrementCounter() { setState(() { _counter++; }); }: This method updates the _counter variable. The key here is setState(). Any changes to the state that should trigger a UI rebuild must be wrapped inside setState(). This tells Flutter that the internal state has changed and it needs to redraw the widget.
  12. @override Widget build(BuildContext context) { ... }: The build method of the State class defines the UI for the MyHomePage widget.
  13. Scaffold(...): This widget provides a basic visual structure for Material Design apps. It includes common UI elements like an AppBar, body, FloatingActionButton, Drawer, SnackBar, etc.
  14. AppBar(...): A horizontal bar typically at the top of the Scaffold, used for titles, actions, and navigation.
  15. body: Center(child: Column(...)): The main content area of the Scaffold. Here, we use Center to center its child, which is a Column. Column arranges its children vertically.
  16. floatingActionButton: FloatingActionButton(...): A circular button that floats above the UI, typically used for primary actions. onPressed defines the action when the button is tapped.

Running Your App

Once you have created your project, running it is simple:

  1. Ensure you have an active device or emulator.

    • You can list available devices using flutter devices.
    • Start an Android emulator via Android Studio’s AVD Manager or an iOS simulator via Xcode.
  2. Run the application from your project directory:

    flutter run
    

    Flutter will compile your Dart code, package it for the target platform, and deploy it to your selected device or emulator. The first run might take a few minutes as it compiles everything. Subsequent runs will be much faster.

    You can also run your app from your IDE (VS Code or Android Studio) by clicking the “Run” button.

Hot Reload and Hot Restart

Flutter offers powerful development features to speed up your workflow:

  • Hot Reload (r in terminal or lightning bolt icon in IDE): Instantly reloads the code changes into the running app without losing the current state. This is incredibly fast and saves a lot of development time. It works by injecting updated source code into the running Dart Virtual Machine.
  • Hot Restart (R in terminal or circular arrow icon in IDE): Reloads the entire application, discarding the current state. Use this when you make changes to main() or global variables, or when hot reload doesn’t seem to apply your changes correctly.

Understanding the Widget Tree

In Flutter, everything is a widget. Widgets are immutable descriptions of a part of a user interface. They are like blueprints. When you build your UI, you compose a hierarchy of widgets, known as the widget tree.

  • Composition over Inheritance: Instead of inheriting from existing UI components, Flutter encourages you to compose smaller, simpler widgets to build complex UIs.
  • Declarative UI: You describe what your UI should look like for a given state, and Flutter takes care of updating it efficiently when the state changes.

Examples

Let’s modify the default counter app to display a custom greeting instead of just a counter, and change the app bar title.

First, the default main.dart structure, which we’ve already seen:

// lib/main.dart (Default counter app)
import 'package:flutter/material.dart';

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

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'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0; // The state we want to change

  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', // Displaying the state
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

Now, let’s modify it to show a custom greeting and change the app bar title. We’ll simplify MyHomePage to be StatelessWidget for this example, as we won’t need to manage a counter state.

// lib/main.dart (Modified to show a custom greeting)
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My Greeting App', // Changed app title
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal), // Changed theme color
        useMaterial3: true,
      ),
      home: const GreetingPage(title: 'Welcome to Flutter!'), // Using a new widget and title
    );
  }
}

// Our new StatelessWidget for the greeting page
class GreetingPage extends StatelessWidget {
  const GreetingPage({super.key, required this.title});

  final String title;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.primary, // Using primary color for app bar
        title: Text(title, style: const TextStyle(color: Colors.white)), // Custom title and text color
      ),
      body: const Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Hello, Flutter Developer!', // Our custom greeting
              style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 20), // Adds some vertical space
            Text(
              'This is your first custom app.',
              style: TextStyle(fontSize: 18),
            ),
          ],
        ),
      ),
      // Removed FloatingActionButton as it's not needed for this static greeting
    );
  }
}

After saving these changes, perform a Hot Reload (press r in the terminal where flutter run is active, or use the IDE’s hot reload button). You should instantly see your app update with the new title and greeting, demonstrating the power of Flutter’s development workflow.

Mini Challenge

Your challenge is to take the modified “Greeting App” from the examples and enhance it:

  1. Reintroduce a FloatingActionButton.
  2. Make the greeting text dynamic: Instead of a static “Hello, Flutter Developer!”, make it change to “Welcome Back!” when the FloatingActionButton is pressed.
  3. Ensure the app bar title remains “Welcome to Flutter!”.

Hint: You’ll likely need to convert GreetingPage back into a StatefulWidget to manage the changing greeting text.

Summary

In this chapter, you’ve taken your first significant steps into Flutter development. You learned how to:

  • Create a new Flutter project using the flutter create command.
  • Understand the basic file structure of a Flutter application.
  • Dissect the main.dart file, identifying key components like main(), runApp(), StatelessWidget, StatefulWidget, MaterialApp, and Scaffold.
  • Run your Flutter application on an emulator or device using flutter run.
  • Leverage Hot Reload and Hot Restart for efficient development.
  • Grasp the fundamental concept that “everything is a widget” and how widgets form a tree structure.

You’ve successfully created and modified your first Flutter app. This foundational knowledge is crucial as you continue your journey to build more complex and production-ready applications. In the next chapter, we’ll dive deeper into more widgets and layout concepts.