DS DevShelfHub Projects · AI tools
Cheatsheets / Flutter
Cheatsheet · Dev tooling

Flutter Cheatsheet: Widgets, State and Navigation Reference

By DevShelfHub

Widgets, state, BuildContext, navigation, layouts, gestures, animation, async, platform channels, Riverpod/Bloc patterns — the Flutter 3.x surface day-to-day.

122 items 9 min Widgets State Channels

Start hereQuick start · 6 you’ll reach for daily

New appflutter create my_app
Runflutter run -d chrome
Hot reloadr · R for restart
LayoutColumn / Row / Expanded
StatesetState(() => …)
Navigatecontext.push('/x')

Target versions · paceVersions

Targets: flutter ≥ 3.22 dart ≥ 3.4 material 3 go_router ≥ 14 riverpod ≥ 2.5

Impeller is the default renderer on iOS and Android (replacing Skia GLES). Material 3 is the default theme. Null-safety is mandatory (Dart 3 dropped the legacy mode). Riverpod 2 uses code generation; flutter_bloc 8 still ships the classic emit() API. Pinned to May 2026; check docs.flutter.dev for newer.

Install · runSetup

bash
# Install (macOS shown — Windows/Linux similar)
brew install --cask flutter             # or download from flutter.dev
flutter --version
flutter doctor                          # verifies Xcode / Android SDK / chrome

# New app
flutter create my_app
cd my_app
flutter run                             # picks the only attached device
flutter run -d chrome                   # explicit target
flutter devices                         # list available targets

# Daily loop
r       # hot reload in the running shell
R       # hot restart (resets state)
q       # quit
flutter doctor tells you exactly what’s missing — Android SDK licenses, Xcode command-line tools, CocoaPods. Don’t skip it: a green checklist saves an afternoon of guessing-why-iOS-won’t-build.

Common imports · pubspec depsCommon imports

Core widgets ship in flutter/material.dart (or cupertino.dart). Add packages to pubspec.yaml and run flutter pub get.

import 'package:flutter/material.dart';Material widgets — Scaffold, AppBar, etc.
import 'package:flutter/cupertino.dart';iOS-style widgets.
import 'package:flutter/services.dart';Clipboard, haptics, system UI overlays.
import 'dart:async';Future, Stream, Timer.
import 'dart:convert';jsonEncode / jsonDecode.
import 'package:http/http.dart' as http;Plain HTTP client.
import 'package:dio/dio.dart';Richer HTTP (interceptors, cancel, timeout).
import 'package:go_router/go_router.dart';Preferred Declarative routing.
import 'package:flutter_riverpod/flutter_riverpod.dart';State management (compile-safe).
import 'package:flutter_bloc/flutter_bloc.dart';BLoC pattern.
import 'package:shared_preferences/shared_preferences.dart';Key/value persistence.
import 'package:hive/hive.dart';Fast on-disk NoSQL.
import 'package:freezed_annotation/freezed_annotation.dart';Sealed-class + immutability codegen.
import 'package:json_annotation/json_annotation.dart';JSON serialization codegen.

Stateless vs statefulWidgets

class X extends StatelessWidgetPure function of inputs. No mutable state.
class X extends StatefulWidget → State<X>Holds state across rebuilds.
@override Widget build(BuildContext c)The render method. Returns a widget tree.
setState(() => field = newValue)Mark state dirty → rebuild.
initState() / dispose()Lifecycle: subscribe / unsubscribe.
didChangeDependencies()Runs when InheritedWidget ancestors change.
const MyWidget({super.key})Always pass key & use const where possible.
BuildContext contextPointer into the widget tree. Use to look up ancestors.

Minimal app

javascript
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: 'Demo',
      theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
      home: const Counter(),
    );
  }
}

class Counter extends StatefulWidget {
  const Counter({super.key});
  @override
  State createState() => _CounterState();
}

class _CounterState extends State {
  int n = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: Center(child: Text('$n', style: const TextStyle(fontSize: 48))),
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() => n++),
        child: const Icon(Icons.add),
      ),
    );
  }
}

Compose widgetsLayout

Linear & axis

Column(children: [...])Vertical stack.
Row(children: [...])Horizontal.
Expanded(child: …)Fills remaining space along the axis.
Flexible(flex: 2, child: …)Proportional sizing.
Spacer()Empty expandable space.
SizedBox(width: 12, height: 12)Fixed gap or box.
Wrap(spacing: 8, children: …)Like Row, wraps to next line when full.

Containers & padding

Container(padding: …, decoration: …)Box with paint + layout. Convenience wrapper.
Padding(padding: EdgeInsets.all(16), child: …)Padding only. Lighter than Container.
EdgeInsets.symmetric(horizontal: 12, vertical: 8)Common pattern.
Center(child: …)Centers child in available space.
Align(alignment: Alignment.topRight, child: …)Position child within parent.
Stack(children: [...])Overlap widgets (z-axis).
Positioned(top: 0, right: 0, child: …)Absolute placement inside a Stack.

Scaffolds & surfaces

Scaffold(appBar: …, body: …, floatingActionButton: …)Standard page chrome.
AppBar(title: Text('…'), actions: [...])Top app bar.
SafeArea(child: …)Insets around notch / status bar.
Card(child: …)Elevated rounded surface.
Material(child: …)Provides ink-splash + theming surface.
SingleChildScrollView(child: Column(…))When content might overflow vertically.

ScrollablesLists & grids

ListView(children: […])Static list. Builds all children eagerly.
ListView.builder(itemCount, itemBuilder)Preferred Lazy — only builds visible rows.
ListView.separated(itemCount, itemBuilder, separatorBuilder)Builder + divider widget between rows.
ListTile(title, subtitle, leading, trailing, onTap)Pre-styled row.
GridView.count(crossAxisCount: 2, children: …)Fixed-column grid.
GridView.builder(gridDelegate: …, itemBuilder: …)Lazy grid.
RefreshIndicator(onRefresh, child: ListView…)Pull-to-refresh.
CustomScrollView(slivers: [...])Mix headers, lists, grids in one scroll view via slivers.
SliverAppBar(pinned: true, expandedHeight: 200)Collapsing app bar.

List with pull-to-refresh

javascript
import 'package:flutter/material.dart';

class Feed extends StatelessWidget {
  final List items;
  final Future Function() onRefresh;
  const Feed({super.key, required this.items, required this.onRefresh});

  @override
  Widget build(BuildContext context) {
    return RefreshIndicator(
      onRefresh: onRefresh,
      child: ListView.separated(
        itemCount: items.length,
        itemBuilder: (ctx, i) => ListTile(
          title: Text(items[i]),
          onTap: () => Navigator.pushNamed(ctx, '/detail', arguments: items[i]),
        ),
        separatorBuilder: (_, __) => const Divider(height: 1),
      ),
    );
  }
}

// For infinite-scroll, swap to ListView.builder + a ScrollController
// that calls fetchMore() when controller.position.pixels >= maxScrollExtent - 200.

Imperative Navigator

Navigator.push(c, MaterialPageRoute(builder: (_) => Detail()))Push a new screen.
Navigator.pop(c)Pop back. Pass a value to return it.
Navigator.pushNamed(c, '/detail', arguments: x)Named route; arguments via ModalRoute.of(c).settings.
Navigator.pushReplacement(c, …)Swap current screen.
Navigator.pushAndRemoveUntil(c, route, (r) => false)Clear stack (post-login flow).

go_router (declarative) Preferred

GoRouter(routes: [GoRoute(path: '/', builder: …)])Define the route table.
MaterialApp.router(routerConfig: r)Wire router to app.
context.go('/items/42')Replace the stack.
context.push('/items/42')Push on top.
context.pop()Pop.
state.pathParameters['id']Read :id from URL.
state.uri.queryParameters['q']Read query string.
ShellRoute(builder: …, routes: [...])Wrap routes in a persistent shell (bottom nav bar).

go_router example

javascript
// pubspec.yaml: go_router: ^14.0.0
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

final _router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (ctx, st) => const HomeScreen(),
      routes: [
        GoRoute(
          path: 'item/:id',
          builder: (ctx, st) => DetailScreen(id: st.pathParameters['id']!),
        ),
      ],
    ),
  ],
);

class App extends StatelessWidget {
  const App({super.key});
  @override
  Widget build(BuildContext context) => MaterialApp.router(routerConfig: _router);
}

// Navigate from any widget:
// context.go('/item/42');           // replace stack
// context.push('/item/42');         // push on top
// context.pop();                    // pop

From setState to RiverpodState management

Built-in

setState(() => field = v)Local widget state. Always start here.
InheritedWidgetLow-level ancestor data propagation. Underlies everything else.
ValueNotifier<int>(0) + ValueListenableBuilderTiny reactive value for shared state.
ChangeNotifier + ListenableBuilderClass-based observable. notifyListeners().

Riverpod 2 Preferred

ProviderScope(child: App())Wrap main() root.
@riverpod int counter(Ref ref) => 0;Codegen provider declaration.
ref.watch(counterProvider)Subscribe; rebuilds on change.
ref.read(counterProvider.notifier).state++Mutate without subscribing.
ConsumerWidget · build(c, WidgetRef ref)Widget with access to ref.
FutureProvider / StreamProviderAsync providers; auto-emit AsyncValue.

flutter_bloc

class CounterCubit extends Cubit<int> {…}Sync state holder. emit(newState).
class XBloc extends Bloc<Event, State> {…}Event-driven state machine.
BlocProvider(create: (_) => CounterCubit())Provide a bloc/cubit to the tree.
BlocBuilder<CounterCubit,int>(builder: …)Rebuild on state change.
context.read<CounterCubit>().increment()Get bloc without listening.

Futures · streamsAsync

Future<T> load() async { return …; }Async function returns a Future.
final r = await http.get(uri);Suspend until resolved. Only inside async.
try { … } on Exception catch (e) { … }Async errors are exceptions, not callbacks.
Future.wait([a, b])Run in parallel; resolves when all do.
Stream<T> ticks() async* { yield …; }Generator stream.
await for (final v in stream) { … }Iterate a stream.
FutureBuilder<T>(future: …, builder: (c,snap) => …)Reactive widget that rebuilds on Future state.
StreamBuilder<T>(stream: …, builder: …)Same, for streams.
Timer.periodic(Duration(seconds: 1), (_) => …)Repeating callback. Cancel in dispose.

FutureBuilder example

javascript
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

class Posts extends StatefulWidget {
  const Posts({super.key});
  @override
  State createState() => _PostsState();
}

class _PostsState extends State {
  late Future> _future;

  @override
  void initState() {
    super.initState();
    _future = _load();
  }

  Future> _load() async {
    final r = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts?_limit=10'));
    return jsonDecode(r.body) as List;
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder>(
      future: _future,
      builder: (ctx, snap) {
        if (snap.connectionState != ConnectionState.done) return const CircularProgressIndicator();
        if (snap.hasError) return Text('Error: ${snap.error}');
        final items = snap.data!;
        return ListView(
          children: [for (final p in items) ListTile(title: Text(p['title']))],
        );
      },
    );
  }
}

Inputs · validationForms & input

TextField(decoration: InputDecoration(labelText: 'Name'))Plain text input.
TextEditingController()Read / set the value. Dispose in dispose().
controller.text / controller.clear()Read / reset.
Form(key: formKey, child: Column(...))Wraps multiple fields; lets you save/validate together.
TextFormField(validator: (v) => v.isEmpty ? 'err' : null)Field with validation.
formKey.currentState!.validate()Run all validators.
FocusNode + autofocus: trueProgrammatic focus.
Checkbox / Switch / SliderStandard interactive widgets.
DropdownButton<T>(items: …, onChanged: …)Material dropdown.

Material 3Theming & styling

ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo)Generate a full M3 palette from one seed colour.
ColorScheme.fromSeed(seedColor: …)Manual seed-based scheme.
Theme.of(context).colorScheme.primaryRead a theme colour inside build.
Theme.of(context).textTheme.titleLargeRead a text style.
MaterialApp(theme: light, darkTheme: dark, themeMode: …)Light/dark theme pair.
MediaQuery.of(context).size.widthScreen size. Legacy Prefer LayoutBuilder for widget-local sizing.
LayoutBuilder(builder: (c, constraints) => …)Responsive: react to incoming constraints.
TextStyle(fontSize: 16, fontWeight: FontWeight.w600)Inline text styling.

Implicit & explicitAnimation

Implicit (one-liners)

AnimatedContainer(duration: …, color: …)Animates whenever its props change.
AnimatedOpacity(opacity, duration)Fade.
AnimatedAlign / AnimatedPositionedReposition.
TweenAnimationBuilder<T>(tween, duration, builder)Animate any value without a controller.
Hero(tag: 'avatar', child: …)Shared-element transition across routes.

Explicit (controllers)

AnimationController(vsync: this, duration: …)Drives [0, 1]. Needs TickerProviderStateMixin.
Tween<double>(begin: 0, end: 1).animate(ctrl)Map controller value to range.
CurvedAnimation(parent, curve: Curves.easeOut)Apply an easing curve.
AnimatedBuilder(animation, builder: (c, child) => …)Rebuild on each tick.
controller.forward() / reverse() / repeat()Drive it.
@override dispose() { ctrl.dispose(); super.dispose(); }Always dispose. Leaking controllers leak vsync ticks.

Channels · FFIPlatform & native

Platform.isIOS / isAndroid / isMacOSBranch by OS. dart:io.
kIsWebConstant true on web; preserves tree-shaking.
MethodChannel('app/native').invokeMethod('x', args)Call Kotlin/Swift from Dart. Async.
EventChannel('app/sensor').receiveBroadcastStream()Subscribe to native event stream.
Pigeon (codegen for channels)Preferred Typesafe channels — generates Dart/Kotlin/Swift bindings from one spec.
dart:ffiDirect C ABI. Use for native libs without a method-channel hop.
SystemChrome.setPreferredOrientations([…])Lock orientation.
Clipboard.setData(ClipboardData(text: '…'))Copy to clipboard.
HapticFeedback.lightImpact()Vibration / haptic.

Full app · ~60 linesEnd-to-end · List + Detail + Persist

Fetches a feed, navigates to a detail screen via go_router, persists a favourite to SharedPreferences. Add the three packages to pubspec.yaml and run.

javascript
// pubspec.yaml: http, shared_preferences, go_router
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

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

final _router = GoRouter(routes: [
  GoRoute(path: '/', builder: (c, s) => const ListScreen()),
  GoRoute(
    path: '/detail',
    builder: (c, s) => DetailScreen(post: s.extra as Map),
  ),
]);

class App extends StatelessWidget {
  const App({super.key});
  @override
  Widget build(BuildContext c) => MaterialApp.router(routerConfig: _router);
}

class ListScreen extends StatefulWidget {
  const ListScreen({super.key});
  @override
  State createState() => _ListScreenState();
}

class _ListScreenState extends State {
  List items = [];

  @override
  void initState() {
    super.initState();
    http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts?_limit=10'))
        .then((r) => setState(() => items = jsonDecode(r.body)));
  }

  @override
  Widget build(BuildContext c) => Scaffold(
        appBar: AppBar(title: const Text('Posts')),
        body: ListView(children: [
          for (final p in items)
            ListTile(title: Text(p['title']), onTap: () => c.push('/detail', extra: p)),
        ]),
      );
}

class DetailScreen extends StatelessWidget {
  final Map post;
  const DetailScreen({super.key, required this.post});

  Future _fav() async {
    final p = await SharedPreferences.getInstance();
    await p.setBool('fav:${post['id']}', true);
  }

  @override
  Widget build(BuildContext c) => Scaffold(
        appBar: AppBar(title: Text(post['title'])),
        body: Padding(
          padding: const EdgeInsets.all(16),
          child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
            Text(post['body']),
            TextButton(onPressed: _fav, child: const Text('Save ★')),
          ]),
        ),
      );
}

Build · releaseTooling

flutter pub getInstall deps from pubspec.yaml.
flutter pub add httpAdd a dependency.
flutter pub run build_runner build --delete-conflicting-outputsCodegen pass (freezed, json_serializable, riverpod_generator).
flutter analyzeStatic analysis. Fail-loud in CI.
dart format .Format every file in tree.
flutter testRun widget + unit tests.
flutter build apk --releaseRelease APK. Use appbundle for Play Store.
flutter build ipa --releaseiOS archive. Needs Xcode + signing.
flutter build web --releaseStatic site bundle.
flutter cleanWipe build/ when things get weird.

Best practiceGood to know

Use const constructors religiously. A const widget is reused across rebuilds instead of allocated. The analyser will tell you which ones can be const — turn the lints on.
Hot reload preserves state; hot restart resets it. Use r for UI tweaks, R when you change initState, top-level main(), or app-wide singletons.
Prefer ListView.builder for anything > ~20 rows. The default ListView(children: […]) builds every child eagerly — easy memory bloat. .builder is lazy.

Common trapsWatch out for

setState after dispose throws. Async callbacks that complete after the widget unmounts will trip this. Guard with if (!mounted) return; before calling setState.
BuildContext isn’t a global. Using one across an await can yield a defunct context. Capture what you need before awaiting (e.g. final messenger = ScaffoldMessenger.of(c);).
Adding a native package needs a full rebuild. Hot reload won’t pick up new platform code. Stop the app and run flutter run again. iOS may also need pod install in ios/.

Go deeperSee also

Flutter FAQ

What is Flutter used for?

Flutter is Google's open-source UI toolkit for building natively compiled apps from a single codebase across mobile (iOS, Android), web, desktop (macOS, Windows, Linux), and embedded targets. It uses the Dart language and its own rendering engine rather than platform-native widgets.

What is the difference between StatelessWidget and StatefulWidget in Flutter?

A StatelessWidget renders once and has no mutable state — it rebuilds only when its parent passes new data. A StatefulWidget owns a separate State object that can call setState() to trigger rebuilds when internal data changes. Use StatefulWidget when the widget needs to react to user interaction or async events.

What is BuildContext in Flutter?

BuildContext is a handle to the location of a widget in the widget tree. It lets you look up inherited widgets (like Theme and MediaQuery), navigate via Navigator or go_router, and call Scaffold.of() or similar helpers. Never store a BuildContext across async gaps without checking mounted first.

What state management solution should I use in Flutter?

For small apps, setState and InheritedWidget are sufficient. Riverpod (with code generation) is the most widely recommended for medium-to-large apps because it is compile-safe and testable. Flutter Bloc is a strong alternative if your team prefers explicit events and states over providers.

Is Flutter free and open source?

Yes. Flutter is BSD-licensed and maintained by Google with a large open-source community. The SDK, engine, and all first-party packages are free to use for personal and commercial projects.

What is hot reload in Flutter?

Hot reload injects updated source code into the running Dart VM and rebuilds the widget tree without losing app state — usually in under a second. Press r in the terminal or click the lightning bolt in your IDE. Hot restart (R) is a full restart that clears state but is still faster than a cold rebuild.