preferences_generator 3.0.0
preferences_generator: ^3.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 build_runner code generator that turns a single annotated class into a
fully type-safe, storage-agnostic preferences API — with automatic
serialization, reactive streams, and ChangeNotifier integration.
┌─ Your schema (1 file) ─────────────────────────────────────────┐
│ @PrefsModule.reactive() │
│ abstract class AppSettings with _$AppSettings, ChangeNotifier │
│ AppSettings._({ String username = 'guest', ... }); │
└───────────────────────────────────────────────────────────────-┘
↓ build_runner
┌─ Generated (1 part file) ──────────────────────────────────────┐
│ • _AppSettingsKeys — compile-time storage key constants │
│ • mixin _$AppSettings — all methods, streams, load/refresh │
│ • class _AppSettings — concrete implementation + state │
└────────────────────────────────────────────────────────────────┘
Table of Contents #
- Installation
- Quick Start
- Presets — choosing the right API shape
- Method Name Templates
- All
@PrefsModuleParameters - All
@PrefEntryParameters - All Supported Types
- Custom Serialization
- Key Casing
- Reactive UI — Streams and ChangeNotifier
- Error Handling for Sync Writes
- Lifecycle —
_load,refresh,removeAll - Implementing
PrefsAdapter - Global
build.yamlOptions - Known Limitations
- AI Agents & LLM Compatibility
Installation #
# pubspec.yaml
dependencies:
preferences_annotation: ^3.0.0
dev_dependencies:
preferences_generator: ^3.0.0
build_runner: ^2.6.0
Quick Start #
1. Define your schema #
// lib/settings.dart
import 'package:preferences_annotation/preferences_annotation.dart';
part 'settings.prefs.dart'; // ← the generated file
enum AppTheme { light, dark, system }
@PrefsModule.dictionary()
abstract class AppSettings with _$AppSettings {
factory AppSettings(PrefsAdapter adapter) = _AppSettings;
// Every named parameter = one preference.
// The default value is both the in-memory initial state and the
// value restored when remove() is called.
AppSettings._({
String username = 'guest',
bool isDarkMode = false,
AppTheme theme = AppTheme.system,
@PrefEntry(key: 'launch_counter')
int launchCount = 0,
});
}
2. Implement PrefsAdapter (once per storage backend) #
class SharedPreferencesAdapter implements PrefsAdapter {
final SharedPreferences _prefs;
SharedPreferencesAdapter(this._prefs);
@override Future<T?> get<T>(String key) async => _prefs.get(key) as T?;
@override Future<void> remove(String key) => _prefs.remove(key);
@override Future<void> removeAll() => _prefs.clear();
@override
Future<void> set<T>(String key, T value) async {
if (value is String) await _prefs.setString(key, value);
else if (value is bool) await _prefs.setBool(key, value);
else if (value is int) await _prefs.setInt(key, value);
else if (value is double) await _prefs.setDouble(key, value);
else if (value is List<String>) await _prefs.setStringList(key, value);
}
}
3. Generate #
dart run build_runner build --delete-conflicting-outputs
4. Use #
final prefs = await SharedPreferences.getInstance();
final settings = AppSettings(SharedPreferencesAdapter(prefs));
// .dictionary() generates: sync getter, async setter, async remover
print(settings.getUsername()); // 'guest'
await settings.setUsername('Alice');
print(settings.getUsername()); // 'Alice'
await settings.removeUsername();
print(settings.getUsername()); // 'guest' (default restored)
Presets #
A preset is a named constructor on @PrefsModule that pre-fills all method
name templates for a common storage pattern. Every preset parameter can be
overridden individually.
Preset comparison #
| Preset | Primary use case | Generated methods for username |
|---|---|---|
.dictionary() |
shared_preferences (recommended default) |
getUsername() sync • setUsername(v) async • removeUsername() async |
.reactive() |
Flutter reactive UIs with ChangeNotifier |
username getter • setUsername(v) sync • usernameStream stream |
.syncOnly() |
Hive, GetStorage, in-memory maps | getUsername() • putUsername(v) • deleteUsername() — all sync |
.syncFirst() |
Mixed-latency — prefer sync, async available | username • setUsername(v) • usernameAsync() • setUsernameAsync(v) |
.minimal() |
CLI tools, scripts | username • setUsername(v) • removeUsername() — nothing else |
.exhaustive() |
Every variant — useful for demos and testing | getUsernameSync • setUsernameSync(v) • getUsernameAsync() • setUsernameAsync(v) • watchUsernameStream |
.readOnly() |
Config loaded externally, no writes | username getter • usernameAsync() |
.disabled() |
Custom starting point — everything off | (nothing — add only what you need) |
Preset examples #
// Flutter app with shared_preferences
@PrefsModule.dictionary()
abstract class AppSettings with _$AppSettings { ... }
// Flutter reactive UI — streams + ChangeNotifier
@PrefsModule.reactive()
abstract class AppSettings with _$AppSettings, ChangeNotifier { ... }
// CLI tool — minimal surface
@PrefsModule.minimal()
abstract class CliConfig with _$CliConfig { ... }
// Secure storage with snake_case keys
@PrefsModule(keyCase: KeyCase.snake)
abstract class SecureSettings with _$SecureSettings, ChangeNotifier { ... }
// Custom — disabled base, opt-in only what you want
@PrefsModule.disabled(
getter: '{{name}}',
setter: 'update{{Name}}',
removeAll: 'wipeAll',
)
abstract class MySettings with _$MySettings { ... }
Method Name Templates #
All per-entry method configuration parameters accept a String? template
with two substitution tokens.
Token reference #
| Token | Substitution | isFirstLaunch example |
|---|---|---|
{{name}} |
Raw camelCase field name | isFirstLaunch |
{{Name}} |
Field name, first letter capitalised | IsFirstLaunch |
Token examples #
| Template | Field: username |
Field: isDarkMode |
|---|---|---|
'{{name}}' |
username |
isDarkMode |
'get{{Name}}' |
getUsername |
getIsDarkMode |
'set{{Name}}' |
setUsername |
setIsDarkMode |
'remove{{Name}}' |
removeUsername |
removeIsDarkMode |
'{{name}}Async' |
usernameAsync |
isDarkModeAsync |
'set{{Name}}Async' |
setUsernameAsync |
setIsDarkModeAsync |
'{{name}}Stream' |
usernameStream |
isDarkModeStream |
'watch{{Name}}Stream' |
watchUsernameStream |
watchIsDarkModeStream |
'on{{Name}}Changed' |
onUsernameChanged |
onIsDarkModeChanged |
'toggle{{Name}}' |
toggleUsername |
toggleIsDarkMode |
Null vs disabled #
nullon@PrefsModule— disables that method type for the entire module.nullon@PrefEntry— inherits from the module template (default behaviour).PrefEntry.disabledon@PrefEntry— force-disables for this entry only.
All @PrefsModule Parameters #
@PrefsModule(
// ── Behaviour ───────────────────────────────────────────────────────────
notifiable: true, // call notifyListeners() on any change
// only meaningful when mixing in ChangeNotifier
keyCase: KeyCase.snake, // storage key casing (see Key Casing section)
onWriteError: MyClass._handleError,
// Optional: void Function(Object error, StackTrace st)
// Called when a sync (fire-and-forget) write fails.
// Without this, write failures are silently discarded.
// ── Per-entry method templates (null = disabled for whole module) ────────
getter: '{{name}}', // sync getter
setter: 'set{{Name}}', // sync setter
remover: 'remove{{Name}}', // sync remover
asyncGetter: '{{name}}Async', // async getter
asyncSetter: 'set{{Name}}Async', // async setter
asyncRemover:'remove{{Name}}Async',// async remover
streamer: '{{name}}Stream', // stream getter (null by default in base)
// ── Module-level method names (literal strings, no token substitution) ──
removeAll: 'removeAll', // clears all storage, reloads defaults
refresh: 'refresh', // re-reads all values from storage
)
All @PrefEntry Parameters #
@PrefEntry(
// ── Storage ─────────────────────────────────────────────────────────────
key: 'my_custom_key',
// Overrides the storage key for this field only.
// Precedence: @PrefEntry(key:) > @PrefsModule(keyCase:) > build.yaml
// ── Method name templates (null = inherit module, PrefEntry.disabled = off)
getter: '{{name}}',
setter: 'set{{Name}}',
remover: 'remove{{Name}}',
asyncGetter: '{{name}}Async',
asyncSetter: 'set{{Name}}Async',
asyncRemover:'remove{{Name}}Async',
streamer: '{{name}}Stream',
// ── Shortcuts ────────────────────────────────────────────────────────────
readOnly: true,
// Disables setter, asyncSetter, remover, asyncRemover for this field.
// Streams are NOT affected — the field still gets a stream if enabled.
notifiable: false,
// Overrides whether changes to this field call notifyListeners().
// null → inherits the module-level setting.
// ── Non-constant default ─────────────────────────────────────────────────
initial: MyClass._getInitialValue,
// Use when the default cannot be a compile-time constant.
// Signature: TypeEntry Function()
// Example: DateTime? creationDate needs initial: () => DateTime.now()
// ── Custom serialization (choose one approach) ───────────────────────────
converter: const MyConverter(),
// A const PrefConverter<TypeEntry, TypeStorage> instance.
toStorage: MyClass._toStorage,
fromStorage: MyClass._fromStorage,
// Inline static functions. Must be used together.
// toStorage: TypeStorage Function(TypeEntry value)
// fromStorage: TypeEntry Function(TypeStorage value)
)
@PrefEntry usage patterns #
AppSettings._({
// 1. Custom storage key
@PrefEntry(key: 'launch_counter')
int launchCount = 0,
// 2. Custom stream name
@PrefEntry(streamer: 'watch{{Name}}Changes')
String? authToken,
// 3. Custom setter name (common for booleans)
@PrefEntry(setter: 'toggle{{Name}}', asyncSetter: 'toggle{{Name}}Async')
bool isDarkMode = false,
// 4. Disable one specific method for this entry only
@PrefEntry(asyncSetter: PrefEntry.disabled)
int readFrequentlyWriteRarely = 0,
// 5. Make a field read-only (no setter or remover generated)
@PrefEntry(readOnly: true)
String installId = 'uuid-1234-abcd',
// 6. Suppress ChangeNotifier for a frequently-changing field
@PrefEntry(notifiable: false)
String temporarySessionId = '',
// 7. Non-constant default value
@PrefEntry(initial: AppSettings._getCreationDate)
DateTime? creationDate,
// 8. Custom type via PrefConverter
@PrefEntry(converter: ColorConverter())
Color? accentColor,
// 9. Custom type via inline functions
@PrefEntry(toStorage: AppSettings._sessionToJson,
fromStorage: AppSettings._sessionFromJson)
ApiSession? session,
});
All Supported Types #
The generator automatically serializes these types to and from the adapter's primitive contract — no manual conversion needed.
Natively supported (no annotation required) #
| Dart type | Storage type | Notes |
|---|---|---|
int |
int |
|
double |
double |
|
bool |
bool |
|
String |
String |
|
List<String> |
List<String> |
Direct storage |
List<int> |
String |
JSON-encoded |
Set<int> |
String |
JSON-encoded |
Set<String> |
String |
JSON-encoded |
Map<String, String> |
Map<String, dynamic> |
|
Map<String, dynamic> |
Map<String, dynamic> |
|
Enum |
String |
Stored as .name, restored with .values.byName() |
DateTime |
String |
Stored as ISO 8601 via .toIso8601String() |
Duration |
int |
Stored as .inMicroseconds |
Named record ({int w, int h}) |
Map<String, dynamic> |
Field names preserved |
Positional record (int, String) |
Map<String, dynamic> |
Keys: f0, f1, … |
Nullable variants T? |
same as T |
null returned when key absent |
Custom types (annotation required) #
// Option A: PrefConverter — reusable, recommended
class UriConverter extends PrefConverter<Uri, String> {
const UriConverter();
@override Uri fromStorage(String v) => Uri.parse(v);
@override String toStorage(Uri v) => v.toString();
}
@PrefEntry(converter: UriConverter())
Uri? apiEndpoint,
// Option B: Inline static functions — one-off types
@PrefEntry(
toStorage: AppSettings._userToMap,
fromStorage: AppSettings._userFromMap,
)
User? currentUser,
static Map<String, dynamic> _userToMap(User u) => u.toJson();
static User _userFromMap(Map<String, dynamic> m) => User.fromJson(m);
Custom Serialization #
Complete PrefConverter example #
import 'package:preferences_annotation/preferences_annotation.dart';
/// Serializes a [Color] to/from its ARGB integer representation.
class ColorConverter extends PrefConverter<Color, int> {
const ColorConverter(); // must be const
@override
Color fromStorage(int value) => Color(value);
@override
int toStorage(Color value) => value.toARGB32();
}
// Usage in schema:
@PrefEntry(converter: ColorConverter())
Color? accentColor,
PrefConverter type parameter guide #
PrefConverter<TypeEntry, TypeStorage>
───────── ────────────
Your type What the adapter stores
e.g. Color e.g. int
TypeStorage must be one of the natively supported types above.
Key Casing #
Storage key casing is resolved in this priority order (highest first):
@PrefEntry(key: 'my_key') ← always wins
↓
@PrefsModule(keyCase: KeyCase.snake) ← overrides build.yaml
↓
build.yaml: options: key_case: snake ← project-wide default
↓
KeyCase.asis ← field name unchanged (fallback)
Available KeyCase values #
| Value | launchCount → |
isDarkMode → |
|---|---|---|
asis |
'launchCount' |
'isDarkMode' |
snake |
'launch_count' |
'is_dark_mode' |
camel |
'launchCount' |
'isDarkMode' |
pascal |
'LaunchCount' |
'IsDarkMode' |
kebab |
'launch-count' |
'is-dark-mode' |
build.yaml global key casing #
# <project>/build.yaml
targets:
$default:
builders:
preferences_generator|preferences:
options:
key_case: snake # applies to every module in this package
Reactive UI #
Streams #
Enable streams with the streamer template parameter. Any entry can have
its own stream regardless of whether the module uses ChangeNotifier.
// Module-wide streams (reactive preset)
@PrefsModule.reactive() // streamer: '{{name}}Stream' by default
abstract class AppSettings with _$AppSettings, ChangeNotifier { ... }
// Generates: usernameStream, themeModeStream, etc.
// Per-entry stream opt-in (base module, no streams by default)
@PrefsModule(keyCase: KeyCase.snake)
abstract class SecureSettings with _$SecureSettings, ChangeNotifier {
SecureSettings._({
@PrefEntry(streamer: 'watch{{Name}}Stream')
String? authToken, // generates: watchAuthTokenStream
@PrefEntry(streamer: '{{name}}Stream')
bool areBiometricsEnabled = false, // generates: areBiometricsEnabledStream
ApiSession? apiSession, // no stream — ChangeNotifier only
});
}
StreamBuilder in Flutter #
// Stream fires when:
// 1. A setter is called (immediate, optimistic update)
// 2. _load() reads a changed value from storage (on startup + refresh())
// 3. removeAll() restores all fields to defaults
StreamBuilder<String>(
stream: settings.usernameStream,
builder: (context, snap) {
return Text(snap.data ?? 'loading...');
},
)
ChangeNotifier + ListenableBuilder #
@PrefsModule.reactive()
abstract class AppSettings with _$AppSettings, ChangeNotifier {
factory AppSettings(PrefsAdapter adapter) = _AppSettings;
AppSettings._({ ThemeMode themeMode = ThemeMode.system });
}
// In your widget tree — rebuilds whenever any setting changes:
ListenableBuilder(
listenable: appSettings,
builder: (context, _) => MaterialApp(
themeMode: appSettings.themeMode,
),
)
Suppressing notifyListeners for specific fields #
// This field changes frequently (e.g., scroll position).
// Suppress notifications to avoid unnecessary rebuilds.
@PrefEntry(notifiable: false)
double scrollOffset = 0.0,
Error Handling #
Synchronous setters use a fire-and-forget Future(() async { ... }) write.
Without onWriteError, storage failures are silently discarded — the
in-memory value is always updated immediately (optimistic update).
@PrefsModule.reactive(onWriteError: AppSettings._onWriteError)
abstract class AppSettings with _$AppSettings, ChangeNotifier {
factory AppSettings(PrefsAdapter adapter) = _AppSettings;
AppSettings._({ String username = 'guest' });
static void _onWriteError(Object error, StackTrace st) {
// Log, report to Sentry, show a snackbar, etc.
debugPrint('[Prefs] Write failed: $error\n$st');
}
}
Async setters (await settings.setUsernameAsync('Alice')) propagate
exceptions normally — wrap them in try/catch at the call site.
Lifecycle #
How _load() works #
_load() is the private method the generator produces. It is:
- Called once automatically in the constructor (fire-and-forget).
- Guarded by
_isLoaded = false— once loaded, subsequent calls are no-ops. - Re-armed by
refresh()andremoveAll(), which reset_isLoaded = false. - Pushes changed values to stream controllers when a value read from storage differs from the in-memory cache.
// Calling multiple async getters in sequence is safe — only one storage
// read cycle occurs regardless of how many getters are called.
final name = await settings.getUsernameAsync();
final mode = await settings.getThemeModeAsync(); // no second storage read
refresh() #
Re-reads all values from storage. Any changed values are pushed to their
stream controllers and notifyListeners() is called if values changed.
// Call after the app returns to foreground, or after an external write.
await settings.refresh();
removeAll() #
Clears all storage keys, then re-reads (restoring defaults). All stream
controllers receive the default values. notifyListeners() is called.
await settings.removeAll(); // equivalent to a factory reset
Implementing PrefsAdapter #
The adapter contract is intentionally minimal — only primitive types. The generator handles all serialization for complex types.
abstract interface class PrefsAdapter {
Future<T?> get<T>(String key); // T is always a primitive
Future<void> set<T>(String key, T value);
Future<void> remove(String key);
Future<void> removeAll();
}
Which primitives does the adapter receive? #
| Dart field type | Adapter T |
|---|---|
int, bool, double, String |
Same type |
List<String> |
List<String> |
Set<*>, Map<*>, Record, DateTime (when stored as JSON), custom |
String or Map<String, dynamic> |
Enum |
String |
DateTime |
String |
Duration |
int |
Complete shared_preferences adapter #
class SharedPreferencesAdapter implements PrefsAdapter {
final SharedPreferences _prefs;
SharedPreferencesAdapter(this._prefs);
@override
Future<T?> get<T>(String key) async => _prefs.get(key) as T?;
@override
Future<void> remove(String key) => _prefs.remove(key);
@override
Future<void> removeAll() => _prefs.clear();
@override
Future<void> set<T>(String key, T value) async {
switch (value) {
case final String v: await _prefs.setString(key, v);
case final bool v: await _prefs.setBool(key, v);
case final int v: await _prefs.setInt(key, v);
case final double v: await _prefs.setDouble(key, v);
case final List<String> v: await _prefs.setStringList(key, v);
default:
// Map<String, dynamic> — store as JSON string
if (value is Map) await _prefs.setString(key, jsonEncode(value));
}
}
}
Minimal in-memory adapter (for testing / console apps) #
class InMemoryAdapter implements PrefsAdapter {
final _store = <String, dynamic>{};
@override Future<T?> get<T>(String key) async => _store[key] as T?;
@override Future<void> set<T>(String key, T value) async => _store[key] = value;
@override Future<void> remove(String key) async => _store.remove(key);
@override Future<void> removeAll() async => _store.clear();
}
Global build.yaml Options #
targets:
$default:
builders:
preferences_generator|preferences:
options:
# Project-wide default key casing.
# Overridden by @PrefsModule(keyCase:) and @PrefEntry(key:).
# Values: asis | snake | camel | pascal | kebab
key_case: snake
# Override the generated file extension (default: .prefs.dart)
# build_extensions:
# '.dart': '.g.dart'
Known Limitations #
Null distinction. With containsKey removed from the adapter contract,
there is no way to distinguish a nullable field that was never written from
one explicitly set to null. Both return null from get(). If the
distinction matters, store a non-null sentinel value instead.
AI Agents & LLM Compatibility #
This section is written for AI coding assistants (Claude, Copilot, Cursor, Gemini, etc.) to use as a reference when generating code with this package.
Decision tree: which preset? #
Does the app use Flutter?
├── Yes
│ ├── Needs reactive UI (streams or ChangeNotifier)?
│ │ ├── Yes → @PrefsModule.reactive() [add ChangeNotifier to class]
│ │ └── No → @PrefsModule.dictionary() [async writes, sync reads]
│ └── Backend is fully synchronous (Hive)?
│ └── Yes → @PrefsModule.syncOnly()
└── No (Dart CLI / server)
├── Needs full API surface?
│ └── Yes → @PrefsModule.exhaustive()
└── No → @PrefsModule.minimal()
Required class structure (invariant) #
Every @PrefsModule class must have exactly:
@PrefsModule.<preset>()
abstract class MySettings with _$MySettings { // ← 'with _$MySettings' required
factory MySettings(PrefsAdapter adapter) = _MySettings; // ← factory required
MySettings._({ /* schema */ }); // ← private ctor required
}
// When using ChangeNotifier:
abstract class MySettings with _$MySettings, ChangeNotifier { ... }
Schema constructor rules #
MySettings._({
// ✅ Compile-time constant default
String username = 'guest',
bool flag = false,
int count = 0,
MyEnum value = MyEnum.first,
// ✅ Nullable — no default needed (null is the default)
String? optionalValue,
// ✅ Non-constant default — use @PrefEntry(initial:)
@PrefEntry(initial: MySettings._now)
DateTime? createdAt,
// ✅ Custom type — must provide converter or toStorage/fromStorage
@PrefEntry(converter: MyConverter())
MyType? customValue,
// ❌ WRONG — non-const default without @PrefEntry(initial:)
// DateTime createdAt = DateTime.now(), // will not compile
});
Correct part directive #
part 'my_settings.prefs.dart'; // ← always .prefs.dart, not .g.dart
Complete reactive example (copy-paste ready) #
// lib/app_settings.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:preferences_annotation/preferences_annotation.dart';
part 'app_settings.prefs.dart';
enum AppTheme { light, dark, system }
@PrefsModule.reactive(onWriteError: AppSettings._onWriteError)
abstract class AppSettings with _$AppSettings, ChangeNotifier {
factory AppSettings(PrefsAdapter adapter) = _AppSettings;
AppSettings._({
String username = 'guest',
bool isDarkMode = false,
AppTheme theme = AppTheme.system,
int? lastNotificationId,
List<String> recentSearches = const <String>[],
Duration sessionTimeout = const Duration(minutes: 30),
@PrefEntry(converter: ColorConverter())
Color? accentColor,
@PrefEntry(key: 'launch_counter', streamer: 'on{{Name}}Updated')
int launchCount = 0,
@PrefEntry(readOnly: true)
String installId = 'uuid-1234-abcd',
@PrefEntry(notifiable: false, initial: AppSettings._now)
DateTime? lastActiveAt,
});
static void _onWriteError(Object e, StackTrace st) =>
debugPrint('[AppSettings] write error: $e');
static DateTime _now() => DateTime.now();
}
// lib/adapters/shared_preferences_adapter.dart
// (see Implementing PrefsAdapter section above)
// lib/main.dart
late final AppSettings appSettings;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final prefs = await SharedPreferences.getInstance();
appSettings = AppSettings(SharedPreferencesAdapter(prefs));
await appSettings.refresh(); // loads storage → fires streams
runApp(const App());
}
// lib/app.dart
ListenableBuilder(
listenable: appSettings,
builder: (context, _) => MaterialApp(
themeMode: appSettings.isDarkMode ? ThemeMode.dark : ThemeMode.light,
),
)
// Stream-driven widget:
StreamBuilder<int>(
stream: appSettings.onLaunchCountUpdated, // custom stream name
builder: (context, snap) => Text('Launches: ${snap.data ?? 0}'),
)
Common mistakes AI agents make #
| Mistake | Correct |
|---|---|
part 'settings.g.dart' |
part 'settings.prefs.dart' |
class MySettings (non-abstract) |
abstract class MySettings |
Missing with _$MySettings |
abstract class MySettings with _$MySettings |
Missing factory constructor |
factory MySettings(PrefsAdapter adapter) = _MySettings; |
DateTime now = DateTime.now() |
@PrefEntry(initial: MySettings._now) DateTime? now, |
settings.usernameStream when using .dictionary() |
streams are not generated by .dictionary() — use .reactive() |
settings.setUsername('x') when using .dictionary() |
.dictionary() setters are async: await settings.setUsername('x') |
Referencing settings.username when using .dictionary() |
getter name is getUsername() in .dictionary() preset |
AffixConfig, CustomConfig, NamedConfig (v2 API) |
Use template strings: setter: 'set{{Name}}' |
Generated method name reference (by preset, field username) #
Preset getter setter remover stream
──────────────────────────────────────────────────────────────────────────────────
dictionary() getUsername() setUsername() async removeUsername() async —
reactive() username setUsername() removeUsername() usernameStream
syncOnly() getUsername() putUsername() deleteUsername() —
syncFirst() username setUsername() removeUsername() —
usernameAsync() setUsernameAsync() removeUsernameAsync()
minimal() username setUsername() removeUsername() —
exhaustive() getUsernameSync setUsernameSync removeUsernameSync watchUsernameStream
getUsernameAsync setUsernameAsync removeUsernameAsync
Packages #
| Package | Role |
|---|---|
preferences_generator |
build_runner generator — dev dependency |
preferences_annotation |
Annotations & interfaces — runtime dependency |