focus_quest 0.0.1
focus_quest: ^0.0.1 copied to clipboard
A gamified focus and anti-doomscroll productivity engine for Flutter apps with sessions, rewards, streaks, persistence, and Riverpod integration.
focus_quest #
A reusable gamified focus and anti-doomscroll productivity engine for Flutter.
focus_quest gives app developers the business logic for focus sessions,
rewards, streaks, levels, statistics, persistence, lifecycle handling, optional
feedback, and Riverpod state. You bring the UI: a virtual pet, garden, study
timer, charity tracker, team focus tool, or any other game layer.
Features #
- Start, pause, resume, complete, cancel, reset, and restore focus sessions
- Timestamp-based countdowns that stay accurate after delayed timer ticks
- Configurable background behavior: pause, cancel, or keep running
- Points, XP, completion bonuses, partial rewards, and custom reward metadata
- Daily goal progress, current streak, longest streak, completion rate, and history
- In-memory, SharedPreferences-backed, and Hive-backed local persistence
- Storage abstraction for custom Isar, SQLite, secure storage, or backend adapters
- Riverpod notifier and immutable state for Flutter apps
- Optional haptic and audioplayers feedback hooks with a no-op default
Use Cases #
focus_quest is suitable for anti-doomscroll apps where staying away from the
phone can grow a virtual forest, feed a pet, unlock collectibles, or record
charity-impact metadata. It is also useful for Pomodoro apps, study timers,
habit trackers, digital wellbeing products, and employee focus tools.
The package tracks app-level focus sessions. It does not block, inspect, or monitor other installed apps.
Supported Platforms #
The core Dart logic works anywhere Flutter runs. The bundled
SharedPreferencesFocusQuestStorage depends on shared_preferences, which
supports Android, iOS, web, macOS, Windows, and Linux. Lifecycle behavior is
app-level and depends on Flutter lifecycle events from the host app.
Installation #
flutter pub add focus_quest
Quick Start #
import 'package:focus_quest/focus_quest.dart';
Future<void> main() async {
final controller = FocusQuestController(
storage: SharedPreferencesFocusQuestStorage(),
);
await controller.initialize();
await controller.start(
duration: const Duration(minutes: 25),
metadata: {
'category': 'study',
'task': 'Japanese vocabulary',
},
);
await controller.pause();
await controller.resume();
await controller.complete();
final state = controller.state;
print('Points: ${state.totalPoints}');
print('Streak: ${state.currentStreak}');
}
Riverpod Usage #
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:focus_quest/focus_quest.dart';
class FocusButton extends ConsumerWidget {
const FocusButton({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(focusQuestStateProvider);
final notifier = ref.read(focusQuestStateProvider.notifier);
return ElevatedButton(
onPressed: state.status == FocusSessionStatus.running
? null
: () => notifier.start(duration: const Duration(minutes: 25)),
child: Text(state.status.name),
);
}
}
Custom Reward Strategy #
Use reward metadata to drive your own game world, pet, garden, or charity UI.
class GardenRewardStrategy implements RewardStrategy {
@override
FocusReward calculate(FocusSession session, FocusQuestConfig config) {
final focusedMinutes = session.actualFocusDuration.inMinutes;
final completed = session.status == FocusSessionStatus.completed;
return FocusReward(
points: completed ? focusedMinutes + config.completionBonus : 0,
experience: focusedMinutes * 2,
metadata: {
'treeGrowth': focusedMinutes,
'petFood': focusedMinutes ~/ 5,
'charityCents': completed ? focusedMinutes : 0,
},
);
}
}
Storage Customization #
Use InMemoryFocusQuestStorage in tests,
SharedPreferencesFocusQuestStorage for lightweight local persistence, or
HiveFocusQuestStorage for a Hive-backed store.
final controller = FocusQuestController(
storage: HiveFocusQuestStorage(boxName: 'my_focus_app'),
);
For a different database or backend, implement FocusQuestStorage and persist
FocusSession.toJson() plus FocusProfile.toJson().
Lifecycle Behavior #
Call handleLifecycleEvent from your app lifecycle observer. The configured
background behavior decides whether the active session pauses, cancels, or keeps
running.
controller = FocusQuestController(
config: const FocusQuestConfig(
backgroundBehavior: BackgroundBehavior.pause,
maxInterruptions: 3,
),
);
await controller.handleLifecycleEvent(FocusLifecycleEvent.paused);
Elapsed and remaining time are calculated from timestamps, not only from one-second ticks. The controller prevents duplicate active timers and finalizes completed sessions once.
Optional Feedback and Audio #
The package includes FocusFeedback, NoopFocusFeedback,
FlutterFocusFeedback for haptics, and AudioplayersFocusFeedback for
optional asset-based sound effects.
final controller = FocusQuestController(
feedback: AudioplayersFocusFeedback(
startedAsset: 'sounds/start.mp3',
completedAsset: 'sounds/complete.mp3',
levelUpAsset: 'sounds/level-up.mp3',
),
);
Sound assets are optional. Declare any assets you use in the host app's
pubspec.yaml.
Example Application #
See example/ for a small app that initializes the controller, starts focus
sessions, handles lifecycle changes, displays progress, and persists state.
Platform Limitations #
- App-level lifecycle tracking is supported.
- Device-wide app usage tracking is not included.
- App blocking is not included.
- Charity donations must be implemented by the app through its own backend or
payment/donation provider.
focus_questcan store reward metadata for that flow, but it does not transfer money.
Testing #
flutter test
flutter test --coverage
cd example && flutter test
dart pub publish --dry-run
Roadmap #
- Richer streak policies and calendar rules
- Share-card helpers for streaks, pets, gardens, and charity progress
Contributing #
Issues and pull requests are welcome. See CONTRIBUTING.md and
CODE_OF_CONDUCT.md for project expectations.