Package selection guidance compiled from public package information, community data, and project usage.
June is a lightweight, modern state management library for Flutter that mirrors Flutter's native state management patterns. It emphasizes simplicity and readability, making it easy to manage state in a reactive way without boilerplate. Designed with developer experience in mind, June integrates seamlessly into existing Flutter projects and supports all major platforms. Its API is intuitive, promoting clean code structure and efficient updates. June is ideal for developers seeking a minimal yet powerful alternative to more complex state solutions.
Use cases
Simple app state management
Reactive UI updates
Cross-platform Flutter apps
Lightweight state solution
Easy integration with existing codebases
Key features
Minimal boilerplate
Flutter-native pattern alignment
Reactive state updates
Platform-agnostic
Clean, readable API
No external dependencies
Suitable for
Beginner to intermediate Flutter developers
Small to medium-sized applications
Projects prioritizing simplicity
Teams wanting fast onboarding
Apps targeting multiple platforms
Considerations
Limited advanced features compared to larger libraries
Not ideal for very large-scale state needs
Less community support than mature alternatives
Best suited for straightforward state flows
Original README
Original README snapshot from the linked source repository. Visit the repository for the latest content.
You learned Flutter. You learned setState. It clicked.
Then you added a library and suddenly you're learning a new framework inside your framework — providers, stores, atoms, streams, reducers.
June refuses to do that.
It gives you shared, reactive state that works exactly like Flutter's own state management — just without the widget tree dependency. That's it.
At a glance
Before June — state trapped in a widget
class_CounterStateextendsState<Counter> {
int count = 0; // 🔒 only this widget// can touch this@overrideWidgetbuild(BuildContext context) {
returnText('$count');
}
}
With June — state lives anywhere, rebuilds anything
classCounterVMextendsJuneState{
int count = 0; // ✅ any widget,// any file,
} // anywhere
Each widget holds its own isolated state. Navigating away and back creates a fresh instance — exactly what you want for forms, sliders, and single-use UI.
Theme toggle
JuneBuilder can wrap MaterialApp itself, rebuilding the entire app on state change.
JuneBuilder(
() => UserController(),
builder: (c) {
if (c.loading) returnconstCircularProgressIndicator();
if (c.user == null) returnconstText('No user loaded');
returnText('Hello, ${c.user!.name}');
},
)
Selective rebuilds with id
Trigger only specific JuneBuilder widgets inside a shared state, leaving others untouched.
classFeedStateextendsJuneState{
String header = 'Latest';
List<Post> posts = [];
voidupdateHeader(String value) {
header = value;
setState('header'); // only rebuilds builders with id: 'header'
}
voidrefreshPosts(List<Post> fresh) {
posts = fresh;
setState('posts'); // only rebuilds builders with id: 'posts'
}
}
// Rebuilds only when 'header' setState is calledJuneBuilder(
() => FeedState(),
id: 'header',
builder: (s) => Text(s.header),
)
// Rebuilds only when 'posts' setState is calledJuneBuilder(
() => FeedState(),
id: 'posts',
builder: (s) => PostList(posts: s.posts),
)
Core API
Symbol
Role
JuneState
Base class for all state objects. Extend it, add fields and methods, call setState() to rebuild.
JuneBuilder
Widget that subscribes to a JuneState and rebuilds when setState() fires.
June.getState()
Retrieves (or lazily creates) a global singleton of a state type. Thread-safe, no setup needed.
tag
Namespaces multiple global instances of the same type.
id
Enables partial rebuilds — only JuneBuilder widgets sharing the same id will rebuild.
global: false
Bypasses the global registry. The builder uses your provided instance directly.
tag vs id — when to use which
tag
id
Lives on
JuneBuilder & June.getState()
JuneBuilder & setState()
Purpose
Pick which stored instance to use
Pick which builders to rebuild
Use when
Same state type needed in multiple places independently
One shared state, but only some widgets should react to a given change
Migration
0.8.x → 1.0.0
The factory function is now passed as a closure rather than an evaluated instance:
// Before (0.8.x)June.getState(CounterVM());
// After (1.0.0+)June.getState(() => CounterVM());
Community
Questions, ideas, or just want to say hi?
Acknowledgements
June was shaped by the best ideas in the Flutter ecosystem. Gratitude to the teams and communities behind Provider, GetX, Bloc, and Riverpod, and to Svelte for proving that less really is more.