Back to Blog
FlutterState ManagementProviderRiverpod

State Management in Flutter - A Practical Guide

2 min read
State Management in Flutter - A Practical Guide

title: State Management in Flutter - A Practical Guide excerpt: Explore different state management solutions in Flutter and learn when to use each approach. publishedDate: "2024-11-01" tags: ["Flutter", "State Management", "Provider", "Riverpod"] coverImage: "/images/blog/flutter-state-management.jpg" isDraft: false

State Management in Flutter

State management is one of the most important concepts in Flutter development. Choosing the right approach can make or break your app's architecture.

What is State?

State is any data that can change over time in your application. This includes:

  • User input
  • Data fetched from APIs
  • UI state (loading, error, success)
  • User preferences

Built-in Solutions

setState

The simplest form of state management, perfect for local widget state:

class CounterPage extends StatefulWidget {
  @override
  _CounterPageState createState() => _CounterPageState();
}
 
class _CounterPageState extends State<CounterPage> {
  int _count = 0;
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('Count: $_count'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() => _count++),
        child: Icon(Icons.add),
      ),
    );
  }
}

InheritedWidget

For sharing state down the widget tree:

class CounterProvider extends InheritedWidget {
  final int count;
  final VoidCallback increment;
 
  const CounterProvider({
    required this.count,
    required this.increment,
    required Widget child,
  }) : super(child: child);
 
  static CounterProvider of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<CounterProvider>()!;
  }
 
  @override
  bool updateShouldNotify(CounterProvider oldWidget) {
    return count != oldWidget.count;
  }
}

Provider Package

Provider is the recommended solution for most Flutter apps:

// Define your state
class CounterNotifier extends ChangeNotifier {
  int _count = 0;
  int get count => _count;
 
  void increment() {
    _count++;
    notifyListeners();
  }
}
 
// Provide it
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => CounterNotifier(),
      child: MyApp(),
    ),
  );
}
 
// Consume it
class CounterDisplay extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = context.watch<CounterNotifier>();
    return Text('Count: ${counter.count}');
  }
}

Riverpod

A more robust alternative to Provider:

// Define a provider
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
  return CounterNotifier();
});
 
class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);
 
  void increment() => state++;
}
 
// Use it in a widget
class CounterWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Text('Count: $count');
  }
}

Choosing the Right Solution

SolutionBest For
setStateSimple, local state
ProviderMedium complexity apps
RiverpodComplex apps, testability
BLoCEnterprise apps, strict patterns

Conclusion

Start simple with setState, and only introduce more complex solutions when needed. Remember: the best state management is the one your team understands.