auto_save_controller 1.0.0 copy "auto_save_controller: ^1.0.0" to clipboard
auto_save_controller: ^1.0.0 copied to clipboard

A Flutter TextEditingController that automatically saves and loads local text drafts with built-in debounce, TTL expiry, and AppLifecycle awareness.

auto_save_controller #

A headless, storage-agnostic Flutter package that extends TextEditingController to automatically save, load, and manage local text drafts — with built-in debounce, TTL (Time-To-Live), and AppLifecycle awareness.

pub.flutter-io.cn License: MIT Live Demo


Features #

  • Zero third-party dependencies — only the Flutter SDK.
  • Storage-agnostic — bring your own backend via the AutoSaveStorage interface (SharedPreferences, Hive, Isar, SQLite, in-memory, …).
  • Debounced saves — configurable delay prevents excessive writes during active typing.
  • TTL expiry — drafts older than a configurable threshold are automatically discarded on load.
  • AppLifecycle aware — pending saves are flushed immediately when the app goes inactive, is paused, or detached.
  • Race-condition safe — a draft is never injected if the user typed something during the async load.
  • AutoSaveController typedef — a concise alias for AutoSaveTextEditingController.
  • Drop-in replacement — extends TextEditingController directly; all existing APIs continue to work.

Installation #

Add to your pubspec.yaml:

dependencies:
  auto_save_controller: ^1.0.0

Then run:

flutter pub get

Getting Started #

Step 1 — Implement AutoSaveStorage #

The package ships with no storage implementation so it stays dependency-free. Create your own adapter by implementing the AutoSaveStorage interface:

class InMemoryStorage implements AutoSaveStorage {
  final Map<String, String> _store = {};

  @override
  Future<String?> read(String key) async => _store[key];

  @override
  Future<void> write(String key, String value) async {
    _store[key] = value;
  }

  @override
  Future<void> delete(String key) async => _store.remove(key);
}

See the example app for full implementations using SharedPreferences, Hive, and Isar.

Step 2 — Register Global Storage (optional) #

Register a single default backend once at application startup:

void main() {
  AutoSave.init(storage: InMemoryStorage());
  runApp(const MyApp());
}

All controllers that don't receive an explicit storage argument will fall back to this global instance.

Step 3 — Use the Controller #

class _MyFormState extends State<MyForm> {
  late final AutoSaveController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AutoSaveController(cacheKey: 'my_form_draft');
  }

  @override
  void dispose() {
    _controller.dispose(); // flushes any pending debounced save
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return TextField(controller: _controller);
  }
}

AutoSaveController is a typedef alias for AutoSaveTextEditingController. Both names refer to the same class.


Per-Controller Storage Injection #

Pass a storage argument to the constructor to bypass the global instance for a specific controller. This is useful when different form fields should persist to different backends, or when you want to avoid calling AutoSave.init() entirely:

final controller = AutoSaveController(
  cacheKey: 'account_bio',
  storage: HiveStorage(),          // overrides AutoSave.storage
  debounceDuration: Duration(seconds: 1),
  maxDraftAge: Duration(days: 30),
);

API Reference #

AutoSaveController (typedef) #

Alias for AutoSaveTextEditingController. Prefer this for brevity.


AutoSaveTextEditingController #

Extends TextEditingController.

Parameter Type Default Description
cacheKey String required Unique storage key for the draft.
text String? null Optional initial text.
storage AutoSaveStorage? AutoSave.storage Per-controller storage override.
debounceDuration Duration 500 ms Delay before a save is committed.
maxDraftAge Duration? null TTL — expired drafts are discarded.
maxDraftLength int 10 000 Max chars eligible for saving.
prioritizeDraftOverInitial bool true Draft vs. initial text priority.

Methods

Method Description
clearDraft() Deletes the stored draft and clears the text field.
dispose() Flushes pending saves, disposes lifecycle listener, then calls super.dispose().

AutoSave #

Global configuration singleton.

Member Description
AutoSave.init(storage:) Registers the global AutoSaveStorage backend.
AutoSave.storage Returns the registered backend; throws StateError if unset.

AutoSaveStorage #

Abstract interface for storage adapters.

abstract class AutoSaveStorage {
  Future<String?> read(String key);
  Future<void> write(String key, String value);
  Future<void> delete(String key);
}

DraftData #

Internal model used for serialization. Exposed publicly for custom adapters that need to inspect or migrate stored data.

Property Type Description
text String The draft text content.
timestamp DateTime UTC time the draft was last saved.

Edge Cases #

Scenario Behaviour
Storage not initialized AutoSave.storage throws a descriptive StateError.
Draft is null in storage Controller starts empty; no error.
Draft is malformed JSON Silently discarded; debugPrint warning logged.
Draft is expired (TTL) Deleted from storage; controller starts empty.
User types during async load Race condition detected; draft injection aborted.
Initial text provided, prioritizeDraftOverInitial: false Draft ignored; initial text preserved.
Text exceeds maxDraftLength Save skipped; existing draft preserved; warning logged.
Text cleared to empty storage.delete(key) called; storage is cleaned up.
App backgrounded mid-debounce _forceSave() cancels timer and saves immediately.
dispose() with pending timer Timer cancelled; best-effort save fired before teardown.

Example App #

Live demo →

The example/ directory contains a full Flutter application demonstrating all three storage adapters (SharedPreferences, Hive, Isar). Each backend has its own page — navigate away and return to see the draft restored from storage.

cd example
flutter pub get
# For Isar — generate the collection schema:
flutter pub run build_runner build --delete-conflicting-outputs
flutter run

Contributing #

Contributions, bug reports, and feature requests are welcome! Please open an issue or pull request on GitHub.


License #

MIT © auto_save_controller contributors

0
likes
160
points
30
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter TextEditingController that automatically saves and loads local text drafts with built-in debounce, TTL expiry, and AppLifecycle awareness.

Repository (GitHub)
View/report issues

Topics

#text-editing #autosave #draft #form #storage

License

MIT (license)

Dependencies

flutter

More

Packages that depend on auto_save_controller