smart_qr_scanner 1.6.0
smart_qr_scanner: ^1.6.0 copied to clipboard
A production-ready Flutter package for real-time QR code and barcode scanning using Google ML Kit with beautiful modern UI, animations, and advanced features.
1.6.0 #
New Features #
Holographic QR Overlay (HolographicQrOverlay)
- New widget in the example app — renders the scanned QR code as a floating hologram immediately after a successful scan
- Entrance animation built with
TweenSequence: scale bursts from0.05×→1.18×→0.94×→1.0×(small-to-big succession with overshoot settle) - Opacity fades in during the first 25 % of the entrance so the scale growth is fully visible
- Continuous 3D tilt oscillation via
Matrix4perspective transform (rotateX± 0.30 rad,rotateY± 0.22 rad, perspective 0.002) — hologram appears to float in space - Entrance tilt: starts at 0.55 rad forward-lean and settles to 0 as the card rises into view
- Float animation: ±6 px vertical translation on a 2800 ms sine loop
- Slide-up entrance: card rises from +40 px below final position
- Cyan sweep scan line rendered by an isolated
_SweepPainter(CustomPainter) — only the scan line repaints per tick, not the QR widget ClipRRectwithborderRadius: 10clips QR edges cleanlyRepaintBoundarywraps the card so tilt/float repaints are isolated from the rest of the treeonDismissis optional — whennullthe parent (SmartScannerWidget) controls removal via_showSuccess- Auto-dismiss with configurable
autoDismissAfterduration whenonDismissis provided
ScanSuccessStyle.pixelBurst
- New enum value on
SmartScannerWidget.successStyle; selects the Paytm-style white-pixel burst animation in place of the default ripple check-mark
successOverlayBuilder parameter
Widget Function(SmartScanResult)?— renders any widget behind the success animation at the moment of scan- Z-order: dark scrim (7a) → overlay (7b) → pixel burst (7c) — pixels always render on top
onScanAnimationComplete callback
- Fires after the success animation completes (pixel burst at t = 0.70); use this for navigation instead of
onScanso the animation is never cut short by the screen being disposed
successLogo / successLogoSize parameters
- Optional
Widget?centered inside the pixel burst; fades out in the second half of the animation
Delayed pixel burst (_showBurst flag)
- Hologram materialises first (0 ms); pixel burst starts 600 ms later so pixels visually emerge from the fully-formed hologram
Camera auto-pause on scan
controller.pause()called the moment a scan is confirmed;controller.resume()called insideonCompleteafter animation ends — camera feed freezes during the success animation
Dark scrim on scan success
- Semi-transparent black overlay (
Colors.black.withAlpha(180)) fades in (300 ms) behind the hologram so white QR modules are legible against any camera background
Improvements #
Scan UI auto-hide
- Scanner overlay (corner brackets + animated scan line), status label, and hint text are hidden as soon as
_detectingbecomestruewhensuccessStyle == pixelBurst, not only after_showSuccess— the frame disappears the instant a code is first detected - All three items are restored when
_showSuccessreturns tofalse
Pixel Burst — performance overhaul
saveLayer/ImageFilter.blurremoved entirely — all blocks drawn directly viacanvas.drawRect; no GPU-layer allocation per frame- Single
Paintobject (_p) reused across every block every frame — eliminates ~15 600Paintallocations per second at 60 fps with 260 blocks - Flash shockwave also reuses
_p(withmaskFilterreset between circles) _Cmdbucket class and_drawBlurredhelper removed (no longer needed)dart:uiimport removed- Block count reduced from 520 → 260
RepaintBoundarywraps both the pixel burst and the holographic overlay — isolates their repaint from the rest of the scanner widget tree
Pixel Burst — animation quality
fadeStartrange0.68–0.90gives a long, gradual fade tailonCompletefires att ≥ 0.70(blur phase start) for snappy navigation timing while animation still plays to completion naturally
Holographic overlay — performance
_sweepCtrl(1600 msrepeat) drives only its ownAnimatedBuilder; the QR widget and tilt/float tree are not rebuilt on sweep ticks- Outer
AnimatedBuilderusesListenable.merge([_entranceCtrl, _floatCtrl, _tiltCtrl])and passes the card as a staticchild:— card widget tree is never rebuilt by animation ticks
Removals #
- "Scanning paused" overlay and pause icon removed from the scanner UI — the paused state is now fully silent
Bug Fixes #
_StatusLabelno longer receives apaused:parameter after the paused state was removed — the stale call-site argument is cleaned up
1.5.0 #
Bug Fixes #
- QR codes not scannable —
QrViewwas usingPrettyQrSmoothSymbolwhich renders smooth/curved connections between modules that confuse QR readers. Switched toPrettyQrSquaresSymbol(standard sharp square modules) and addedPrettyQrQuietZone.standard(mandatory 4-module quiet zone); generated codes are now reliably scanned by all standard QR readers.
Removals #
share_plusremoved — all sharing UI and logic removed from the main package and example app.HistoryExporter.exportCsv()now writes the CSV to the temporary directory and returnstrue/false; it no longer opens the system share sheet. Share buttons removed from the result screen and generator screen in the example app.QrPainter/buildQrImage()removed — custom painter replaced byPrettyQrViewfrompretty_qr_code: ^3.6.0.QrViewnow delegates toPrettyQrView.data()withPrettyQrSquaresSymbolfor scannable output.
Dependencies #
| Change | Package | Details |
|---|---|---|
| Added | pretty_qr_code |
^3.6.0 — replaces custom QrPainter; provides PrettyQrView for QR rendering |
| Removed | qr |
Replaced by pretty_qr_code |
| Removed | share_plus |
Sharing functionality removed from package |
1.4.0 #
Improvements #
- README updated with full feature documentation, platform setup guide, API reference, themes, troubleshooting, and performance tips
1.3.0 #
New Features #
QR Code Generator
- Added
QrGeneratorWidget— a full-featured QR code creator with no external rendering dependency - Supports 6 input types: URL, plain text, WiFi credentials (WPA/WEP/None), email, phone number, and vCard contact
- Custom
QrPainter(CustomPainter) renders the complete QR matrix including the mandatory 4-module quiet zone on all sides — generated codes are now reliably scannable by all standard readers QrViewwidget wrapsQrPainterfor simple drop-in usagebuildQrImage(String data) → QrImage?top-level helper for programmatic access- Accent-coloured finder patterns (eye regions); data modules rendered in solid black for maximum contrast
- Removed
qr_flutterdependency; uses the lighterqr: ^3.0.0package directly
Image Capture & Sharing
RepaintBoundary+RenderRepaintBoundary.toImage(pixelRatio: 3.0)captures the QR widget at 3× resolution as PNG bytes- Save generated QR codes to the device photo gallery via the
galpackage (Gal.putImageBytes) - Share generated QR codes as a PNG image with a date/time subject line via
share_plus(Share.shareXFiles)
Persistent Storage
- Added
StorageService— thin SharedPreferences wrapper for JSON-based persistence HistoryServicenow persists viaStorageService; history is restored on every app launchSmartScanResult.toJson()/SmartScanResult.fromJson()— full round-trip serialization of all fields including enums (via index),DateTime,Rect, andList<Offset>
Favorites
- Added
FavoritesService— persists favoritedrawValuestrings viaStorageService isFavorite(String rawValue),toggle(String rawValue),loadAll()API- Added
FavoriteButtonwidget — drop-in animated bookmark button with a scale animation (1.0 → 1.35) on toggle; acceptsactiveColoroverride
History Export
- Added
HistoryExporter.exportCsv(List<SmartScanResult>)— generates a UTF-8 CSV file with columns:Timestamp,Format,Type,Raw Value,Display Value,Confidence - Writes to
getTemporaryDirectory()and opens the system share sheet viashare_plus
URL Handler
- Added
SmartUrlHandlerutility with three methods:canHandle(SmartScanResult)— returnstruefor URL, email, phone, SMS, geo, and raw http/https valuesactionLabel(SmartScanResult)— returns a human-friendly button labellaunch(SmartScanResult)— opens the appropriate system handler viaurl_launcher
Haptic Patterns
- Added
HapticPatternclass with predefined patterns:short,medium,long,doubleShort,tripleShort HapticPattern.forType(BarcodeType)returns a pattern tailored to each barcode type (e.g., tripleShort for WiFi, doubleShort for email/phone, short for plain URL)FeedbackService.scanSuccessnow accepts an optionalbarcodeTypeparameter and applies the matching pattern viaVibration.vibrate(pattern: [...])
Duplicate Toast
- Added
DuplicateToastOverlaywidget — wraps any child in aStackand exposesDuplicateToastOverlay.show(context, {String? message}) - Toast fades in (
FadeTransition), displays for 2 seconds, then fades out automatically
Instant Camera Open
ScannerScreennow accepts an optionalSmartQrScannerController? controllerparameter- When a pre-warmed controller is passed in,
initialize()is not called again — the camera init overlaps with the route transition animation, eliminating the black-screen delay on open - Navigation transition shortened to 280 ms (from 420 ms) with a matching 220 ms reverse
Bug Fixes #
- Generated QR codes not scannable —
QrPainterwas rendering the QR matrix edge-to-edge with no quiet zone; fixed by calculatingoffset = quietZone * moduleSizeand shifting all module rects by that amount. Additionally removedClipRRect(borderRadius: 12)from the capture boundary which was physically clipping the corner finder patterns - Data module opacity — changed
dataColordefault fromColor(0xDD000000)(87% opacity) toColors.blackfor maximum contrast and reliable detection - Android manifest merger conflict —
WRITE_EXTERNAL_STORAGE@maxSdkVersionconflict withcamera_android_camerax(value 28) fixed by addingxmlns:toolsnamespace andtools:replace="android:maxSdkVersion"to the permission element - iOS Simulator camera —
availableCameras()returns an empty list on the iOS Simulator (no physical camera hardware). The controller now detectsSIMULATOR_DEVICE_NAMEinPlatform.environmentand sets a sentinel error'__ios_simulator__';SmartScannerWidgetintercepts this and renders a dedicated teal info screen ("iOS Simulator — run on a real device") instead of the generic red error state DarwinAudioErrorcrash on dispose —audioplayersremoved entirely from the package.FeedbackService.dispose()is now a synchronous void method with no audio player teardown;FeedbackService.scanSuccess()no longer has asoundparameter
Example App #
- Complete UI redesign with a light theme (teal/cyan gradient
#00BCD4 → #006064, white/off-white#F0F7FFbackgrounds) - Floating pill-style bottom navigation bar (4 tabs: Scan, History, Generate, Settings) implemented with
IndexedStack - Scan tab: gradient hero card, 6-mode feature grid, theme row, recent scans list
- History tab: inline (no separate screen push), search, swipe-to-delete, CSV export, clear confirmation sheet
- Generate tab:
QrGeneratorWidgetembedded in a white card with teal accent - Settings tab: section cards with toggle rows and scan mode segmented control
ResultScreenredesigned to light theme — teal gradient rounded header, white data cards, teal primary buttons, white secondary buttons side-by-sideFavoriteButtonadded toResultScreenAppBar and every history tile- "Open URL / Call / Send Email" contextual action button in
ResultScreenpowered bySmartUrlHandler - Share button in
ResultScreenusesShare.share(rawValue)directly (was showing a SnackBar placeholder)
Dependencies Added #
| Package | Version | Purpose |
|---|---|---|
gal |
^2.3.0 |
Save QR images to device gallery |
path_provider |
^2.1.0 |
Temporary directory for share file |
qr |
^3.0.0 |
QR matrix generation (replaces qr_flutter) |
Dependencies Removed #
| Package | Reason |
|---|---|
qr_flutter |
Replaced by a custom QrPainter using qr directly — removes Flutter rendering constraints and gives full control over quiet zone and module styling |
audioplayers |
Caused DarwinAudioError crash on iOS dispose. Feedback is now vibration-only via the vibration package; FeedbackService no longer plays audio |
1.0.0 #
Initial Release #
Core Scanning
- Real-time QR code and barcode scanning powered by Google ML Kit
- Supports 13 barcode formats: QR Code, Aztec, Codabar, Code 39/93/128, Data Matrix, EAN-8/13, ITF, PDF417, UPC-A/E
- Single-scan and continuous-scan modes (
ScanMode.single/ScanMode.continuous) - Configurable scan area with
scanAreaWidthFactorandscanAreaHeightFactor - Frame throttling (
framesToSkip) to reduce CPU/battery usage - Scan-area filtering — only barcodes whose centre falls inside the scan window are accepted
Gallery Scanning
scanFromGallery()onSmartQrScannerController— pick any image from the device gallery and extract barcodes via ML Kit- Fires
onScanon success; firesonErrorif no valid code is found in the image - Adds result to scan history and triggers haptic/audio feedback like a live scan
- Requires
NSPhotoLibraryUsageDescriptionin iOSInfo.plist
Camera
- Front/back camera switch via
switchCamera()— engine tracks_currentFacingseparately from the immutable config to allow mid-session toggling - Smooth camera switch: sets
isSwitching = trueand renders a black placeholder before disposing the oldCameraController, preventing thebuildPreview() on disposed CameraControllercrash - Fade-in animation (
TweenAnimationBuilder) when the new camera preview appears after switching - Flash toggle via
toggleFlash(); flash automatically disabled when using the front camera - Auto-focus mode configurable via
ScannerConfig.enableAutoFocus
Controller API
SmartQrScannerControllerextendsChangeNotifier— use withListenableBuilderoraddListenerscanEvents— broadcastStream<SmartScanResult>for every new non-duplicate resultrawScanEvents— broadcastStream<List<SmartScanResult>>for every raw ML Kit batchonScan,onRawScan,onTimeout,onError— simple callback APIscanOnce()—Future<SmartScanResult>for single-scan flow without callbackspause()/resume()— stop/start frame processing without closing the cameraclearHistory()— reset the in-memory scan logisSwitchinggetter — true while the camera is being swapped
UI — SmartScannerWidget
showControls,showHint,showFlash,showGallery,showFlip,showMenu— granular visibility togglesonThemeChanged— callback fired when the user picks a new themeSmartScannerWidgetStateis public — useGlobalKey<SmartScannerWidgetState>to callshowThemePicker()from an external button
Themes
- Three built-in themes:
ScannerTheme.neon,ScannerTheme.light,ScannerTheme.minimal - Fully custom theme via
ScannerTheme(...)withcopyWith()support - Theme picker bottom sheet — drag handle, palette icon header, animated selection rows, checkmark indicator
Loading Screen
- Animated loader: glowing corner brackets, sweep scan line with glow, pulsing QR icon,
PREPARING SCANNERlabel, sequential 3-dot indicator
Transitions
- Scanner screen: slide-up-from-bottom (
easeOutCubic, 420 ms) + fast fade-in over first 40% of the transition - Result screen: fade animation (400 ms)
Feedback & Accessibility
- Haptic vibration and audio beep on successful scan
- Duplicate prevention with configurable time window
- Configurable scan timeout with
onTimeoutcallback - In-memory scan history with configurable capacity
- Lifecycle-aware: auto-pauses on
AppLifecycleState.paused, resumes onAppLifecycleState.resumed - Built-in camera permission request flow with settings deep-link on permanent denial
Platform Setup
- Android:
minSdkVersion 21, ML Kitbarcode_uimeta-data,CAMERApermission - iOS:
NSCameraUsageDescriptionandNSPhotoLibraryUsageDescriptioninInfo.plist,platform :ios, '14.0'