Imagine you’re building a music player app
You have:
A Play button on the player screen
A Mini Player at the bottom of the home screen
A Playlist screen showing all songs
Now… Flutter gives you multiple ways to manage data and update the UI.
Three of the most common are:
setState — The “I just need to refresh this widget” approach
Think of setState as saying:
“Hey Flutter, just repaint this small part of the UI because something here changed.”
When to use:
Music App Example:
Updating the Play/Pause button icon when the user taps it.
class PlayButton extends StatefulWidget {
@override
_PlayButtonState createState() => _PlayButtonState();
}
class _PlayButtonState extends State<PlayButton> {
bool isPlaying = false;
void togglePlay() {
setState(() {
isPlaying = !isPlaying; // just flip the icon state
});
}
@override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow),
onPressed: togglePlay,
);
}
}
Here, only the Play button changes — no need to bother the rest of the app.
Provider — The “shared state manager”
Think of Provider as:
“I have data that multiple widgets need to know about, even if they’re far apart.”
When to use:
App-wide or screen-wide shared data
State needs to survive across multiple widgets/screens
Perfect for your music queue, playback status, user preferences
Music App Example:
Keeping track of the currently playing song so both Mini Player and Full Player Screen update together.
class MusicProvider extends ChangeNotifier {
String? currentSong;
void playSong(String song) {
currentSong = song;
notifyListeners(); // tell all listening widgets to rebuild
}
}
// Provide at top level
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => MusicProvider(),
child: MyApp(),
),
);
}
Now you can read this currentSong anywhere in your app — that’s the magic.
Consumer — The “I only want to rebuild this tiny part” trick
Think of Consumer as:
“I’ll listen to the Provider’s changes, but only rebuild this small widget, not the whole screen.”
When to use:
Music App Example:
Updating just the song title in the mini player without reloading the play button or progress bar unnecessarily.
class MiniPlayer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.grey[900],
child: Row(
children: [
Consumer<MusicProvider>(
builder: (context, music, _) {
return Text(
music.currentSong ?? "No song playing",
style: TextStyle(color: Colors.white),
);
},
),
Icon(Icons.play_arrow, color: Colors.white),
],
),
);
}
}
Only the Text widget rebuilds when the song changes — everything else stays put.