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