In Flutter, state refers to any data that can change over time: user input, API responses, toggle switches, counters, and more. Managing state effectively is critical for building apps that are both responsive and maintainable. Let’s explore the different ways Flutter lets you manage state, from simple to complex.
What is State?
- StatelessWidget: No mutable state. UI is static unless rebuilt from outside.
- StatefulWidget: Holds mutable state that can change during its lifecycle.

A. Managing State with setState
(Local State)
setState
is perfect for simple, local scenarios like toggles, counters, or input fields. Here’s a classic example:
import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) => const MaterialApp(home: CounterScreen()); } class CounterScreen extends StatefulWidget { const CounterScreen({super.key}); @override State<CounterScreen> createState() => _CounterScreenState(); } class _CounterScreenState extends State<CounterScreen> { int _counter = 0; void _increment() => setState(() => _counter++); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Counter')), body: Center(child: Text('Count: $_counter', style: const TextStyle(fontSize: 24))), floatingActionButton: FloatingActionButton( onPressed: _increment, child: const Icon(Icons.add), ), ); } }
Key Points:
- The counter’s value lives in the widget’s state.
setState
triggers a rebuild of the UI with the new value.
B. Lifting State Up
When multiple widgets need access to the same state, move (“lift”) the state up to their common ancestor. Then:
- Store state in a parent widget.
- Pass data down via constructor parameters.
- Notify the parent of changes with callbacks.
C. Managing Global/App-wide State
For larger apps, passing state manually becomes messy. This is where state management packages shine. Popular solutions include:
- Provider – the official Flutter-recommended package.
- Riverpod – a modern, improved take on Provider.
- Others: Bloc, Redux, MobX, GetX.
D. Example with Provider
First, add Provider to pubspec.yaml
:
dependencies: provider: ^6.0.0
Then, here’s a simple counter app using Provider for app-wide state:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; // 1. State class class CounterModel with ChangeNotifier { int value = 0; void increment() { value++; notifyListeners(); // Triggers UI update } } void main() { runApp( ChangeNotifierProvider( create: (_) => CounterModel(), child: const MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp(home: const CounterScreen()); } } class CounterScreen extends StatelessWidget { const CounterScreen({super.key}); @override Widget build(BuildContext context) { final counter = context.watch<CounterModel>(); return Scaffold( appBar: AppBar(title: const Text('Provider Counter')), body: Center(child: Text('Count: ${counter.value}', style: const TextStyle(fontSize: 24))), floatingActionButton: FloatingActionButton( onPressed: counter.increment, child: const Icon(Icons.add), ), ); } }
Key Concepts:
ChangeNotifierProvider
makes state accessible anywhere in the widget tree.- Widgets listen for changes with
context.watch
(orcontext.read
for one-time access).
E. When to Use What?
- setState: Small, local state (simple screens, one widget).
- Provider/Riverpod: Sharing state between multiple widgets or app-wide state.
- Bloc/Redux: Complex apps, larger teams, advanced scenarios.
Key Takeaways
- Always ask: where does this state live, and who needs access?
- Start simple with
setState
; scale up to Provider or Riverpod as your app grows. - Flutter gives you flexibility: you can manage small or large state needs effectively.
Next Up: Navigation, Routing, and Animations in Flutter
In Chapter 4 of Flight with Flutter, we’ll dive into navigation, routing, and animations—essential for creating apps that feel seamless and dynamic.