Widgets And Rendering

Flutter UI is widget composition. A widget is an immutable description of part of the UI for the current configuration and state.

Minimal Shape

import 'package:flutter/material.dart';

void main() => runApp(const App());

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: Center(child: Text('Hello Flutter')),
      ),
    );
  }
}

Use StatelessWidget when the widget only depends on constructor inputs and inherited context. Use StatefulWidget when the widget owns local mutable UI state.

Widget Rules

TopicPractical rule
build()Keep it fast and side-effect free
const widgetsUse them when constructor inputs are compile-time constants
KeysAdd keys when preserving identity across reorder, insert, or remove operations matters
LayoutConstraints flow down, sizes flow up, parent sets position
MaterialUse for Material Design apps and Android-friendly defaults
CupertinoUse for iOS-styled controls where platform feel matters

Local State Example

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

  @override
  State<CounterButton> createState() => _CounterButtonState();
}

class _CounterButtonState extends State<CounterButton> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () => setState(() => count += 1),
      child: Text('Count: $count'),
    );
  }
}

This is correct for local button state. It is wrong for shared account state, cart state, sync state, or persisted settings.

Gotcha: Rebuilds are normal. The bug is expensive work during rebuild, unstable identity, or state stored in the wrong scope.