vehicle_frame_capture 4.3.0
vehicle_frame_capture: ^4.3.0 copied to clipboard
A guided, multi-angle vehicle photo capture flow: a rectangular frame overlay, live device-level feedback, and camera integration in one widget.
vehicle_frame_capture #
A guided, multi-angle vehicle photo capture flow for Flutter apps ā a live camera preview with a rectangular framing guide that turns green when the device is held level, walking the user through every angle a vehicle listing typically needs.
It's deliberately unopinionated: no review UI, colors, fonts, or step list are forced on you ā every one of them is a parameter with a sensible default.
Features #
- šø Configurable capture steps: defaults to 12 angles (6 exterior + 6 interior, see
VehicleSide.defaultValues), or pass your own subset/order ā including any of the ~35 additional angles inVehicleSide.catalog, or fully custom ones viaVehicleSide.custom(...) - šļø Categorized: every angle is tagged
exteriororinteriorviaVehicleCaptureCategory, for tabbed capture UIs - šÆ Rectangular frame overlay: a simple, universal framing guide used across every step
- š± Device leveling: accelerometer-driven level detection, visualized with a two-axis crosshair/bubble-level indicator, with tunable tolerance ā and skippable per angle (
VehicleSide.requiresLevel) for shots that are inherently tilted up/down (roof, engine bay, wheels, ...) - šØ Themeable: colors and text styles are all overridable via
VehicleCaptureTheme; text falls back to your app's ambientThemewhen not overridden ā no font package is forced on you - š Capture flow management: automatic progression, with
onPhotoCaptured/onStepChangedcallbacks to react live - š¼ļø Review UI is optional: returns the captured images directly by default; opt into the bundled review screen with
showSummary: trueonly if you want it - š· Camera controls: pick front/back camera and resolution preset
- ā” Self-contained: locks to landscape while active and restores portrait on exit ā no setup required from the calling app
Installation #
Add this to your app's pubspec.yaml:
dependencies:
vehicle_frame_capture: ^4.0.0
Platform setup #
This package uses the camera and sensors_plus plugins, which need camera permissions declared in your app:
Android (android/app/src/main/AndroidManifest.xml):
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
iOS (ios/Runner/Info.plist):
<key>NSCameraUsageDescription</key>
<string>Camera access is needed to capture vehicle photos.</string>
Usage #
Bare minimum ā no review screen, full default step list #
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:vehicle_frame_capture/vehicle_frame_capture.dart';
Future<void> startCapture(BuildContext context) async {
final images = await Navigator.push<List<File>>(
context,
MaterialPageRoute(builder: (context) => const CameraScreen()),
);
if (images != null) {
// images.length == 12, one per VehicleSide.defaultValues, in flow order.
// Build your own review/upload UI with these ā CameraScreen doesn't
// show one unless you ask it to (see below).
}
}
With the bundled review screen, a custom step list, and theming #
final images = await Navigator.push<List<File>>(
context,
MaterialPageRoute(
builder: (context) => CameraScreen(
showSummary: true, // shows the bundled SummaryScreen before returning
steps: const [
VehicleSide.front,
VehicleSide.frontRight,
VehicleSide.rearRight,
VehicleSide.rear,
],
theme: const VehicleCaptureTheme(
readyColor: Colors.orangeAccent,
primaryColor: Colors.deepPurple,
),
resolutionPreset: ResolutionPreset.high,
preferredLensDirection: CameraLensDirection.back,
onStepChanged: (side, index, total) =>
debugPrint('Step ${index + 1}/$total: ${side.label}'),
onPhotoCaptured: (side, file) =>
debugPrint('Captured ${side.label} -> ${file.path}'),
),
),
);
CameraScreen handles the entire flow internally ā camera preview, leveling, the frame overlay, and per-step progression ā and returns the captured List<File> (or null if the user backs out) via the standard Navigator.pop result.
Picking angles from the full catalog, or adding a custom one #
CameraScreen(
steps: [
...VehicleSide.defaultExterior,
VehicleSide.wheelFrontLeft, // from the extended catalog
VehicleSide.vinPlate,
VehicleSide.custom( // or define your own
label: 'Spare Tire',
instruction: 'Open the trunk and capture the spare tire compartment',
category: VehicleCaptureCategory.interior,
requiresLevel: false,
),
],
)
Components #
CameraScreen #
The capture flow as a single, self-contained screen. Push it and await its result.
| Parameter | Default | Purpose |
|---|---|---|
steps |
VehicleSide.defaultValues (6 exterior + 6 interior) |
Which angles to capture, in order |
theme |
VehicleCaptureTheme() |
Colors and text styles |
showSummary |
false |
Show the bundled SummaryScreen before returning, instead of returning immediately |
resolutionPreset |
ResolutionPreset.max |
Camera capture quality (highest the device supports) |
preferredLensDirection |
CameraLensDirection.back |
Which camera to use (falls back to the first available) |
levelYTolerance / levelZTolerance |
2.0 / 3.0 |
How strict the "level" check is (m/s², roll/pitch) |
enableTapToFocus |
true |
Tap the preview to set focus + exposure at that point (resets each step) |
onPhotoCaptured |
null |
(VehicleSide, File) called after each photo is saved |
onStepChanged |
null |
(VehicleSide, int index, int total) called on each step transition |
Each step's VehicleSide.requiresLevel overrides the tolerance check entirely for that step ā when false, the level indicator is hidden and the shutter is always enabled, regardless of levelYTolerance/levelZTolerance.
VehicleCaptureTheme #
Colors (readyColor, idleColor, dangerColor, primaryColor, summaryBackgroundColor) and optional text styles (titleTextStyle, instructionTextStyle, labelTextStyle) ā anything left null falls back to the ambient Theme.
SummaryScreen #
The optional review grid shown when showSummary: true. Exported standalone too, in case you want to reuse it outside the main flow.
VehicleFramePainter #
The CustomPainter that draws the rectangular frame guide.
CustomPaint(
painter: VehicleFramePainter(isReady: true, readyColor: Colors.green),
size: Size.infinite,
)
CaptureFlow #
Manages capture state and progression over a configurable list of VehicleSide steps.
final captureFlow = CaptureFlow(sides: [VehicleSide.front, VehicleSide.rear]);
final currentStep = captureFlow.currentStep;
captureFlow.nextStep();
VehicleSide #
A plain class rather than an enum, so it isn't limited to a fixed set of angles:
class VehicleSide {
final String id; // stable identity (used for equality/Map keys)
final VehicleCaptureCategory category; // exterior or interior
final String label; // shown in the header and on tiles
final String instruction; // one-line guidance while active
final bool requiresLevel; // false for angles shot tilted up/down
}
VehicleSide.defaultValuesā the 12 angles (defaultExterior+defaultInterior) used when nosteps/sidesare given.VehicleSide.catalog(catalogExterior+catalogInterior) ā every built-in angle, ~35 more beyond the defaults: wheels, windshield, headlights/taillights, license plate, VIN plate, undercarriage, engine bay, instrument cluster, glove box, seat belts, cargo area, and more.VehicleSide.custom({label, instruction, category, requiresLevel, id})ā define a one-off angle not in the catalog.
VehicleCaptureCategory #
enum VehicleCaptureCategory { exterior, interior }
Groups angles for tabbed UIs ā every VehicleSide.category is one of these.
What this package does not do #
It does not run any on-device object/vehicle detection ā the "ready" (green) state reflects device level only, not image content. If you need contents-aware detection, layer it on top by watching the returned images.
License #
MIT ā see LICENSE.