DEV Community

Margarita
Margarita

Posted on

Flutter for Beginners: Where to Start?

Who this article is for: for those who are just starting their journey in Flutter framework development

Hello everyone! This is an introductory guide to Flutter. We will cover the basic concepts and break down the code that Flutter generates when creating a new project. I started mobile app development with Flutter — without knowing architecture and without experience in Kotlin or Swift. Only later, when I figured out the basics, did I move on to native development. I can say: if you already write in Kotlin or Swift, it will be very easy for you. And vice versa — Flutter will simplify learning native development.

What is Flutter?

Flutter is a cross-platform framework from Google. In simple terms: we write one code and can run the application on Android, iOS, Web, and other platforms. Development is done in the Dart language, which is also created by Google. It's important to understand: Flutter is not just a library, but a full-fledged UI engine that renders the interface itself.

First, let's discuss what I consider the most important part — architecture. Flutter uses its own rendering engine, unlike React Native, which renders native platform components. In Flutter, there is the ability to call native code through platform channels, but personally, I haven't encountered situations where this was necessary — the framework covers the vast majority of tasks. During development, you have access to two sets of widgets:

  • Cupertino — for iOS style
  • Material — for Android style

You may know that Android and iOS have different interface patterns and animations. The human brain gets used to consistency: a user who has been on Android for years won't immediately figure out iOS. Therefore, when developing, it's important to consider user comfort and adapt the UI for both platforms.

Widgets

When you open the Flutter documentation, you'll see the phrase: "In Flutter, everything is a widget." A class, text, button — absolutely everything is a widget. But behind this simple statement lies a complex architecture of three trees:

  1. Widget Tree — the widget tree. Lives briefly, serves to describe the UI configuration.

  2. Element Tree — the element tree. Lives longer, links widgets to their render objects. When a widget is recreated, the element persists and monitors its widget.

  3. Render Object Tree — the rendering tree. This is where the actual drawing on the screen happens.

Flutter uses a declarative approach: we describe what the UI should look like, and the rendering tree handles the drawing.

Analizing code

Let's look at the code that is offered to us when creating the project and then improve it

First, we connect the library with components

import 'package:flutter/material.dart';
Enter fullscreen mode Exit fullscreen mode

The main function is the entry point to the application. runApp launches it, accepting the root widget

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

Enter fullscreen mode Exit fullscreen mode

Creating the First Class
It inherits from StatelessWidget — meaning it has no state and doesn't redraw itself. Here we set general settings: theme, colors, and specify which widget to show first

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),
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

In the home parameter, we pass MyHomePage and give it a title — we'll use it later.
Next MyHomePage this is a StatefulWidget — it has state and can redraw. In the class itself, we only create this state

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

  final String title;

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

Enter fullscreen mode Exit fullscreen mode

The constructor requires title (required) and saves it to a variable. Widgets can accept any values and use them internally.

Where Everything Happens: State
In the _MyHomePageState class, we describe the UI and logic. State can be stored in different ways — here it's simple, so we use setState:
Breaking it down in order

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

Enter fullscreen mode Exit fullscreen mode
  • Scaffold — the "foundation" of the screen. We add AppBar (header), navigation, and body to it.

  • setState — triggers a redraw. But only the build method redraws, not the entire widget. The variable won't reset to zero.

  • Theme.of(context) — we pull the theme from anywhere in the tree. We set it in MyApp — we use it here.

  • Positioning: Center aligns to the center, inside it Column — a column of elements.

  • mainAxisAlignment: MainAxisAlignment.center aligns children to the center vertically.

  • Strings with variables: '$_counter' — in Dart, this is how we insert variables into text.

  • FloatingActionButton — a round button in the bottom right corner. In onPressed — our function, tooltip is needed for accessibility (read by screen readers), in child — a plus icon. Flutter gives many icons out of the box, but you can find libraries or draw your own.

Practice: Improving the Counter

Theory is important, but practice is everything. Let's refine the starter project: add a decrement button and change the layout. Here's what we got

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

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

  void _decrementCounter() {
    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,
            ),
            const SizedBox(height: 20),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: _decrementCounter,
                  child: const Icon(Icons.remove),
                ),
                const SizedBox(width: 20),
                ElevatedButton(
                  onPressed: _incrementCounter,
                  child: const Icon(Icons.add),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

What We Changed and How It Works

  1. Added a Decrement Function Just like _incrementCounter, but we decrement by 1. setState tells Flutter: "data has changed, redraw the screen".
void _decrementCounter() {
  setState(() {
    _counter--;
  });
}

Enter fullscreen mode Exit fullscreen mode
  1. Removed FloatingActionButton
    Was: one button in the bottom right corner.
    Became: two buttons in the center of the screen.
    FloatingActionButton is convenient for one main action. But we now have two actions — plus and minus. So we moved the buttons into the screen body.

  2. Added a Row with Buttons

Row(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    ElevatedButton(
      onPressed: _decrementCounter,
      child: const Icon(Icons.remove),
    ),
    const SizedBox(width: 20),
    ElevatedButton(
      onPressed: _incrementCounter,
      child: const Icon(Icons.add),
    ),
  ],
),

Enter fullscreen mode Exit fullscreen mode
  • Row — arranges elements in a row (horizontally). mainAxisAlignment: MainAxisAlignment.spaceAround — aligns elements with equal space between them.

  • ElevatedButton — a button with "elevation" (shadow, material design).

  • onPressed — we pass the function directly, without parentheses: _incrementCounter, not _incrementCounter(). Otherwise Flutter will call the function immediately during build, not on press.

Conclusion

In this article, we broke down the basic Flutter starter project and refined it a bit — added a second button and played with the layout. But this is only the tip of the iceberg. In real development, there's much more to understand.
The main thing to remember is that Flutter is built on three pillars: widgets, state, and context. Everything is a widget. State changes through setState (or more complex tools). Context gives access to theme, navigation, and parent data.
Practice, experiment, break code and fix it. That's the only way understanding comes.
Good luck in development! 🚀

Top comments (1)

Collapse
 
randal_lschwartz_dfd1ac profile image
Randal L. Schwartz

TL;DR: Check out docs.flutter.dev/learn/pathway

Whatever path you take, start with the Google-provided well-written up-to-date documentation, namely:

First, install Dart and Flutter as indicated on docs.flutter.dev/get-started/install for your platform:

on dart.dev:

on flutter.dev:

and never read a blog post or watch a video older than six months without seeking the advice of an expert. (Flutter changes fast, with releases happening almost monthly.)

Recommended videos and books: docs.flutter.dev/resources/videos and docs.flutter.dev/resources/books.

Recommended YouTube channels: youtube.com/@flutterdev and youtube.com/@FlutterCommunity and youtube.com/@RandalOnDartAndFlutter