Sergey Penkovsky f1cf1d054f Merge pull request #22 from pese-git/develop
# Release - CherryPick 3.x

> **CherryPick** — a lightweight and modular DI framework for Dart and Flutter that solves dependency injection through strong typing, code generation, and dependency control.

Version **3.x** was recently released with significant improvements.

## Main Changes in 3.x

* **O(1) dependency resolution** — thanks to Map indexing of bindings, performance does not depend on the size of the scope in the DI graph. This provides noticeable speedup in large projects.
* **Protection against circular dependencies** — checking works both within a single scope and across the entire hierarchy. When a cycle is detected, an informative exception with the dependency chain is thrown.
* **Integration with Talker** — all DI events (registration, creation, deletion, errors) are logged and can be displayed in the console or UI.
* **Automatic resource cleanup** — objects implementing `Disposable` are properly released when the scope is closed.
* **Stabilized declarative approach support** — annotations and code generation now work more reliably and are more convenient for use in projects.

## Resource Cleanup Example

```dart
class MyServiceWithSocket implements Disposable {
  @override
  Future<void> dispose() async {
    await socket.close();
    print('Socket closed!');
  }
}

class AppModule extends Module {
  @override
  void builder(Scope currentScope) {
    // singleton Api
    bind<MyServiceWithSocket>()
      .toProvide(() => MyServiceWithSocket())
      .singleton();
  }
}

scope.installModules([AppModule()]);

await CherryPick.closeRootScope(); // will wait for async dispose to complete
```

## Circular Dependency Checking

One of the new features in CherryPick 3.x is built-in cycle protection.
This helps catch situations early where services start depending on each other recursively.

### How to Enable Checking

For checking within a single scope:

```dart
final scope = CherryPick.openRootScope();
scope.enableCycleDetection();
```

For global checking across the entire hierarchy:

```dart
CherryPick.enableGlobalCycleDetection();
CherryPick.enableGlobalCrossScopeCycleDetection();
final rootScope = CherryPick.openRootScope();
```

### How a Cycle Can Occur

Suppose we have two services that depend on each other:

```dart
class UserService {
  final OrderService orderService;
  UserService(this.orderService);
}

class OrderService {
  final UserService userService;
  OrderService(this.userService);
}
```

If we register them in the same scope:

```dart
class AppModule extends Module {
  @override
  void builder(Scope currentScope) {
    bind<UserService>().toProvide(() => UserService(scope.resolve()));
    bind<OrderService>().toProvide(() => OrderService(scope.resolve()));
  }
}

final scope = CherryPick.openRootScope()
  ..enableCycleDetection()
  ..installModules([AppModule()]);

scope.resolve<UserService>();
```

Then when trying to resolve the dependency, an exception will be thrown:

```bash
 Circular dependency detected for UserService
Dependency chain: UserService -> OrderService -> UserService
```

This way, the error is detected immediately, not "somewhere in runtime".

## Integration with Talker

CherryPick 3.x allows logging all DI events through [Talker](https://pub.dev/packages/talker): registration, object creation, deletion, and errors. This is convenient for debugging and diagnosing the dependency graph.

Connection example:

```dart
final talker = Talker();
final observer = TalkerCherryPickObserver(talker);
CherryPick.setGlobalObserver(observer);
```

After this, DI events will be displayed in the console or UI:

```bash
┌───────────────────────────────────────────────────────────────
│ [info]    9:41:33  | [scope opened][CherryPick] scope_1757054493089_7072
└───────────────────────────────────────────────────────────────
┌───────────────────────────────────────────────────────────────
│ [verbose] 9:41:33  | [diagnostic][CherryPick] Scope created: scope_1757054493089_7072 {type: Scope, name: scope_1757054493089_7072, description: scope created}
└───────────────────────────────────────────────────────────────
```

In the log, you can see when scopes are created, which objects are registered and deleted, and catch errors and cycles in real time.


## Declarative Approach with Annotations

In addition to fully programmatic module descriptions, CherryPick supports **declarative DI style through annotations**.  
This allows minimizing manual code and automatically generating modules and mixins for automatic dependency injection.

Example of a declarative module:

```dart
@module()
abstract class AppModule extends Module {
  @provide()
  @singleton()
  Api api() => Api();

  @provide()
  Repo repo(Api api) => Repo(api);
}
````

After code generation, you can automatically inject dependencies into widgets or services:

```dart
@injectable()
class MyScreen extends StatelessWidget with _$MyScreen {
  @inject()
  late final Repo repo;

  MyScreen() {
    _inject(this);
  }
}
```

This way you can choose a convenient style: either **purely programmatic** or **declarative with annotations**.


## Who Might Find CherryPick Useful?

* Projects where it's important to guarantee **no cycles in the dependency graph**;
* Teams that want to **minimize manual DI code** and use a declarative style with annotations;
* Applications that require **automatic resource cleanup** (sockets, controllers, streams).

## Useful Links

* 📦 Package: [pub.dev/packages/cherrypick](https://pub.dev/packages/cherrypick)
* 💻 Code: [github.com/pese-git/cherrypick](https://github.com/pese-git/cherrypick)
* 📖 Documentation: [cherrypick-di.netlify.app](https://cherrypick-di.netlify.app/)
2025-09-08 17:04:48 +03:00
2025-05-18 16:41:48 +03:00
2025-09-05 09:37:24 +03:00
2025-08-12 00:29:57 +03:00
2025-09-08 14:58:37 +03:00
2025-08-08 23:24:05 +03:00
2025-08-12 00:29:57 +03:00
2025-05-19 16:11:41 +03:00
2025-09-08 15:40:09 +03:00

Melos + FVM CI


CherryPick Workspace

CherryPick Workspace is a modular, open-source dependency injection ecosystem for Dart and Flutter, designed to offer lightweight, flexible, and scalable DI suitable for both backend and frontend (Flutter) development. This monorepo contains the main DI runtime library, annotation helpers, code generation for modular bindings, and seamless Flutter integration.


Packages Overview

  • cherrypick
    The core dependency injection library. Supports modular bindings, hierarchical scopes, named and singleton bindings, provider functions (sync/async), runtime parameters, and test-friendly composition.
    Intended for use in pure Dart and Flutter projects.

  • cherrypick_annotations
    A set of Dart annotations (@module, @singleton, @instance, @provide, @named, @params) enabling concise, declarative DI modules and providers, primarily for use with code generation tools.

  • cherrypick_generator
    A source_gen-based code generator that automatically converts your annotated modules and providers into ready-to-use boilerplate for registration and resolution within your app.
    Reduces manual wiring and errors; compatible with build_runner.

  • cherrypick_flutter
    Adds Flutter-native integration, exposing DI scopes and modules to the widget tree through CherryPickProvider and enabling dependency management throughout your Flutter app.


Why CherryPick?

  • Zero-overhead and intuitive API:
    Clean, minimal syntax, strong typing, powerful binding lifecycle control.
  • High testability:
    Supports overriding and hierarchical scope trees.
  • Both Sync & Async support:
    Register and resolve async providers, factories, and dependencies.
  • Seamless code generation:
    Effortless setup with annotations + generator—skip boilerplate!
  • Works with or without Flutter.
  • Production ready:
    Robust enough for apps, packages, and server-side Dart.
  • Extensible & Modular:
    Add bindings at runtime, use sub-modules, or integrate via codegen.

Get Started

1. Add dependencies

In your pubspec.yaml:

dependencies:
  cherrypick: ^<latest-version>
  cherrypick_annotations: ^<latest-version>

dev_dependencies:
  build_runner: ^<latest>
  cherrypick_generator: ^<latest-version>

For Flutter projects, add:

dependencies:
  cherrypick_flutter: ^<latest-version>

2. Write a DI Module (with annotations)

import 'package:cherrypick_annotations/cherrypick_annotations.dart';
import 'package:cherrypick/cherrypick.dart';

@module()
abstract class MyModule extends Module {
  @singleton()
  ApiClient apiClient() => ApiClient();

  @provide()
  DataRepository dataRepo(ApiClient client) => DataRepository(client);

  @provide()
  String greeting(@params() String name) => 'Hello, $name!';
}

3. Generate the bindings

dart run build_runner build
# or for Flutter:
flutter pub run build_runner build

The generator will create a $MyModule class with binding code.

4. Install and Resolve

final scope = CherryPick.openRootScope()
  ..installModules([$MyModule()]);

final repo = scope.resolve<DataRepository>();
final greeting = scope.resolve<String>(params: 'John'); // 'Hello, John!'

For Flutter, wrap your app with CherryPickProvider for DI scopes in the widget tree:

void main() {
  runApp(
    CherryPickProvider(child: MyApp()),
  );
}

Features at a Glance

  • Fast, lightweight DI for any Dart/Flutter project
  • 🧩 Modular & hierarchical scopes (root, subscopes)
  • 🔖 Named/bound/singleton instances out of the box
  • 🔄 Sync and async provider support
  • ✏️ Runtime parameters for dynamic factory methods
  • 🏷️ Code generator for annotation-based DI setup (cherrypick_generator)
  • 🕹️ Deep Flutter integration via CherryPickProvider

Example Usage

Please see:


Contribution & License

  • Contributions: PRs, issues, and feedback are welcome on GitHub.
  • License: Apache 2.0 for all packages in this workspace.

Happy Cherry Picking! 🍒

Description
CherryPick is a modular, open-source dependency injection ecosystem for Dart and Flutter
Readme Apache-2.0 2 MiB
Languages
Dart 96.2%
TypeScript 2.1%
Python 1.3%
CSS 0.4%