Flutter Widgets Tutorial: From Basics to Advanced

Flutter basic

Introduction

Flutter is a modern UI toolkit for building natively compiled apps across mobile, web, and desktop. In this tutorial, we’ll cover some of the most fundamental Flutter building blocks: StatelessWidget, StatefulWidget, MaterialApp, Scaffold, and super.key. Then, we’ll move on to more advanced concepts.

1. StatelessWidget — Fixed UI Components

StatelessWidget is used when your widget’s UI doesn’t change dynamically after it’s rendered.

import 'package:flutter/material.dart';

class MyStaticText extends StatelessWidget {
  const MyStaticText({super.key});

  @override
  Widget build(BuildContext context) {
    return Text('I am static and do not change!');
  }
}

When to use:

Displaying fixed texts, logos, buttons (without interactivity).

2. MaterialApp — Starting Point of Your App

Every Flutter app usually begins with a MaterialApp.

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My Flutter App',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
      ),
      home: const HomeScreen(),
    );
  }
}

Key Features:

  • Sets global theme.
  • Defines home screen.
  • Provides routing and localization.

3. super.key — Widget Identity

super.key is used to pass the key from child to the parent widget class. It helps Flutter detect changes and reuse widgets efficiently.

class MyWidget extends StatelessWidget {
  const MyWidget({super.key});
}

It is best practice to always include super.key in constructors.

4. StatefulWidget — For Dynamic & Interactive UI

Use StatefulWidget when the UI needs to change over time—like when clicking a button or toggling a switch.

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

  @override
  State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int counter = 0;

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

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

5. Scaffold — Your App Layout Container
Scaffold provides a default app layout structure. It includes:

BottomNavigationBar

AppBar

Body

Drawer

FloatingActionButton

Scaffold(
  appBar: AppBar(title: const Text("My App")),
  body: const Center(child: Text("Hello, Flutter!")),
  floatingActionButton: FloatingActionButton(
    onPressed: () {},
    child: const Icon(Icons.add),
  ),
);

Going Beyond: Advanced Widget Concepts

Widget Lifecycle

Understand the lifecycle methods:

  • initState()
  • dispose()
  • didUpdateWidget()

Keys (GlobalKey vs LocalKey)

Used to preserve widget state when widgets are reordered or recreated.

Navigation and Routing

Use Navigator.push() and named routes for multi-screen apps.

Provider & State Management

Use Provider, Riverpod, or Bloc for large-scale app state management.

Theme Customization

Use ThemeData and Theme.of(context) to build reusable and beautiful apps.

Conclusion

You’ve now learned:

  • The difference between Stateless and Stateful widgets.
  • How MaterialApp and Scaffold provide structure.
  • The importance of super.key for performance and rebuilds.
  • How to extend these basics with advanced concepts.