Lesson 1. Your first Flutter app: Hello World
Egor Dolgov · 09.11.2026Let's set up the lesson for you: your system, how you create the project, and where you run it.
Let's write a first app from scratch: show “Hello World!” on a screen with a header, move it into a widget of our own and open a website at the press of a button. On the way we will see where every Flutter program begins.
Before the lesson you need Flutter installed and an editor set up — that is Lesson 0. Installing Flutter and setting up your editor . You can run the example anywhere: in Chrome, as a desktop app, in an emulator or on a phone.
Contents:
- Create and run a project
- The main() function
- The runApp() function and the first screen
- A widget of your own
- Hot reload
- Your first package: opening a link
- Summary
1. Create and run a project
The
flutter create
command makes a new project. The project name is in Latin letters, lowercase, with words separated by underscores. The
--empty
flag creates a minimal project — it is the easiest place to start:
flutter create --empty hello_flutter
At the end the command tells you what to do next:
In order to run your empty application, type:
$ cd hello_flutter
$ flutter run
Your empty application code is in hello_flutter/lib/main.dart.
Run those two commands: go into the project folder and start it. If “Hello World!” appears on the screen, Flutter works and you can start writing code.
In VS Code the same project is created with the Flutter: Create New Project command from the command palette and the Empty Application template; in Android Studio, with the New Flutter Project wizard and the Empty Project type.
2. The main() function
Open
lib/main.dart
— Flutter runs this file by default. Find the line
void main()
in it: execution of every Dart program — and every Flutter app — starts with the
main()
function.
To understand every line, we will write the program ourselves. Delete everything in the file and start with an empty function:
void main() {
}
void
means the function returns nothing. An interface appears only when
main()
hands Flutter a widget to draw — that is what
runApp()
does.
3. The runApp() function and the first screen
To show an interface,
main()
calls
runApp()
and passes it a widget. A widget describes a piece of the interface: a text, a button, a gap or a whole screen. A Flutter interface is built from widgets nested inside each other. Replace the code in
lib/main.dart
:
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('flutter.su')),
body: const Center(child: Text('Hello World!')),
),
),
);
}
-
runApp()takes a widget and stretches it over the whole app window. -
MaterialAppis the root of the app: it sets up the theme and navigation.Scaffoldis the frame of a screen, with room for a header (appBar) and content (body). More about them in separate lessons. -
Centerputs its child (child) in the middle,Textshows a line of text. -
import 'package:flutter/material.dart'brings in Flutter's widgets, Material Design widgets included.
Run the app with
flutter run
or the run button in your editor: you will see a “flutter.su” header and “Hello World!” in the middle of the screen.
4. A widget of your own
When a screen grows, its parts are moved into widgets of their own. The simplest kind is a class that extends
StatelessWidget
: it has no mutable state and describes the interface in its
build()
method. Replace the code:
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(
home: Scaffold(
appBar: AppBar(title: const Text('flutter.su')),
body: const MyBody(),
),
);
}
}
class MyBody extends StatelessWidget {
const MyBody({super.key});
@override
Widget build(BuildContext context) {
return const Center(child: Text('Hello World!'));
}
}
-
MyAppis the whole app,MyBodyis the content of the screen. -
const MyApp({super.key})is the constructor;keyis passed on to the parent class. -
build()returns the widget tree Flutter will draw.
Nothing changed on the screen — only the way the code is organised.
5. Hot reload
Change the text in
MyBody
and save the file — without restarting the app. This is
hot reload
: Flutter puts the new code into the running app and keeps its state. If the app was started from a terminal, the
r
key triggers a reload —
flutter run
prints the hint itself:
Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h List all available interactive commands.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).
Hot reload will not pick up changes to the
main()
function itself — those need a
hot restart
(the
R
key): the app starts over and loses its state.
6. Your first package: opening a link
Packages from pub.dev extend what Flutter can do. Let's add a button that opens a website — the url_launcher package does that. Add it with a command run in the project folder:
flutter pub add url_launcher
The command does two things: it adds the package to the
pubspec.yaml
file in the project root and downloads it (the same as
flutter pub get
). Open
pubspec.yaml
— a
url_launcher
line has appeared under
dependencies
:
dependencies:
flutter:
sdk: flutter
url_launcher: ^6.3.3
^6.3.3
means “any compatible version”: 6.3.3 or later, but below 7.0.0.
pubspec.yaml
is an ordinary text file and you can edit it yourself: add the package under
dependencies
and run
flutter pub get
. VS Code has a
Get Packages
button above an open
pubspec.yaml
for that, Android Studio has
Pub get
.
url_launcher has code for each platform (Android, iOS, web…). Hot reload does not pick up such code: after adding the package, stop the app and start it again, otherwise you may get a
MissingPluginException
error.
Now the complete
lib/main.dart
:
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('flutter.su')),
body: const MyBody(),
),
);
}
}
class MyBody extends StatelessWidget {
const MyBody({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Hello World!'),
const SizedBox(height: 16),
FilledButton(
onPressed: _openSite,
child: const Text('Open flutter.su'),
),
],
),
);
}
}
Future<void> _openSite() async {
final url = Uri.parse('https://flutter.su');
if (!await launchUrl(url)) {
throw Exception('Could not open $url');
}
}
-
Columnplaces widgets one under another;mainAxisSize: MainAxisSize.minshrinks the column to its content so thatCentercan centre it. -
SizedBox(height: 16)is empty space between the text and the button. -
FilledButtonis a filled Material 3 button;onPressedis called when it is pressed. -
launchUrl()opens the address. If that fails, the function returnsfalseor throws aPlatformException, depending on the reason.
Earlier versions of this lesson usedFlatButton,canLaunch()andlaunch(). There is noFlatButtonclass in modern Flutter — it was replaced byTextButton( migration guide ). In url_launcher,canLaunch()andlaunch()have been deprecated since version 6.1.0; usecanLaunchUrl()andlaunchUrl(), which take aUri, instead.
7. Summary
-
flutter create --emptycreates a project,flutter runstarts it. -
A program starts with
main(), andrunApp()shows the interface. -
An interface is built from nested widgets; the root of the app is
MaterialApp, the frame of a screen isScaffold. -
Your own widgets extend
StatelessWidgetand describe the interface inbuild(). -
Hot reload (
r) changes code without losing state, hot restart (R) starts the app over. -
Packages are added with
flutter pub add— it writes them intopubspec.yaml; you can also edit that file by hand and then runflutter pub get.
Next: Lesson 2. StatelessWidget and StatefulWidget (in Russian for now).
Send questions and suggestions about this lesson to contact@flutter.su.