preferences_generator 2.0.0
preferences_generator: ^2.0.0 copied to clipboard
A powerful code generator that creates type-safe, boilerplate-free preference modules from simple abstract classes, used with `preferences_annotation`.
Preferences Generator #
A powerful, type-safe code generation solution for creating preference and settings classes in Dart & Flutter.
This package provides a clean, annotation-based API to eliminate boilerplate code for managing user
settings, allowing you to interact with shared_preferences, flutter_secure_storage, Hive, or
any other key-value store in a fully type-safe and conventional way.
Features #
- β Type-Safe: No more magic strings. Get compile-time safety for all your preference keys, types, and method calls.
- π§± Storage Agnostic: The adapter interface is incredibly simple and only deals with primitive types. The generator handles the rest.
- βοΈ Automatic Serialization: No more manual
jsonEncodeor type conversions in your adapter. The generator automatically handlesDateTime,Duration,Enum,Record, and even your custom classes. - π Highly Configurable: Use presets like
.dictionary(),.syncOnly(), or.reactive()to match your storage backend's API, or fine-tune every method name. - π Reactive Ready: Automatically generate
Streams for any preference to easily build reactive UIs. - π§ Global Configuration: Use
build.yamlto define project-wide conventions, likesnake_casekeys, for all your modules. - π― Rich Type Support: Out-of-the-box support for
int,String,double,bool,List,Set,Map,Enum,DateTime,Duration, andRecord.
Getting Started #
1. Installation #
Add the necessary dependencies to your pubspec.yaml.
dependencies:
# The annotation package
preferences_annotation: ^2.0.0
dev_dependencies:
# The generator
preferences_generator: ^2.0.0
# The build tool
build_runner: ^2.4.0
2. Define Your Preference Schema #
Create an abstract class that defines your preferences. The schema is now defined in a simple, private constructor.
lib/settings.dart
import 'package:preferences_annotation/preferences_annotation.dart';
import 'in_memory_adapter.dart'; // We will create this next
part 'settings.prefs.dart'; // Note the new .prefs.dart extension
enum AppTheme { light, dark, system }
// Use a preset to define the generated API.
// .dictionary() is great for key-value stores like shared_preferences.
@PrefsModule.dictionary()
abstract class AppSettings with _$AppSettings {
// A public factory connects to the generated implementation.
factory AppSettings(PrefsAdapter adapter) = _AppSettings;
// The private constructor defines the preference schema.
AppSettings._({
// Dart defaults provide the preference's default value.
String username = 'guest',
// Nullable types don't need a default.
DateTime? lastLogin,
// All supported types just work.
AppTheme theme = AppTheme.system,
// Use @PrefEntry for fine-grained control.
@PrefEntry(key: 'launch_counter')
int launchCount = 0,
});
}
3. Implement the PrefsAdapter #
The v2.0 PrefsAdapter is much simpler. It only needs to handle primitive types (String, int,
double, bool, List<String>). The generator takes care of converting complex types like
DateTime or Enum for you.
lib/in_memory_adapter.dart
import 'package:preferences_annotation/preferences_annotation.dart';
/// A simple, synchronous adapter that holds values in a map.
class InMemoryAdapter implements PrefsAdapter {
final Map<String, dynamic> _storage = {};
@override
Future<T?> get<T>(String key) async => _storage[key] as T?;
@override
Future<void> set<T>(String key, T value) async => _storage[key] = value;
@override
Future<void> remove(String key) async => _storage.remove(key);
@override
Future<void> removeAll() async => _storage.clear();
}
4. Run the Code Generator #
Run build_runner to generate the settings.prefs.dart part file.
dart run build_runner build --delete-conflicting-outputs
5. Use Your Preference Module #
Instantiate and use your fully type-safe AppSettings class. The method names are generated based
on the @PrefsModule preset you chose.
lib/main.dart
import 'settings.dart';
import 'in_memory_adapter.dart';
Future<void> main() async {
final adapter = InMemoryAdapter();
final settings = AppSettings(adapter);
// Getters are synchronous by default in the .dictionary() preset
print('Current theme: ${settings.getTheme().name}');
// Setters are asynchronous
await settings.setUsername('Alice');
print('New username: ${settings.getUsername()}');
await settings.setLastLogin(DateTime.now());
print('Last login: ${settings.getLastLogin()}');
// Removers are also asynchronous
await settings.removeLastLogin();
print('Last login after removal: ${settings.getLastLogin()}');
}
Advanced Configuration #
build.yaml Options #
You can define global settings in your project's build.yaml file.
# your_project/build.yaml
targets:
$default:
builders:
preferences_generator:preferences:
enabled: true
options:
# Sets the project-wide default key case.
# Options: asis, snake, camel, pascal, kebab
key_case: snake
# Optional: Override the default file extension.
build_extensions: .g.dart
@PrefsModule Configuration #
You can override global settings on a per-module basis.
// This module will use kebab-case, overriding the snake_case from build.yaml.
@PrefsModule.dictionary(keyCase: KeyCase.kebab)
abstract class ApiSettings with _$ApiSettings {
// ...
}
Migration Guide v1.x -> v2.0.0 #
Version 2.0.0 is a major release with significant API improvements. Follow these steps to migrate.
1. Update Dependencies #
In your pubspec.yaml, update the package versions:
dependencies:
preferences_annotation: ^2.0.0 # or latest
dev_dependencies:
preferences_generator: ^2.0.0 # or latest
2. Refactor PreferenceAdapter to PrefsAdapter #
The adapter interface has been simplified.
- Rename your class from
implements PreferenceAdaptertoimplements PrefsAdapter. - The
getandsetmethods now only need to handle primitives. Remove all manual serialization logic (likejsonEncode,DateTime.toIso8601String, etc.) from your adapter. - The
clear()method is nowremoveAll(). - The
containsKey()method has been removed from the interface.
Before (v1.x):
class OldAdapter implements PreferenceAdapter {
// ... contained complex logic for DateTime, JSON, etc.
}
After (v2.0):
class NewAdapter implements PrefsAdapter {
// ... only deals with primitives. No type checking needed!
}
3. Refactor Your Schema Class #
The schema is now defined in a private generative constructor.
Before (v1.x):
@PreferenceModule()
abstract class AppSettings with _$AppSettings {
factory AppSettings(PreferenceAdapter adapter, {
@PreferenceEntry(defaultValue: AppTheme.system)
AppTheme theme,
}) = _AppSettings;
}
After (v2.0):
- Rename
@PreferenceModuleto@PrefsModuleand choose a preset (e.g.,@PrefsModule.dictionary()). - Define a private generative constructor (
AppSettings._({...})) for the schema. - Move parameters from the factory to this new constructor.
- Replace
@PreferenceEntry(defaultValue: ...)with standard Dart default values (AppTheme theme = AppTheme.system).
@PrefsModule.dictionary()
abstract class AppSettings with _$AppSettings {
factory AppSettings(PrefsAdapter adapter) = _AppSettings;
AppSettings._({
AppTheme theme = AppTheme.system,
});
}
4. Update Usage & part Directives #
- The default generated file extension has changed from
.g.dartto.prefs.dart. You must update allpartdirectives in your schema files.- Before:
part 'settings.g.dart'; - After:
part 'settings.prefs.dart';
- Before:
- Generated method names have changed to be more explicit (e.g.,
theme->getTheme(),setTheme()). Review your generated.prefs.dartfile for the new method names corresponding to your chosen preset.
License #
This project is licensed under the MIT License.