DEV Community

Claus Valca for M5 Dev

Posted on

Building OpenROM v3.8.0: Adding Opt-In Online Features to an Offline-First App

We just shipped v3.8.0 of OpenROM — a free, open-source
retro gaming toolkit built with Flutter + Dart.

From day one, OpenROM was designed to be 100% offline.
No telemetry, no accounts, no network requests. But users
kept asking for two things: auto-update notifications and
RetroAchievements hash verification.

The challenge: how do you add online features to an
offline-first app without betraying its core philosophy?

The approach

Every online feature is:

  • Disabled by default (false in SharedPreferences)
  • Requires explicit user opt-in in Settings
  • Fails silently — never blocks offline functionality
  • Clearly documented in SECURITY.md
abstract class OnlinePrefs {
  static const String autoUpdateEnabled = 
    'online_auto_update_enabled';  // default: false
  static const String raEnabled = 
    'online_ra_enabled';           // default: false
}
Enter fullscreen mode Exit fullscreen mode

Auto-update check

Simple GitHub releases API call on startup (if enabled):

static Future<UpdateCheckResult> checkForUpdate(
    String currentVersion) async {
  try {
    final response = await http.get(
      Uri.parse(_repoApi),
      headers: {'Accept': 'application/vnd.github+json'},
    ).timeout(Duration(seconds: 10));
    // compare semver, return result
  } catch (e) {
    return UpdateCheckResult(error: e.toString()); // fail silently
  }
}
Enter fullscreen mode Exit fullscreen mode

No auto-install. Just a banner. User clicks download
themselves.

RetroAchievements hash lookup

MD5 the ROM file, hit the RA API, show a badge on the
ROM card:

Future<RaHashResult> lookupHash(String filePath) async {
  final hash = await computeMd5(filePath);
  // hit retroachievements.org/API/API_GetGameInfoByMD5.php
  // return result — never throws
}
Enter fullscreen mode Exit fullscreen mode

This solves a real pain point: downloading a 4GB PS2 ISO
only to discover achievements are USA-only. Now OpenROM
tells you before you convert.

Stack

  • Flutter + Dart — cross-platform GUI + core logic
  • Dart monorepo — core/ package + gui/ app
  • No Python, no Nuitka — pure Dart = zero AV false positives
  • GPL v3

Links

GitHub: https://github.com/M5Devs/OpenROM
Latest release: v3.8.0


What's your approach to adding online features to
offline-first apps? Happy to discuss in the comments.

Top comments (0)