An open API service indexing awesome lists of open source software.

https://github.com/uproid/instancer

A simple, lightweight factory registry for Dart that makes creating instances of registered types easy and flexible.
https://github.com/uproid/instancer

dart finch flutter instance pub

Last synced: 2 months ago
JSON representation

A simple, lightweight factory registry for Dart that makes creating instances of registered types easy and flexible.

Awesome Lists containing this project

README

          

# Instancer

[![pub package](https://img.shields.io/pub/v/instancer.svg)](https://pub.dev/packages/instancer)
[![Donate](https://img.shields.io/badge/Donate-Support-green.svg)](https://buymeacoffee.com/uproid)
[![issues-closed](https://img.shields.io/github/issues-closed/uproid/instancer?color=green)](https://github.com/uproid/instancer/issues?q=is%3Aissue+is%3Aclosed)
[![issues-open](https://img.shields.io/github/issues-raw/uproid/instancer)](https://github.com/uproid/instancer/issues)
[![Contributions](https://img.shields.io/github/contributors/uproid/instancer)](https://github.com/uproid/instancer/blob/main/CONTRIBUTING.md)

A simple, lightweight factory registry for Dart that makes creating instances of registered types easy and flexible.

Perfect for dependency injection, prototyping, configuration management, and testing!

## Features

โœจ **Simple API**: Just 5 intuitive methods to learn
๐ŸŽฏ **Type-safe**: Full generic type support
๐Ÿชถ **Lightweight**: Zero dependencies
๐Ÿ”ง **Flexible**: Register any factory function
๐Ÿงช **Testable**: Easy to clear and reset for testing

## Getting started

Add this to your `pubspec.yaml`:

```yaml
dependencies:
instancer: ^1.0.0
```

Then import it:

```dart
import 'package:instancer/instancer.dart';
```

## Usage

### ๐Ÿš€ Quick Start

```dart
import 'package:instancer/instancer.dart';

class User {
final String name;
final int age;

User({required this.name, required this.age});
}

void main() {
// 1๏ธโƒฃ Register a factory function
Instancer.register(() => User(name: 'Guest', age: 18));

// 2๏ธโƒฃ Create instances anywhere in your code
final user1 = Instancer.create();
final user2 = Instancer.create();

print(user1.name); // Output: Guest
print(identical(user1, user2)); // Output: false (different instances)
}
```

### ๐Ÿ“ฆ Real-World Examples

#### Example 1: Configuration Management

```dart
class AppConfig {
final String apiUrl;
final bool debugMode;
final int timeout;

AppConfig({
required this.apiUrl,
required this.debugMode,
required this.timeout,
});
}

void main() {
// Development environment
Instancer.register(
() => AppConfig(
apiUrl: 'http://localhost:3000',
debugMode: true,
timeout: 30,
),
);

// Use it anywhere in your app
final config = Instancer.create();
print(config.apiUrl); // http://localhost:3000

// Switch to production (just re-register)
Instancer.register(
() => AppConfig(
apiUrl: 'https://api.production.com',
debugMode: false,
timeout: 10,
),
);

final prodConfig = Instancer.create();
print(prodConfig.apiUrl); // https://api.production.com
}
```

#### Example 2: Dependency Injection

```dart
class Database {
void query(String sql) => print('Executing: $sql');
}

class UserRepository {
final Database database;

UserRepository({required this.database});

void save(String name) {
database.query("INSERT INTO users (name) VALUES ('$name')");
}
}

class UserService {
final UserRepository repository;

UserService({required this.repository});

void createUser(String name) {
print('Creating user: $name');
repository.save(name);
}
}

void main() {
// Register dependencies in order
Instancer.register(() => Database());

Instancer.register(
() => UserRepository(database: Instancer.create()),
);

Instancer.register(
() => UserService(repository: Instancer.create()),
);

// Use the service
final userService = Instancer.create();
userService.createUser('Alice');
// Output:
// Creating user: Alice
// Executing: INSERT INTO users (name) VALUES ('Alice')
}
```

#### Example 3: Testing with Mocks

```dart
abstract class ApiClient {
Future fetchData();
}

class RealApiClient implements ApiClient {
@override
Future fetchData() async {
// Real network call
return 'Real data from server';
}
}

class MockApiClient implements ApiClient {
@override
Future fetchData() async {
// Mock data for testing
return 'Mock data';
}
}

void main() {
// Production code
Instancer.register(() => RealApiClient());

// In your tests, just re-register with mock
Instancer.register(() => MockApiClient());

final client = Instancer.create();
client.fetchData().then(print); // Output: Mock data

// Clean up after tests
Instancer.clear();
}
```

#### Example 4: Prototype Pattern

```dart
class EmailTemplate {
final String subject;
final String body;
final List recipients;

EmailTemplate({
required this.subject,
required this.body,
this.recipients = const [],
});

@override
String toString() => 'Email: $subject to ${recipients.length} recipients';
}

void main() {
// Register a prototype template
Instancer.register(
() => EmailTemplate(
subject: 'Welcome!',
body: 'Welcome to our service',
recipients: ['user@example.com'],
),
);

// Create multiple emails from the same template
final email1 = Instancer.create();
final email2 = Instancer.create();

print(email1); // Email: Welcome! to 1 recipients
print(email2); // Email: Welcome! to 1 recipients
print('Same instance? ${identical(email1, email2)}'); // false
}
```

#### Example 5: Factory with State

```dart
class Logger {
static int _instanceCount = 0;
final int id;

Logger() : id = ++_instanceCount;

void log(String message) {
print('[Logger $id] $message');
}
}

void main() {
// Each creation increases the counter
Instancer.register(() => Logger());

final logger1 = Instancer.create();
final logger2 = Instancer.create();
final logger3 = Instancer.create();

logger1.log('First message'); // [Logger 1] First message
logger2.log('Second message'); // [Logger 2] Second message
logger3.log('Third message'); // [Logger 3] Third message
}
```

## API Reference

### `register(T Function() factory)`

Registers a factory function for type `T`.

```dart
Instancer.register(() => User(name: 'John', age: 25));
```

**Parameters:**
- `factory`: A function that returns a new instance of type `T`

### `create()`

Creates a new instance of type `T` using the registered factory.

```dart
final user = Instancer.create();
```

**Returns:** A new instance of type `T`

**Throws:** `StateError` if no factory is registered for type `T`

### `isRegistered()`

Checks if a factory is registered for type `T`.

```dart
if (Instancer.isRegistered()) {
print('User factory is registered!');
}
```

**Returns:** `true` if registered, `false` otherwise

### `unregister()`

Removes the factory for type `T`.

```dart
final removed = Instancer.unregister();
print('Factory removed: $removed');
```

**Returns:** `true` if a factory was removed, `false` if none existed

### `clear()`

Removes all registered factories. Useful for testing or resetting state.

```dart
Instancer.clear();
```

### `count`

Returns the number of registered factories.

```dart
print('Total registered factories: ${Instancer.count}');
```

## Why Instancer?

| Feature | Instancer | Traditional Singleton | Factory Pattern |
|---------|-----------|----------------------|-----------------|
| No inheritance required | โœ… | โŒ | โœ… |
| No code generation | โœ… | โœ… | โœ… |
| Multiple instances | โœ… | โŒ | โœ… |
| Easy to test | โœ… | โŒ | โœ… |
| Dynamic registration | โœ… | โŒ | โŒ |
| Zero dependencies | โœ… | โœ… | โœ… |

### Benefits:

- **No inheritance required**: Your classes don't need to extend anything
- **No code generation**: Works without `build_runner` or code generation
- **Pure Dart**: No platform-specific code, works everywhere (Flutter, CLI, Web)
- **Flexible**: Register simple constructors or complex initialization logic
- **Testable**: Easy to swap implementations for mocking in tests
- **Simple**: Only 5 methods to learn - `register`, `create`, `isRegistered`, `unregister`, `clear`

## Common Use Cases

โœ… **Dependency Injection** - Register and inject dependencies
โœ… **Configuration Management** - Switch between dev/prod configs
โœ… **Testing** - Replace real implementations with mocks
โœ… **Prototype Pattern** - Create multiple instances from templates
โœ… **Factory Pattern** - Centralized instance creation
โœ… **Plugin Systems** - Register and create plugin instances

## Tips & Best Practices

### 1. Register early, use anywhere

```dart
// In your app initialization
void setupDependencies() {
Instancer.register(() => SqliteDatabase());
Instancer.register(() => HttpApiClient());
Instancer.register(() => AuthService());
}

// Use anywhere in your app
final auth = Instancer.create();
```

### 2. Use for environment-specific configurations

```dart
void setupConfig(Environment env) {
if (env == Environment.dev) {
Instancer.register(() => DevConfig());
} else {
Instancer.register(() => ProdConfig());
}
}
```

### 3. Clean up in tests

```dart
void main() {
setUp(() {
Instancer.register(() => MockService());
});

tearDown(() {
Instancer.clear(); // Always clean up!
});

test('service works', () {
final service = Instancer.create();
// Test...
});
}
```

## Contributing

Contributions are welcome! Please read our [CONTRIBUTING.md](CONTRIBUTING.md) for details.

1. Check existing [issues](https://github.com/uproid/instancer/issues)
2. Create a new issue or submit a pull request
3. Follow the existing code style

## License

MIT License - see the [LICENSE](LICENSE) file for details.

## Author

Created with โค๏ธ by [Uproid](https://github.com/uproid)

---

**Like this package?** Give it a โญ on [GitHub](https://github.com/uproid/instancer)!