TL;DR
Summary
State management is the #1 source of confusion for Flutter beginners. Pick the wrong one and your code becomes spaghetti. Here's the clear guide.
What is "State" in Flutter?
State = any data that can change and affect your UI. The user is logged in or not — that's state. The cart has 3 items — that's state. A button is loading — that's state. Managing state means deciding: where does this data live, and how does it flow through the app?
Provider — The Beginner's Choice
Provider is Flutter's official recommendation for small to medium apps. Simple, well-documented, backed by Google.
// 1. Define your state class
class CounterState extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // Tells Flutter to rebuild
}
}
// 2. Wrap your app with the provider
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CounterState(),
child: MyApp(),
),
);
}
// 3. Read and update from any widget
Consumer<CounterState>(
builder: (context, state, _) => Text('${state.count}'),
)
// Update:
context.read<CounterState>().increment();
Riverpod — The Modern Choice
Riverpod is Provider's spiritual successor, created by the same developer. It's type-safe, testable, and doesn't depend on the widget tree. Recommended for new projects.
import 'package:flutter_riverpod/flutter_riverpod.dart';
// 1. Define a Notifier
class CounterNotifier extends Notifier<int> {
@override
int build() => 0;
void increment() => state++;
}
// 2. Create a provider
final counterProvider = NotifierProvider<CounterNotifier, int>(
CounterNotifier.new,
);
// 3. Use in widget (ConsumerWidget instead of StatelessWidget)
class MyWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Column(children: [
Text('$count'),
ElevatedButton(
onPressed: () => ref.read(counterProvider.notifier).increment(),
child: Text('Increment'),
),
]);
}
}
Bloc — For Complex Apps
Bloc (Business Logic Component) separates UI from logic using events and states. Verbose but extremely testable and scalable. Used in large apps like big bank apps and e-commerce platforms.
// Events
abstract class CounterEvent {}
class Increment extends CounterEvent {}
class Decrement extends CounterEvent {}
// States (just an int here)
// In real apps, states are classes with data
// Bloc
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<Increment>((event, emit) => emit(state + 1));
on<Decrement>((event, emit) => emit(state - 1));
}
}
// In widget
BlocBuilder<CounterBloc, int>(
builder: (context, count) => Text('$count'),
)
// Dispatch event:
context.read<CounterBloc>().add(Increment());
- Provider
- Riverpod
- Bloc
- Small apps, beginners, quick projects
- New projects, medium/large apps, teams
- Large enterprise apps, teams that need structure
My recommendation: start with Provider to understand the concept, then move to Riverpod for any serious project. Learn Bloc only if your team or client requires it.
Final Answer
There's no wrong answer — it depends on your project size and team. For a new Flutter app in 2025: use Riverpod. It has the best balance of simplicity, power, and maintainability.