Scan a room with Apple's RoomPlan and get it back as walls, doors, windows and furniture with real dimensions, plus a USDZ model. iOS, LiDAR required.
Scan a room with Apple's RoomPlan and get it back as data.
final controller = RoomScanController();// Ask first. Most iPhones cannot do this.final support = await RoomScanController.support();if (!support.supported) { /* support.reason tells you why */ }RoomScanView(controller: controller);await controller.start();// …the user walks the room…await controller.stop();controller.rooms.listen((room) { print('${room.walls.length} walls, ${room.objects.length} objects'); print(room.usdzPath); // a USDZ model of what was scanned});Not a point cloud — a parametric model. RoomPlan post-processes the scan
into squared-off walls with real dimensions, and identifies what it found:
| | |
| --- | --- |
| walls, floors | surfaces with metre dimensions and a transform |
| doors, windows, openings | found within the walls |
| objects | recognised furniture — chair, table, bed, storage… |
| usdzPath | a USDZ model, ready for AR Quick Look |
| raw | RoomPlan's own encoding, untouched |
Every dimension is in metres, and position is read out of the transform
rather than invented.
RoomPlan ships RoomCaptureView — the live coaching overlay and the wireframe
that appears as walls are discovered. Reimplementing that in Flutter would mean
redrawing an interface Apple already tuned against real users, so this hosts
theirs and hands the result back as data.
That makes RoomScanView a platform view. It composites differently from a
Flutter widget, and it is the whole camera surface, so treat it as a screen
rather than a component.
RoomPlan needs LiDAR. Most iPhones and iPads do not have it — it is the Pro
models and recent iPad Pros. RoomScanController.support() tells the two
failures apart, because they call for different words to a user:
switch (support.reason) { case RoomScanUnsupportedReason.noLidar: // "your device cannot" case RoomScanUnsupportedReason.osTooOld: // "update iOS" case RoomScanUnsupportedReason.supported: // go ahead}The plugin compiles into apps targeting far older systems and reports
osTooOld there, so adding it does not raise your deployment target.
Scanning has not been run on a LiDAR device by this author. The plugin
compiles for iOS, registers, and reports noLidar correctly on hardware
without the sensor — but no real scan has been observed end to end, because
that needs a Pro device and the Simulator cannot do it.
What that means concretely: raw is safe, because it is whatever RoomPlan
encoded. The typed lists — walls, objects, dimensions, position —
are read from an encoding that has been modelled from Apple's API and unit
tested against realistic JSON, not against a scan. They are deliberately
forgiving: a field RoomPlan renames leaves them thinner rather than throwing,
and the value is still in raw.
If you run a real scan, please open an issue with the raw JSON either way.
That is the one thing that would move this from tested to verified.
MIT © K M Shahriar Hossain