Flutter is a framework for creating multiplatform interfaces from widgets.
If you’ve made it this far, you already know Dart. Now we’re going to use that language to talk to Flutter.
Flutter is not a language, it’s a UI Toolkit (a user interface toolkit). Think of it as a giant box of Lego pieces. Dart is the physics that allows those pieces to fit together, and Flutter is the pieces themselves (buttons, texts, menus).
In this post, we’re going to create our first project, understand what’s inside all those generated folders, and write our first graphical interface.
Creating the project
Open your terminal (or VS Code’s command palette) and navigate to where you save your projects. Run:
flutter create mi_primera_app
When it finishes, open that folder in VS Code. You’ll see a bunch of files. Don’t panic. Let’s see what’s important and what’s “noise”.
Anatomy of a Flutter project
Flutter’s folder structure is very opinionated. Here’s the roadmap:
The lib folder
This is where you’ll spend almost all your time.
- Contains all your Dart code (
.dart). - When creating the project, you’ll see a
main.dartfile. That’s the entry point of your application.
The pubspec.yaml file
This is the most important configuration file. Written in YAML (a very indentation-sensitive format), it defines:
- Metadata: App name, version, and description.
- Dependencies: Third-party libraries we’ll use (like Google Maps, or a date picker).
- Assets: Here we register local images and font files.
Watch out for spaces: In YAML, indentation is strict. Two spaces in the wrong place and the file will error. Always use 2 spaces per level.
The platform-specific folders
Flutter generates folders with the projects and configuration for each platform, such as android, ios, web, windows, macos, and linux, depending on the enabled platforms.
- These are generally not touched, unless you need to configure specific permissions (like using the camera or GPS) or change the App’s icon.
The build folder
It is automatically generated and contains the compiled executables. Never write anything here, as it gets deleted every time you compile.
Everything is a widget
Before writing code, burn this into your mind: In Flutter, EVERYTHING is a Widget.
- A button? It’s a Widget.
- A text? It’s a Widget.
- The space between two elements? It’s a Widget (
SizedBox). - Centering content? It’s a Widget (
Center). - The entire application? It’s a Widget.
The interface is built by creating a Widget Tree, where some widgets contain others.
Cleaning up main.dart
By default, Flutter comes with a “Counter” example app. It’s great, but it has too much noise to start with.
Let’s delete all the content of lib/main.dart and write it from scratch.
Import Material
The first thing is to bring in Google’s design toolkit (Material Design).
import 'package:flutter/material.dart';
The main function
Just like we saw in Dart, we need an entry point. But now, instead of doing a print, we’re going to “start” the interface with the runApp() function.
void main() {
runApp(MiPrimeraApp());
}
The root widget MaterialApp
Here we define our MiPrimeraApp class. Since our App won’t change (for now), it will be a StatelessWidget.
The most important widget we will return here is MaterialApp.
MaterialApp is the main “wrapper”. It provides us with:
- Navigation between pages.
- Themes (colors, fonts).
- Basic configuration so the App looks good on both Android and iOS.
class MiPrimeraApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hello Flutter',
home: Text('Hello World'), // This will look ugly, keep reading
);
}
}
If you run this, you’ll see a “Hello World” text in the top left corner, with a black background and an ugly red underline. Why? Because MaterialApp configures the App, but doesn’t “paint” the typical white canvas of a screen. For that, we need a Scaffold.
Scaffold: the screen’s scaffolding
The Scaffold is the widget that implements the basic visual structure of Material Design. It automatically gives us:
- An AppBar (top bar).
- A Body (the app’s body).
- A FloatingActionButton (floating button).
- A Drawer (side menu).
Let’s improve our code by using a Scaffold inside the home of MaterialApp.
import 'package:flutter/material.dart';
void main() => runApp(MiPrimeraApp());
class MiPrimeraApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// Remove the "Debug" label from the corner
debugShowCheckedModeBanner: false,
title: 'My App',
theme: ThemeData(primarySwatch: Colors.blue),
// Here starts the main screen
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
backgroundColor: Colors.blueAccent,
),
body: Center(
child: Text(
'Hello LuisLlamas.es',
style: TextStyle(fontSize: 24, color: Colors.black87),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
print('Button pressed');
},
child: Icon(Icons.add),
),
),
);
}
}
Widget nesting
Notice the tree structure:
MaterialApp(Global Configuration)Scaffold(Screen Structure)Center(Layout for centering)Text(Final Content)
At first, seeing so many parentheses ))); at the end is scary. But with VS Code and the Flutter extension, you’ll see color guides that help you know where each one closes.
Running the application
To see your creation:
- Open a simulator (Android/iOS) or connect your phone via USB.
- Press
F5in VS Code.
Congratulations! You just rendered your first native interface at 60fps.