In the world of mobile app development, managing state is crucial for building scalable and efficient apps. When it comes to Flutter, there are various strategies for managing state, from simple to highly scalable. In this article, we'll explore six popular ways of managing state in Flutter apps, including real examples and best practices.
1. Using setState()
When it comes to managing state in Flutter, the simplest way is by using the built-in setState() method in a StatefulWidget. This approach is ideal for local UI state where the state belongs to one widget and doesn't need to be shared across the app.
Example: Counter App with setState()
`dart
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: CounterScreen());
}
}
class CounterScreen extends StatefulWidget {
@override
_CounterScreenState createState() => _CounterScreenState();
}
class _CounterScreenState extends State
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('setState Counter')),
body: Center(
child: Text('Count: $_count', style: TextStyle(fontSize: 24)),
),
floatingActionButton: FloatingActionButton(
onPressed: _increment,
child: Icon(Icons.add),
),
);
}
}
`
Pros:
- Very simple to implement
- Great for local or temporary UI state
- No external dependencies
Cons:
- Doesn't scale for large apps
- Hard to share state between widgets
- Logic mixed with UI
Use it when:
- Prototyping or building small widgets
- Handling isolated UI state (e.g., toggling a button, showing a modal)
2. InheritedWidget â Flutterâs Foundation
InheritedWidget is the low-level mechanism that Flutter uses to propagate data down the widget tree. Most state management solutions (including Provider) are built on top of it. Understanding InheritedWidget helps you grasp how Flutter's state management works under the hood.
Example: Theme Manager with InheritedWidget
`dart
import 'package:flutter/material.dart';
class AppTheme extends InheritedWidget {
final bool isDarkMode;
final Function toggleTheme;
const AppTheme({
Key? key,
required this.isDarkMode,
required this.toggleTheme,
required Widget child,
}) : super(key: key, child: child);
static AppTheme? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType
}
@override
bool updateShouldNotify(AppTheme oldWidget) {
return isDarkMode != oldWidget.isDarkMode;
}
}
class ThemeManager extends StatefulWidget {
final Widget child;
const ThemeManager({Key? key, required this.child}) : super(key: key);
@override
_ThemeManagerState createState() => _ThemeManagerState();
}
class _ThemeManagerState extends State
bool _isDarkMode = false;
void _toggleTheme() {
setState(() {
_isDarkMode = !_isDarkMode;
});
}
@override
Widget build(BuildContext context) {
return AppTheme(
isDarkMode: _isDarkMode,
toggleTheme: _toggleTheme,
child: widget.child,
);
}
}
`
Pros:
- Built into Flutter â no external dependencies
- Efficient widget rebuilds
- Foundation for understanding other solutions
- Direct control over propagation logic
Cons:
- Verbose boilerplate code
- Requires wrapper StatefulWidget
- Easy to make mistakes
- Not beginner-friendly
Use it when:
- Learning how Flutter's state management works internally
- Building custom state management solutions
- You need very specific control over state propagation