Native portrait Flutter client for Dungeon Crawl Stone Soup (DCSS) Webtiles.
This project targets sideloaded mobile builds and connects to any DCSS WebTiles server over WSS. The default server is wss://crawl.dcss.io/socket, with four well-known public servers selectable in-app and support for custom URLs.
A web / PWA build is also published automatically to GitHub Pages on every push to main:
Networking
- WebSocket connection manager with full DCSS Webtiles JSON protocol support
- HTTP tile image fetcher (
dio) with local caching viapath_provider
Rendering
- Flame-based tile viewport (
tile_scene.dart) - HTTP tile loader/cache (
tile_loader.dart) - Individual tile sprite widget (
tile_sprite_widget.dart) - DCSS color/markup text utilities (
dcss_text_util.dart)
UI
- Login screen with built-in server picker and credential storage (
flutter_secure_storage) - In-game screen: tile viewport, status bar, and scrollable message log
- In-game menu overlay
- In-game text prompt overlay (
txt_overlay.dart) - Tabbed virtual keyboard panel with 5 layers:
- Move — 8-directional movement, wait, rest
- Act — common actions (open, close, pick up, drop, …)
- Char — character screens (inventory, spells, skills, …)
- Keys — special keys (Enter, Escape, Tab, …)
- More — less-common commands
- Persistent modifier strip (Ctrl / Shift toggles)
- Settings screen with persistent preferences (
shared_preferences)
State management
- Riverpod throughout (
flutter_riverpod)
| Short | Name | Location | WSS URL |
|---|---|---|---|
| CDI | crawl.dcss.io | New York 🇺🇸 | wss://crawl.dcss.io/socket |
| CPO | crawl.project357.org | Sydney 🇦🇺 | wss://crawl.project357.org/socket |
| CNC | crawl.nemelex.cards | South Korea 🇰🇷 | wss://crawl.nemelex.cards/socket |
| CUE | underhound.eu:8080 | Europe 🇪🇺 | wss://underhound.eu:8080/socket |
Custom server URLs can be entered manually in Settings.
| Setting | Default | Description |
|---|---|---|
| Server URL | wss://crawl.dcss.io/socket |
WebTiles server to connect to |
| Tile Scale Multiplier | 1.0 |
Scales the tile viewport rendering size |
| Message Log Font Size | 14.0 |
Font size for the in-game message log |
| Haptics Enabled | true |
Vibration feedback on key presses |
| Show Grid Lines | false |
Overlay grid lines on the tile viewport |
lib/
├── main.dart # App entry point
├── app.dart # Root widget / router
├── game/
│ ├── game_state.dart # Game state (Riverpod)
│ ├── tile_index.dart # Tile ID → sprite mapping
│ ├── tile_loader.dart # HTTP tile fetcher + cache
│ └── tile_scene.dart # Flame tile scene renderer
├── network/
│ ├── dcss_protocol.dart # Webtiles JSON protocol parser
│ └── websocket_manager.dart # WebSocket connection manager
├── ui/
│ ├── game_screen.dart # Main in-game screen
│ ├── login_screen.dart # Login + server picker
│ ├── menu_overlay.dart # In-game menu overlay
│ ├── message_log_widget.dart # Scrolling message log
│ ├── status_bar_widget.dart # HP / MP / gold status bar
│ ├── tile_sprite_widget.dart # Single tile sprite widget
│ ├── txt_overlay.dart # In-game text prompt overlay
│ ├── dcss_text_util.dart # DCSS color/markup rendering
│ └── keyboard/
│ ├── keyboard_panel.dart # Tabbed keyboard container
│ ├── layer_tab_bar.dart # Layer tab bar
│ ├── modifier_strip.dart # Ctrl / Shift modifier toggles
│ ├── move_layer.dart # Movement layer
│ ├── act_layer.dart # Actions layer
│ ├── char_layer.dart # Character layer
│ ├── keys_layer.dart # Special keys layer
│ ├── more_layer.dart # More commands layer
│ ├── key_button.dart # Individual key button widget
│ ├── keyboard_action.dart # Key action model
│ └── keyboard_layer.dart # Layer base class / interface
├── settings/
│ ├── app_settings.dart # Settings model + Riverpod notifier
│ └── settings_screen.dart # Settings UI screen
└── utils/
└── keycode_helpers.dart # DCSS keycode conversion helpers
- Flutter
>=3.3.0(Dart SDK>=3.3.0 <4.0.0) - Android SDK (for APK/AAB builds)
- Xcode (for iOS local builds)
flutter pub get
flutter runIf platform folders are missing in your local checkout, scaffold them once:
flutter create --platforms=android,ios .flutter build apk --release
flutter build appbundle --releaseOutputs:
build/app/outputs/flutter-apk/app-release.apkbuild/app/outputs/bundle/release/app-release.aab
flutter build ios --release --no-codesignflutter build web --release --base-href "/dcss-mobile/"Output: build/web/ — serve from any static file host or open index.html locally.
No App Store required. To install on iPhone or iPad:
- Open https://willthehuman.github.io/dcss-mobile/ in Safari.
- Tap the Share button (box with arrow).
- Tap Add to Home Screen.
- Tap Add.
Launching from the home screen opens the app fullscreen with no browser chrome, exactly like a native app.
Note:
flutter_secure_storageis used for credential storage on native builds. On the web build, credentials fall back tolocalStoragevia theflutter_secure_storage_webpackage. This is less secure than the native keychain — avoid using the web build on shared devices.
Workflow file: .github/workflows/build.yml
It runs on pushes to main, pull requests, manual dispatch, and version tags (v*).
Pipeline steps:
- Checkout code
- Setup Java + Flutter
- Auto-generate missing
android/andios/folders if needed flutter pub getflutter analyze- Run tests when present
- Build Android APK + AAB
- Upload Android artifacts
- Run iOS validation build (
flutter build ios --release --no-codesign) on macOS - Package an unsigned IPA from
Runner.app - Build Flutter Web and deploy to GitHub Pages (see below)
- For tag pushes (
v*), publish a GitHub Release with APK/AAB/IPA attached
All web deployments publish to the gh-pages branch using peaceiris/actions-gh-pages.
| Event | Deploy path | URL |
|---|---|---|
Push to main |
/ (root) |
https://willthehuman.github.io/dcss-mobile/ |
| Pull request opened/updated | /pr-<number>/ |
https://willthehuman.github.io/dcss-mobile/pr-42/ |
- PR previews are posted as a bot comment on the pull request automatically.
- Preview folders are cleaned up automatically when a PR is closed via
.github/workflows/cleanup-pr-preview.yml.
Push a tag like:
git tag v1.0.0
git push origin v1.0.0The workflow will create a GitHub Release and attach:
app-release.apkapp-release.aabdcss-mobile-unsigned.ipa
- CI validates iOS compilation with
--no-codesignbut does not produce a signed IPA. - This app is intended for sideloading/testing, not app store submission.
- The web PWA build is deployed to the
gh-pagesbranch automatically on every push tomain. GitHub Pages must be configured to serve from thegh-pagesbranch in repository Settings → Pages.