๐ฏ Honeycomb
Concise, type-safe, codegen-free state management library for Flutter.
Honeycomb provides clear separation between State and Effect semantics, automatic dependency tracking, and a powerful Scope/Override mechanism.
โจ Features
- ๐ฏ Context-Free Usage โ Access state in pure Dart logic (Services/Repositories) via Global Container.
- โก Auto Dependency Tracking โ Computed automatically tracks dependencies from
watch. - ๐ก State vs Effect โ Clearly distinguish between replayable state and one-time events.
- ๐ญ Scope/Override โ Flexible dependency injection and local overrides.
- ๐ No Codegen โ Pure Dart, no build_runner required.
- ๐ Type Safe โ Full generic support.
- ๐งช Easy to Test โ Decouple state logic from UI for easy testing.
๐ฆ Installation
dependencies:
honeycomb: ^1.0.0
flutter pub get
๐ Quick Start
1. Define State
import 'package:aegis_honeycomb/honeycomb.dart';
// Read-write state
final counterState = StateRef(0);
// Derived state (auto dependency tracking)
final doubledCounter = Computed((watch) => watch(counterState) * 2);
// Async state
final userProfile = Computed.async((watch) async {
final userId = watch(currentUserId);
return await api.fetchUser(userId);
});
// One-time events
final toastEffect = Effect<String>();
2. Provide Container
// You can keep a global container if you don't want to rely on BuildContext.
final appContainer = HoneycombContainer();
void main() {
runApp(
HoneycombScope(
container: appContainer,
child: MyApp(),
),
);
}
3. Use in UI
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return HoneycombConsumer(
builder: (context, ref, child) {
final count = ref.watch(counterState);
final doubled = ref.watch(doubledCounter);
return Column(
children: [
Text('Count: $count'),
Text('Doubled: $doubled'),
ElevatedButton(
onPressed: () {
final container = HoneycombScope.readOf(context);
container.write(counterState, count + 1);
},
child: Text('Increment'),
),
],
);
},
);
}
}
๐ Documentation
| Document | Description |
|---|---|
| Getting Started | Learn Honeycomb from scratch |
| Core Concepts | Deep dive into design philosophy |
| API Reference | Full API documentation |
| Best Practices | Recommended usage patterns |
| Comparison | Comparison with Provider/Riverpod/Bloc |
| FAQ | Frequently Asked Questions |
๐ฏ Core Concepts at a Glance
State vs Effect
// State: Replayable, always returns the latest value
final userName = StateRef('Guest');
// Effect: One-time event, no historical storage
final showToast = Effect<String>(strategy: EffectStrategy.drop);
Dependency Tracking
final fullName = Computed((watch) {
// Automatically tracks firstName and lastName
return '${watch(firstName)} ${watch(lastName)}';
});
// fullName recalculates whenever firstName or lastName changes
Scope Override
HoneycombScope supports overriding state values in a subtree using the overrides parameter. This is extremely useful for testing (Mocking) or parameterizing child components.
How it works: When resolving an Atom, the container first checks if it's in overrides; if not, it looks up the parent container; finally, it creates a new node based on the default logic.
// Locally override state (e.g., for testing or theme switching)
HoneycombScope(
overrides: [
// Force themeState to be dark
themeState.overrideWith(ThemeData.dark()),
// Or override an async state with mock data
userProfile.overrideWith(AsyncValue.data(MockUser())),
],
child: DarkModePage(),
)
Using in Business Logic (Outside Context)
Sometimes you need to access state in Repositories, Services, or pure Dart logic.
1. Create a Global Container (e.g. in app_globals.dart)
// Global singleton container
final appContainer = HoneycombContainer();
2. Use directly in Services
class AuthService {
void logout() {
// Read state
final currentUser = appContainer.read(userState);
// Write state
appContainer.write(userState, null);
// Emit event
appContainer.emit(navigationEffect, '/login');
}
}
3. Inject into UI Tree
void main() {
runApp(
HoneycombScope(
container: appContainer, // Must inject the same instance for UI updates
child: MyApp(),
),
);
}
๐งช Testing
test('counter increments', () {
final container = HoneycombContainer();
expect(container.read(counterState), 0);
container.write(counterState, 1);
expect(container.read(counterState), 1);
expect(container.read(doubledCounter), 2);
});
๐ Comparison
| Feature | Honeycomb | Provider | Riverpod | Bloc |
|---|---|---|---|---|
| No Codegen | โ | โ | โ | โ |
| Auto Tracking | โ | โ | โ | โ |
| State/Effect Separation | โ | โ | โ | โ |
| Scope Override | โ | โ | โ | โ |
| Batch Updates | โ | โ | โ | โ |
| Learning Curve | Low | Low | Medium | High |
๐ค Contributing
Contributions are welcome! Please check CONTRIBUTING.md.
๐ License
MIT License - See the LICENSE file.