idle_flutter 0.2.0 copy "idle_flutter: ^0.2.0" to clipboard
idle_flutter: ^0.2.0 copied to clipboard

A Flutter binding layer for idle/incremental games built on idle_core and idle_save

idle_flutter #

idle_flutter is a lightweight Flutter binding layer for idle_core + idle_save. It stays framework-agnostic (no Riverpod/BLoC lock-in) and focuses on two things by default: safe lifecycle + offline persistence, and safe UI rebuild behavior.

Documentation in this repository is always written in English.

Core Goals #

  • Framework neutral: Use any state management you want. No forced coupling.
  • Mistake-proof persistence: Avoid lost saves or double-applied offline progress.
  • UI performance safety: Selector equality by default, optional rebuild warnings in debug.
  • Mobile/Desktop first: Web support only when explicitly targeted.

Quick Start #

1) Define state and reducer #

import 'package:idle_core/idle_core.dart';

class GameState extends IdleState {
  final int gold;
  final int rate;

  const GameState({required this.gold, required this.rate});

  GameState copyWith({int? gold, int? rate}) =>
      GameState(gold: gold ?? this.gold, rate: rate ?? this.rate);

  @override
  Map<String, dynamic> toJson() => {'gold': gold, 'rate': rate};

  static GameState fromJson(Map<String, dynamic> json) => GameState(
        gold: (json['gold'] as num?)?.toInt() ?? 0,
        rate: (json['rate'] as num?)?.toInt() ?? 1,
      );
}

class UpgradeRate extends IdleAction {
  final int delta;
  const UpgradeRate(this.delta);
}

GameState reducer(GameState state, IdleAction action) {
  if (action is IdleTickAction) {
    return state.copyWith(gold: state.gold + state.rate);
  }
  if (action is UpgradeRate) {
    return state.copyWith(rate: state.rate + action.delta);
  }
  return state;
}

2) Choose a store #

import 'package:idle_flutter/idle_flutter.dart';
import 'package:idle_save/idle_save.dart';

final store = SharedPreferencesStore('idle_save');

final controller = IdleFlutterController<GameState>(
  config: IdleConfig<GameState>(
    dtMs: 1000,
    maxOfflineMs: 60 * 60 * 1000,
    maxTicksTotal: 100000,
  ),
  reducer: reducer,
  initialState: const GameState(gold: 0, rate: 1),
  stateDecoder: GameState.fromJson,
  store: store,
  migrator: Migrator(latestVersion: 1),
);

3) Bind to widgets #

IdleControllerHost<GameState>(
  controller: controller,
  loading: const SizedBox.shrink(),
  builder: (context, c) {
    return IdleSelector<GameState, int>(
      controller: c,
      selector: (c) => c.state.gold,
      builder: (context, gold, c) => Text('gold=$gold'),
    );
  },
)

Safe Lifecycle + Offline Design #

Core idea #

  1. Persist lastSeenMs: track the last active time of the app.
  2. Apply offline progress exactly once: restore, apply offline window, then immediately save.
  3. Debounce + max interval: saves never get starved by continuous ticks.

Behavior summary #

  • App start: load save -> apply offline -> save immediately
  • App background: capture lastSeenMs -> save immediately
  • App resume: apply offline -> save immediately
  • Normal runtime: debounced save + max interval to guarantee periodic saves
  • autosaveDebounce: default 1s; delay after last activity before saving
  • autosaveMaxInterval: default 10s; ensures a save happens even with constant ticks
final controller = IdleFlutterController<GameState>(
  ...
  autosaveDebounce: const Duration(milliseconds: 300),
  autosaveMaxInterval: const Duration(seconds: 5),
);

API Reference #

IdleFlutterController #

Role

  • Flutter lifecycle wiring for IdleEngine
  • Persistence + migration management
  • Debounced autosave and offline safety

Constructor parameters

  • config: IdleConfig (dtMs, maxOfflineMs, maxTicksTotal)
  • reducer: IdleReducer (game logic)
  • initialState: initial state
  • stateDecoder: decoder for saved state
  • store: primary SaveStore
  • backupStore: optional backup SaveStore
  • codec: save codec (default JSON)
  • migrator: migration chain
  • nowMs: time provider for tests
  • autosaveDebounce: debounce delay
  • autosaveMaxInterval: max save interval
  • startTickingImmediately: auto start on init

Key properties

  • state: current state
  • value: ValueListenable value
  • lastSeenMs: last active timestamp
  • ready: start completed
  • disposed: disposed state

Key methods

  • start(): load + restore + offline apply
  • dispatch(IdleAction): apply action
  • tick({count}): manual tick
  • flush(): force save
  • dispose(): release lifecycle and final save

IdleControllerHost #

Safe wrapper for start()/dispose() lifecycle.

  • startOnInit: auto start on init
  • disposeOnUnmount: dispose controller on unmount
  • rebuildOnChange: rebuild subtree on change

IdleBuilder #

AnimatedBuilder wrapper for full rebuilds.

IdleSelector #

Rebuilds only when selected values change.

  • Default equality uses listEquals/mapEquals/setEquals for collections
  • Other types use ==
  • Use shouldRebuild to provide custom logic
IdleSelector<GameState, List<int>>(
  controller: controller,
  selector: (c) => c.state.someList,
  shouldRebuild: (prev, next) => prev.length != next.length,
  builder: ...,
)

Debug rebuild warning (optional)

IdleSelector<GameState, int>(
  controller: controller,
  selector: (c) => c.state.gold,
  debug: const IdleSelectorDebug(
    label: 'GoldText',
    maxRebuilds: 60,
    interval: Duration(seconds: 10),
  ),
  builder: ...,
)
  • Only in debug mode
  • Logs a warning if rebuild count exceeds the threshold

Stores (SaveStore) #

SharedPreferencesStore #

final store = SharedPreferencesStore('idle_save');
  • Works on Web/Mobile/Desktop
  • Simple and reliable default choice

PathFileStore #

final store = await PathFileStore.documents('idle_save.json');
  • File-backed on Mobile/Desktop
  • On Web, falls back to in-memory store (not persistent)

Web Support Policy #

idle_flutter is Mobile/Desktop focused by default. If Web is a target, prefer:

  • SharedPreferencesStore or a custom SaveStore
  • A web-specific persistence strategy

If Web is not a target, explicitly state Mobile/Desktop focus in your app docs.

Update Policy #

This package follows an SDK-like update policy:

  • Stable APIs
  • Predictable upgrades
  • Clear migration guidance

Contact #

[email protected]

1
likes
160
points
321
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter binding layer for idle/incremental games built on idle_core and idle_save

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, idle_core, idle_save, path_provider, shared_preferences

More

Packages that depend on idle_flutter