saveable 0.1.0
saveable: ^0.1.0 copied to clipboard
Automatic variable-level state persistence.
// ignore_for_file: avoid_print
import 'package:saveable/saveable.dart';
// =============================================================================
// Example 1: Simple In-Memory Storage (for testing/demos)
// =============================================================================
/// A simple in-memory storage implementation for demonstration.
/// In production, use SharedPreferences, Hive, or similar.
class InMemoryStorage implements Storage {
final Map<String, dynamic> _data = {};
@override
dynamic read(String key) => _data[key];
@override
Future<void> write(String key, dynamic value) async {
_data[key] = value;
}
@override
Future<void> delete(String key) async {
_data.remove(key);
}
@override
Future<void> clear() async {
_data.clear();
}
@override
Future<void> close() async {}
}
// =============================================================================
// Example 2: Using Saveable directly
// =============================================================================
void simpleUsageExample() {
final storage = InMemoryStorage();
// Simple int counter
final counter = Saveable<int>(
key: 'counter',
storage: storage,
defaultValue: 0,
);
print('Initial counter: ${counter.value}'); // 0
// Update the value - automatically persists
counter.value = 42;
print('After update: ${counter.value}'); // 42
// Use update() for functional updates
counter.update((current) => current + 1);
print('After increment: ${counter.value}'); // 43
// Clean up
counter.dispose();
}
// =============================================================================
// Example 3: Complex types with JSON serialization
// =============================================================================
/// Example user model
class User {
final String id;
final String name;
final String email;
const User({
required this.id,
required this.name,
required this.email,
});
factory User.empty() => const User(id: '', name: '', email: '');
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as String,
name: json['name'] as String,
email: json['email'] as String,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
};
}
User copyWith({String? id, String? name, String? email}) {
return User(
id: id ?? this.id,
name: name ?? this.name,
email: email ?? this.email,
);
}
@override
String toString() => 'User(id: $id, name: $name, email: $email)';
}
void complexTypeExample() {
final storage = InMemoryStorage();
// Complex type with serialization
final user = Saveable<User>(
key: 'current_user',
storage: storage,
defaultValue: User.empty(),
fromJson: User.fromJson,
toJson: (u) => u.toJson(),
);
print('Initial user: ${user.value}'); // User(id: , name: , email: )
// Update with copyWith pattern
user.update((current) => current.copyWith(
id: '123',
name: 'John Doe',
email: 'john@example.com',
));
print('After update: ${user.value}');
// User(id: 123, name: John Doe, email: john@example.com)
// Clean up
user.dispose();
}
// =============================================================================
// Example 4: Using StateSaver mixin
// =============================================================================
/// Example settings provider using StateSaver mixin
class SettingsProvider with StateSaver {
late final Saveable<bool> darkMode;
late final Saveable<String> locale;
late final Saveable<int> fontSize;
late final Saveable<User> currentUser;
final Storage _storage;
@override
Storage get storage => _storage;
@override
String get storagePrefix => 'settings';
SettingsProvider(this._storage) {
// Simple types - no serialization needed
darkMode = saveable<bool>(
key: 'dark_mode',
defaultValue: false,
);
locale = saveable<String>(
key: 'locale',
defaultValue: 'en',
);
fontSize = saveable<int>(
key: 'font_size',
defaultValue: 14,
);
// Complex type - with serialization
currentUser = saveable<User>(
key: 'current_user',
defaultValue: User.empty(),
fromJson: User.fromJson,
toJson: (u) => u.toJson(),
);
}
void dispose() {
disposeSaveables();
}
}
void stateSaverExample() {
final storage = InMemoryStorage();
final settings = SettingsProvider(storage);
print('Dark mode: ${settings.darkMode.value}'); // false
print('Locale: ${settings.locale.value}'); // en
// Update values
settings.darkMode.value = true;
settings.locale.value = 'fr';
settings.fontSize.value = 16;
print('Dark mode: ${settings.darkMode.value}'); // true
print('Locale: ${settings.locale.value}'); // fr
print('Font size: ${settings.fontSize.value}'); // 16
// Update complex type
settings.currentUser.update((user) => user.copyWith(
id: '456',
name: 'Jane Doe',
));
print('User: ${settings.currentUser.value}');
// Clean up all saveables at once
settings.dispose();
}
// =============================================================================
// Example 5: Using with ValueListenableBuilder (in a widget)
// =============================================================================
/// Example showing how to use Saveable with Flutter widgets.
///
/// ```dart
/// class SettingsScreen extends StatelessWidget {
/// final SettingsProvider settings;
///
/// const SettingsScreen({super.key, required this.settings});
///
/// @override
/// Widget build(BuildContext context) {
/// return Column(
/// children: [
/// // Dark mode toggle
/// ValueListenableBuilder<bool>(
/// valueListenable: settings.darkMode,
/// builder: (context, isDark, child) {
/// return SwitchListTile(
/// title: const Text('Dark Mode'),
/// value: isDark,
/// onChanged: (value) => settings.darkMode.value = value,
/// );
/// },
/// ),
///
/// // Font size slider
/// ValueListenableBuilder<int>(
/// valueListenable: settings.fontSize,
/// builder: (context, size, child) {
/// return Slider(
/// value: size.toDouble(),
/// min: 10,
/// max: 24,
/// onChanged: (value) => settings.fontSize.value = value.toInt(),
/// );
/// },
/// ),
/// ],
/// );
/// }
/// }
/// ```
// =============================================================================
// Main
// =============================================================================
void main() {
print('=== Simple Usage Example ===');
simpleUsageExample();
print('\n=== Complex Type Example ===');
complexTypeExample();
print('\n=== StateSaver Mixin Example ===');
stateSaverExample();
print('\nAll examples completed successfully!');
}