Skip to content

Flutter Framework Introduction: A Practical Overview

Get into Flutter basics: Dart language, Skia renderer, widget tree, hot reload, single iOS and Android codebase. Compared with React Native.

· · 8 min read
Flutter widget tree diagram next to a phone showing the same UI rendered on iOS and Android

Quick Take

Flutter is Google's UI framework for building one codebase that ships to iOS, Android, web, and desktop. It's written in Dart, renders with Skia, and gets you sub-second hot reloads. Here's a working introduction with comparisons to React Native and Kotlin Multiplatform.

Quick take: Flutter is Google's open-source UI framework for one-codebase apps that ship to iOS, Android, web, Windows, macOS, and Linux. You write in Dart, the framework renders with Skia (transitioning to Impeller), and the developer experience centres on sub-second hot reload. Compared to React Native, Flutter draws every pixel itself instead of bridging to native widgets, which gives consistent visuals at the cost of native-look fidelity, structured as a tutorial, with practical examples, an overview of each option.

Flutter shipped 1.0 in December 2018. By the State of Flutter survey in 2024, over 1 million apps had been published using it, and the framework supports six target platforms from one codebase. That's a real productivity story, but the trade-offs matter. Flutter isn't a magic free lunch, it's a specific architectural bet that pays off in some domains and hurts in others.

I've shipped two Flutter apps for clients (a fitness app and an internal logistics tool) and tried Flutter for web on a third project. Honest take: I'd reach for Flutter again for the same kinds of apps. I wouldn't pick it for a graphics-heavy game, a deeply native-feeling iOS app, or a content-driven website. The framework's choices push you toward certain kinds of products and away from others.

What Is Flutter and Why Does It Exist?

Flutter is a UI framework, not a mobile platform. It runs on top of whichever OS the user has, draws its own widgets onto a canvas via Skia (or Impeller, the next-gen renderer), and compiles your Dart code to native ARM machine code for release builds. The pitch: write once, ship everywhere, without losing performance.

The "draw your own pixels" decision is what makes Flutter different. React Native, Xamarin, and Cordova all bridge from a non-native language to platform widgets, you write JS or C#, and a UIButton or an Android Button appears under the hood. Flutter skips that bridge entirely. The button you see on screen is a Flutter-drawn button, not a UIButton.

Why does that matter? Two reasons:

  • Visual consistency: your app looks the same on iOS 17 and Android 14, on Pixel and on Samsung. Designers love this.
  • No bridge tax: no marshalling data between JS and native, which used to be React Native's main performance ceiling.

Trade-off: your iOS app doesn't feel exactly like a native iOS app. Cupertino widgets help (Flutter ships an iOS-style component library), but the small details (the spring animation on a UISegmentedControl, the haptic on a UIPicker) need work to replicate. Some teams care, some don't.

How Does Flutter Use Dart?

Dart is the language. It's a Google-designed, class-based, sound-null-safe language with a syntax close to a mix of TypeScript and Java. The full language tour at dart.dev/language is the canonical reference. Quick taste:

void main() {
  final greeting = sayHello('World');
  print(greeting);
}

String sayHello(String name) {
  return 'Hello, $name!';
}

class User {
  final String name;
  final int age;

  const User({required this.name, required this.age});

  String describe() => '$name, age $age';
}

A few things to notice:

  • final declares a one-time-assignable variable (the equivalent of Kotlin's val)
  • String interpolation uses $variable or ${expression}
  • Constructors can use named parameters with required and default values
  • Single-expression methods use => instead of { return ...; }
  • Sound null safety means String is non-nullable, String? is nullable, enforced at compile time

Dart compiles two ways: JIT for development (gives you hot reload) and AOT for release (compiles to ARM machine code, ships in your IPA or APK). That dual-mode is the technical underpinning of Flutter's developer experience.

The downside of Dart: it's almost exclusively a Flutter language. Outside Flutter, the Dart ecosystem is small. If you stop doing Flutter, your Dart skills don't transfer to many other jobs. Worth knowing before you commit a year of your career to it.

A 3D render of layered chat-app UI panels arranged like nested cards
Photo by Alex Shuper on Unsplash

What Is the Widget Tree?

Everything in Flutter is a widget. Buttons are widgets. Padding is a widget. Centering is a widget. Even your app's root is a widget. The entire UI is a tree of nested widgets, rebuilt whenever state changes.

A minimal Flutter 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: 'Hello Flutter',
      home: Scaffold(
        appBar: AppBar(title: const Text('Hello')),
        body: const Center(
          child: Text('Hello, Flutter', style: TextStyle(fontSize: 24)),
        ),
      ),
    );
  }
}

The tree here: MyApp -> MaterialApp -> Scaffold -> (AppBar + Center) -> Text. Every node implements a build method that returns more widgets. There's no XML, no separate template language, no JSX-style hybrid, just Dart code.

StatelessWidget vs StatefulWidget

Two flavours of widget matter day to day:

  • StatelessWidget: pure function of its inputs. Rebuilds when the parent rebuilds it with new constructor args. No internal state.
  • StatefulWidget: holds mutable state in a paired State object. Calls setState to trigger a rebuild.

Quick counter example:

class Counter extends StatefulWidget {
  const Counter({super.key});

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int count = 0;

  void increment() {
    setState(() {
      count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: $count'),
        ElevatedButton(onPressed: increment, child: const Text('Add')),
      ],
    );
  }
}

That's the entire stateful pattern. setState schedules a rebuild on the next frame, the framework diffs the new widget tree against the old, and only the changed parts actually repaint. For state that lives beyond one widget, real apps add a state-management library, Provider, Riverpod, Bloc, or signals-style Solid Signals. Each has its fans, and the Flutter docs cover the trade-offs at docs.flutter.dev/data-and-backend/state-mgmt/options.

How Does Hot Reload Actually Work?

Hot reload injects new Dart source into the running Dart VM, rebuilds the widget tree from the current root, and preserves your app's state. Press r in the terminal (or save in your editor with auto-reload) and the change appears in under a second.

Why does this matter? Because UI work is iteration-heavy. Tweak a colour, check the result. Adjust a spacing, check again. With native iOS or Android, every change is a full rebuild and restart, maybe 30 seconds. With Flutter hot reload, it's under one second. Over a day of UI work, that compounds into hours saved.

When hot reload doesn't work: changes to main(), top-level fields, enums, generic type parameters, or class hierarchies. For those, you need a hot restart (R), which is slower but still much faster than a full rebuild.

In my experience, hot reload is the single feature most likely to convert a sceptical developer. Watch someone tweak a button colour and see it apply mid-app without losing state, and the case for Flutter makes itself.

Two smartphones side by side showing different app interfaces
Photo by Andrey Matveev on Unsplash

Flutter vs React Native vs Kotlin Multiplatform

The three obvious cross-platform options in 2026. The honest comparison:

FlutterReact NativeKotlin Multiplatform
LanguageDartJavaScript or TypeScriptKotlin
UI renderingSkia, custom widgetsNative widgets via bridgeNative UI (SwiftUI / Compose)
Code sharingUI + logic, all platformsUI + logic, mostly mobileLogic only, UI is native
PerformanceNear-native, AOT compiledGood with new architectureNative (since UI is native)
Native lookNeeds work for iOS feelInherits from nativeNative by definition
Best forBranded UI, custom designWeb-team-friendly mobileShared business logic, native UI

There's no universal winner. Flutter wins when you have a distinct brand and want pixel-perfect consistency across platforms. React Native wins when your team is already a React shop and the app is mostly views over an API. Kotlin Multiplatform wins when you want to ship truly native UI on each platform but share the data layer, the choice for teams that already have separate iOS and Android specialists.

I picked Flutter for the fitness app because the client had a strong brand and wanted the same visual feel everywhere. I'd pick KMP today for a team that already has both iOS and Android engineers and doesn't want to throw out their existing native UI work.

What Should You Build First?

Run flutter create hello_flutter, open the project in VS Code or Android Studio, and start the default counter app. That generated app is the closest thing Flutter has to a Hello World, and it shows StatefulWidget, the build method, and hot reload all in one. Modify the increment to multiply, save, and watch the running app update.

From there, the typical next steps: add a Container widget around the text, swap Column for Row, try a ListView of items. Container is the layout widget you'll reach for most, and the linked guide below covers every property worth knowing. Build five or six small UI samples and the widget mental model clicks fast.

Frequently Asked Questions

What language do you write Flutter apps in?
Dart. It's a Google-designed language with a syntax close to TypeScript or Java, single inheritance, sound null safety, and ahead-of-time compilation for release builds. Flutter is essentially the only major framework using Dart, which means learning it pays off only if you commit to Flutter.
How does Flutter differ from React Native?
React Native bridges JavaScript to native iOS and Android widgets. Flutter ships its own renderer (Skia, soon Impeller) and draws every pixel itself, so the same code looks identical on both platforms. Trade-off: Flutter apps look less like native widgets, which is sometimes a feature (consistent branding) and sometimes a bug (off-platform feel).
Is Flutter good for production apps?
Yes. Google Ads, Google Pay, BMW's My BMW app, and Alibaba's Xianyu all use Flutter at scale. The performance is close to native because everything compiles to ARM machine code via AOT. The places to be careful: heavy platform integration, gameplay-style graphics, and apps that need to look 100% native on iOS.
What is hot reload?
Hot reload injects updated source code into the running Dart VM and rebuilds the widget tree, usually in under a second. It preserves state, so you can tweak a colour or fix a layout and see the result without losing your form data or navigation stack. It's the single biggest reason developers stick with Flutter once they try it.
Can Flutter target the web and desktop?
Yes, both are stable as of 2024. Web ships via CanvasKit (which compiles Skia to WebAssembly) or HTML rendering. Desktop covers Windows, macOS, and Linux. The catch: web performance lags native mobile, and desktop is less mature than mobile, but for internal tools and dashboards, it's solid.