An interactive isometric map editor built with Flutter and the Flame game engine. Import your own isometric sprite packs from a ZIP, organize them into fully configurable groups, then paint terrain, buildings and props onto an isometric grid — all in the browser.
Inspired by isocity. IsoMapa bundles no art of its own — you bring your own packs. See CREDITS.md for recommended free (CC0) sources.
- Bring your own assets — the editor starts empty and asks you to import an isometric pack from a ZIP. The import screen lists every PNG, lets you assign each one (or a whole folder) to a group, exclude images you don't want, and pick the default terrain. Tile size and ground anchors are auto-detected from sprite alpha bounds.
- Configurable groups — groups (terrain, buildings, vegetation, …) are not hardcoded. Create, rename, reorder and pick icons for them; mark one as the base (the ground layer). Group order defines how sprites stack.
- Multiple packs at once — load as many packs as you like and use them simultaneously. Each group tab merges the sprites of every pack, with a per-pack filter. No more swapping one pack out to use another.
- Brush-based editing — tap a thumbnail to pick a brush, drag to paint a stroke; one sprite per group per cell, groups layered on top of each other.
- Eraser & eyedropper — remove the topmost object (or reset the ground), or pick any placed asset as the active brush.
- Per-cell Z-offset — nudge a placed asset up or down for layered scenes.
- Place metadata — attach a name and notes to any cell.
- Undo / redo — grouped per stroke;
Ctrl+Z/Ctrl+Y. - Persistence — save/load the map and export/import it as JSON; imported packs and your group layout persist across reloads (IndexedDB + localStorage).
- Performant rendering — viewport culling keeps large maps (up to 120×120) smooth.
Requires the Flutter SDK (see environment.sdk in pubspec.yaml). The web is
the configured target.
flutter pub get
flutter run -d chrome # develop
flutter build web # production build -> build/web/On first launch the app shows an onboarding screen: import a pack with at least one image assigned to the base (terrain) group, and the first map is created automatically.
flutter analyze # static analysis (flutter_lints)
flutter test # run the test suiteThe app is layered Flutter UI → Flame game → map component, with everything parameterised by configurable groups and runtime-imported packs, so the core never hardcodes any tileset.
| File | Responsibility |
|---|---|
lib/main.dart |
Flutter shell: menus, toolbar, resizable palette, the onboarding overlay, and all camera & paint input via a single gesture recognizer. |
lib/iso_game.dart |
FlameGame bridge — owns the camera, holds editor state (ValueNotifiers the UI listens to), the AssetLibrary, map lifecycle and save/load/export. |
lib/iso_map.dart |
Pack-agnostic model + renderer + edit engine: isometric projection, painter's-algorithm depth sorting, viewport culling, undo/redo. |
lib/asset_group.dart |
AssetGroup / GroupConfig — the configurable, ordered groups (one is the base). |
lib/asset_pack.dart |
AssetPack — an imported pack's sprites bucketed by group id. |
lib/asset_library.dart |
Merges all packs + groups: per-group palettes across packs, geometry, default terrain, ref resolution. |
lib/sprite_ref.dart |
SpriteRef (pack/group/index) + the resolved Placement stored on cells. |
lib/city_data.dart |
The editable data model (CityData, CellData) and JSON (de)serialization. |
lib/editor_palette.dart, lib/config_menu.dart |
The editor UI: group tabs with per-pack filters, the group manager, and the PNG-by-PNG import flow. |
lib/pack_import.dart, lib/pack_store.dart |
Parse an isometric pack from a ZIP (list PNGs, detect geometry/anchors) and persist it. |
lib/platform/ |
Web file download / localStorage / IndexedDB (package:web + dart:js_interop), with a no-op stub off the web. |
Grid (x, y) maps to world space via the diamond projection used throughout
iso_map.dart:
worldX = (y - x) * tileWidth / 2
worldY = (x + y) * tileHeight / 2
Cells are drawn by diagonals (s = x + y increasing) so nearer cells
overdraw farther ones — required for correct depth of tall sprites. Within a
cell, the base group draws first (the ground), then the remaining groups in
order.
Source code: MIT — see LICENSE. Imported sprite art is licensed by whoever made the pack you load — see CREDITS.md.