๐Ÿก Popsicle โ€” Simple. Reactive. Composable

Popsicle is a lightweight, extensible state management and dependency injection (DI) framework for Flutter, built with simplicity and power in mind. Designed for developers who want full control without boilerplate, Popsicle unifies state, DI, and lifecycle management under one clean architecture.


๐Ÿš€ Features

  • โœ… Reactive & composable state management (ReactiveState, AsyncState, StreamState)
  • โœ… Built-in dependency injection without global variables
  • โœ… Zero-boilerplate DI with centralized configuration via AppDI class
  • โœ… Lifecycle-aware state observers & disposal
  • โœ… Middleware support for state transformation or interception
  • โœ… Supports Flutter & Dart CLI applications
  • โœ… Designed for both small and large-scale apps
  • โœ… Modular architecture for easy extensibility

๐Ÿ“ฆ Installation

Add popsicle to your pubspec.yaml:

dependencies:
  popsicle: ^1.0.0

Then run:

flutter pub get

๐Ÿง  Philosophy

Popsicle is inspired by the idea of:

f(State) => UI

We believe the UI should be a pure function of state โ€” with your logic encapsulated, testable, and clean.


๐Ÿ› ๏ธ Getting Started

1. Extend AppDI to register dependencies

class AppDI extends DIConfigurator {
  static final authService = container.reg<AuthService>(AuthServiceImpl());
  static final logger = container.regLazy(() => Logger());
}

2. Inject Popsicle in your app root (Optional ๐Ÿ™ƒ)

void main() {
  runApp(
    PopsicleDI(
      app: const MyApp(),
      inject: () => AppDI(),
    ),
  );
}

3. Use anywhere (without context!)

final auth = Popsicle.instance<AuthService>();
auth.login();

4. Create a reactive state

final counter = ReactiveState<int>(0);

counter.listen((value) {
  print("Counter updated: $value");
});

counter.value++; // Counter updated: 1

๐ŸŽฏ Lifecycle Management

Popsicle supports lifecycle-aware widgets and cleanup:

class MyState extends ReactiveState<int> with Disposable {
  MyState() {
    startTimer();
  }

  void startTimer() {
    Timer.periodic(Duration(seconds: 1), (_) => value++);
  }

  @override
  void dispose() {
    print("Cleaning up MyState");
    super.dispose();
  }
}

๐Ÿงช Testing

final testContainer = DIContainer();
testContainer.registerSingleton<MockService>(MockService());

final service = testContainer.resolve<MockService>();
expect(service.doSomething(), true);

๐Ÿงฉ Extensions (Planned)

  • โœ… Code generation for injectable services
  • โœ… Integration with Riverpod, Bloc
  • โœ… Dev tools inspector panel
  • โœ… Web dashboard for live state inspection

๐Ÿ“š API Overview

Feature Class/Method Description
DI Registration container.registerSingleton<T>() Register a singleton instance
Lazy Singleton container.registerFactory() Register a lazy-loaded singleton
Reactive State ReactiveState<T> State that emits changes
Async State AsyncState<T> Handle async loading / error / data
Stream State StreamState<T> Wrap a Dart stream as state
Global Access Popsicle.instance<T>() Access service globally, no context needed

๐Ÿ’ก Why Popsicle?

  • No black-box magic
  • Minimal boilerplate
  • Pure Dart core
  • Flutter-ready but framework-agnostic
  • Clean architecture friendly

๐Ÿ‘ฅ Community

Coming soon: Discord + GitHub Discussions


๐Ÿชช License

Apache License 2.0 ยฉ AR Rahman

This project is licensed under the Apache License 2.0.

License


๐Ÿ’ฌ Feedback

Have ideas or suggestions? Feel free to open an issue or pull request!

Libraries

popsicle