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.
Open your terminal or command prompt.
Navigate to the directory where you want to create your project.
Run the following command:
flutter create my_first_appReplace
my_first_appwith your desired project name. It’s recommended to use lowercase with underscores for project names.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.dartis 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 toandroid/, 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 likepackage.jsonin Node.js orbuild.gradlein 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),
),
);
}
}
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.void main() { ... }: This is the entry point of any Dart program. When your app starts, themain()function is the first code that executes.runApp(const MyApp());: TherunApp()function takes aWidgetas an argument and makes it the root of your widget tree.MyAppis our top-level widget. Theconstkeyword is used for compile-time constants, which Flutter optimizes for performance, especially for widgets that don’t change.class MyApp extends StatelessWidget { ... }:MyAppis aStatelessWidget. 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.@override Widget build(BuildContext context) { ... }: Every widget must implement abuildmethod. This method describes the part of the user interface represented by the widget. TheBuildContextargument provides information about the widget’s location in the widget tree.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.home: const MyHomePage(...): Thehomeproperty ofMaterialAppspecifies the default route or the initial screen of your app. Here, it’s set toMyHomePage.class MyHomePage extends StatefulWidget { ... }:MyHomePageis aStatefulWidget. 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.class _MyHomePageState extends State<MyHomePage> { ... }: AStatefulWidgethas a correspondingStateclass. TheStateobject holds the mutable state of the widget and is where thebuildmethod for the stateful widget is implemented. The underscore_beforeMyHomePageStatemakes it private to themain.dartfile.int _counter = 0;: This is the mutable state variable for our counter.void _incrementCounter() { setState(() { _counter++; }); }: This method updates the_countervariable. The key here issetState(). Any changes to the state that should trigger a UI rebuild must be wrapped insidesetState(). This tells Flutter that the internal state has changed and it needs to redraw the widget.@override Widget build(BuildContext context) { ... }: Thebuildmethod of theStateclass defines the UI for theMyHomePagewidget.Scaffold(...): This widget provides a basic visual structure for Material Design apps. It includes common UI elements like anAppBar,body,FloatingActionButton,Drawer,SnackBar, etc.AppBar(...): A horizontal bar typically at the top of theScaffold, used for titles, actions, and navigation.body: Center(child: Column(...)): The main content area of theScaffold. Here, we useCenterto center its child, which is aColumn.Columnarranges its children vertically.floatingActionButton: FloatingActionButton(...): A circular button that floats above the UI, typically used for primary actions.onPresseddefines the action when the button is tapped.
Running Your App
Once you have created your project, running it is simple:
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.
- You can list available devices using
Run the application from your project directory:
flutter runFlutter 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 (
rin 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 (
Rin terminal or circular arrow icon in IDE): Reloads the entire application, discarding the current state. Use this when you make changes tomain()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:
- Reintroduce a
FloatingActionButton. - Make the greeting text dynamic: Instead of a static “Hello, Flutter Developer!”, make it change to “Welcome Back!” when the
FloatingActionButtonis pressed. - 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 createcommand. - Understand the basic file structure of a Flutter application.
- Dissect the
main.dartfile, identifying key components likemain(),runApp(),StatelessWidget,StatefulWidget,MaterialApp, andScaffold. - 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.