scannerpro 3.0.0 copy "scannerpro: ^3.0.0" to clipboard
scannerpro: ^3.0.0 copied to clipboard

Universal Flutter scanning SDK with 18 Vision AI modes, offline OCR, MICR/MRZ/VIN parsers, image compression, encrypted storage, cloud sync, and PDF export.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:scannerpro/scannerpro.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setSystemUIOverlayStyle(
    const SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
      statusBarIconBrightness: Brightness.light,
    ),
  );
  runApp(const ScannerExampleApp());
}

/// Universal Scanner Pro Feature Showcase Application.
class ScannerExampleApp extends StatelessWidget {
  const ScannerExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ScannerPro Showcase Studio',
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: const Color(0xFF090D12),
        primaryColor: const Color(0xFF00E5FF),
        colorScheme: const ColorScheme.dark(
          primary: Color(0xFF00E5FF),
          secondary: Color(0xFFFFD600),
          surface: Color(0xFF161B22),
        ),
      ),
      home: const MainShowcaseDashboard(),
    );
  }
}

class MainShowcaseDashboard extends StatefulWidget {
  const MainShowcaseDashboard({super.key});

  @override
  State<MainShowcaseDashboard> createState() => _MainShowcaseDashboardState();
}

class _MainShowcaseDashboardState extends State<MainShowcaseDashboard> {
  int _currentTabIndex = 0;
  late ScannerController _controller;
  ScanResult? _latestResult;
  final List<ScanResult> _sessionHistory = [];

  @override
  void initState() {
    super.initState();
    _controller = ScannerController(
      initialMode: ScanMode.qr,
      options: const ScannerOptions(
        scanStrategy: ScanStrategy.continuous,
        enableAdaptiveFps: true,
        enableIsolateProcessing: true,
        duplicateTimeout: Duration(milliseconds: 2000),
      ),
      onResultDetected: (result) {
        setState(() {
          _latestResult = result;
          _sessionHistory.insert(0, result);
          if (_sessionHistory.length > 100) {
            _sessionHistory.removeLast();
          }
        });
      },
    );
    _controller.initialize();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final tabs = [
      _buildLiveScannerTab(),
      _buildDocumentStudioTab(),
      _buildIdParsersTab(),
      _buildBatchWarehouseTab(),
      _buildBenchmarkTelemetryTab(),
    ];

    return Scaffold(
      appBar: AppBar(
        backgroundColor: const Color(0xFF161B22),
        elevation: 0,
        title: Row(
          children: [
            Container(
              padding: const EdgeInsets.all(6),
              decoration: BoxDecoration(
                color: const Color(0xFF00E5FF).withValues(alpha: 0.15),
                borderRadius: BorderRadius.circular(8),
              ),
              child: const Icon(Icons.qr_code_scanner_rounded, color: Color(0xFF00E5FF)),
            ),
            const SizedBox(width: 10),
            Text(
              'ScannerPro v$scannerProVersion',
              style: TextStyle(
                fontWeight: FontWeight.bold,
                fontSize: 18,
                letterSpacing: 0.5,
              ),
            ),
          ],
        ),
        actions: [
          IconButton(
            tooltip: 'Toggle Flashlight',
            icon: Icon(
              _controller.isFlashOn ? Icons.flash_on_rounded : Icons.flash_off_rounded,
              color: _controller.isFlashOn ? const Color(0xFFFFD600) : Colors.white70,
            ),
            onPressed: () => _controller.toggleFlash(),
          ),
          IconButton(
            tooltip: 'Switch Camera',
            icon: const Icon(Icons.flip_camera_ios_rounded),
            onPressed: () => _controller.switchCamera(),
          ),
          IconButton(
            tooltip: 'Gallery Image Scan',
            icon: const Icon(Icons.photo_library_rounded),
            onPressed: () => _controller.pickAndScanImage(),
          ),
        ],
      ),
      body: tabs[_currentTabIndex],
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentTabIndex,
        onTap: (index) => setState(() => _currentTabIndex = index),
        backgroundColor: const Color(0xFF161B22),
        selectedItemColor: const Color(0xFF00E5FF),
        unselectedItemColor: Colors.white54,
        type: BottomNavigationBarType.fixed,
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.camera_alt_rounded),
            label: 'Scanner',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.description_rounded),
            label: 'Document',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.badge_rounded),
            label: 'ID Cards',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.inventory_2_rounded),
            label: 'Batch',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.speed_rounded),
            label: 'Telemetry',
          ),
        ],
      ),
    );
  }

  // --- TAB 1: Live Scanner & Reticle ---
  Widget _buildLiveScannerTab() {
    return ListenableBuilder(
      listenable: _controller,
      builder: (context, _) {
        return Stack(
          fit: StackFit.expand,
          children: [
            // Camera Preview
            ScannerCameraPreview(controller: _controller),

            // Mode Selector Bar (Top)
            Positioned(
              top: 12,
              left: 12,
              right: 12,
              child: SingleChildScrollView(
                scrollDirection: Axis.horizontal,
                child: Row(
                  children: ScanMode.values.map((mode) {
                    final isSelected = _controller.selectedMode == mode;
                    return Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 4),
                      child: ChoiceChip(
                        selected: isSelected,
                        label: Row(
                          children: [
                            Icon(
                              mode.icon,
                              size: 16,
                              color: isSelected ? Colors.black : Colors.white,
                            ),
                            const SizedBox(width: 6),
                            Text(mode.title),
                          ],
                        ),
                        selectedColor: const Color(0xFF00E5FF),
                        backgroundColor: const Color(0xFF161B22).withValues(alpha: 0.85),
                        labelStyle: TextStyle(
                          color: isSelected ? Colors.black : Colors.white,
                          fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
                          fontSize: 12,
                        ),
                        onSelected: (_) => _controller.setMode(mode),
                      ),
                    );
                  }).toList(),
                ),
              ),
            ),

            // Target Overlay Painter
            CustomPaint(
              painter: ScannerOverlayPainter(
                scanMode: _controller.selectedMode,
                animationValue: 0.5,
                accentColor: const Color(0xFF00E5FF),
              ),
            ),

            // Bottom Floating Controls & Result Card
            Positioned(
              bottom: 16,
              left: 16,
              right: 16,
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  if (_latestResult != null) _buildResultCard(_latestResult!),
                  const SizedBox(height: 8),
                  Container(
                    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                    decoration: BoxDecoration(
                      color: const Color(0xFF161B22).withValues(alpha: 0.9),
                      borderRadius: BorderRadius.circular(16),
                      border: Border.all(color: Colors.white12),
                    ),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        Row(
                          children: [
                            const Icon(Icons.zoom_in_rounded, size: 20, color: Color(0xFF00E5FF)),
                            const SizedBox(width: 8),
                            Text(
                              'Zoom: ${_controller.currentZoomLevel.toStringAsFixed(1)}x',
                              style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13),
                            ),
                          ],
                        ),
                        Expanded(
                          child: Slider(
                            value: _controller.currentZoomLevel,
                            min: _controller.minZoomLevel,
                            max: _controller.maxZoomLevel,
                            activeColor: const Color(0xFF00E5FF),
                            onChanged: (val) => _controller.setZoomLevel(val),
                          ),
                        ),
                        IconButton(
                          icon: Icon(
                            _controller.isPaused ? Icons.play_arrow_rounded : Icons.pause_rounded,
                            color: const Color(0xFFFFD600),
                          ),
                          onPressed: () {
                            if (_controller.isPaused) {
                              _controller.resumeScanning();
                            } else {
                              _controller.pauseScanning();
                            }
                          },
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ],
        );
      },
    );
  }

  Widget _buildResultCard(ScanResult result) {
    final validation = ScannerPro.validateResult(result);
    return Card(
      color: const Color(0xFF161B22),
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
        side: BorderSide(
          color: validation.isValid ? Colors.greenAccent : Colors.redAccent,
          width: 1.5,
        ),
      ),
      child: ListTile(
        leading: Icon(
          validation.isValid ? Icons.check_circle_rounded : Icons.error_rounded,
          color: validation.isValid ? Colors.greenAccent : Colors.redAccent,
          size: 32,
        ),
        title: Text(
          result.mode.title,
          style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
        ),
        subtitle: Text(
          result.rawValue,
          maxLines: 2,
          overflow: TextOverflow.ellipsis,
          style: const TextStyle(color: Colors.white70, fontSize: 12),
        ),
        trailing: Container(
          padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
          decoration: BoxDecoration(
            color: validation.isValid
                ? Colors.greenAccent.withValues(alpha: 0.15)
                : Colors.redAccent.withValues(alpha: 0.15),
            borderRadius: BorderRadius.circular(8),
          ),
          child: Text(
            validation.isValid ? 'VALID ✓' : 'INVALID ✗',
            style: TextStyle(
              color: validation.isValid ? Colors.greenAccent : Colors.redAccent,
              fontSize: 11,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
      ),
    );
  }

  // --- TAB 2: Document Studio & PDF Export ---
  Widget _buildDocumentStudioTab() {
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            'Document Scanner Studio',
            style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white),
          ),
          const SizedBox(height: 6),
          const Text(
            'Auto edge detection, quad perspective crop, blur score analysis, and multi-page PDF export.',
            style: TextStyle(color: Colors.white60, fontSize: 13),
          ),
          const SizedBox(height: 16),
          Expanded(
            child: Container(
              width: double.infinity,
              decoration: BoxDecoration(
                color: const Color(0xFF161B22),
                borderRadius: BorderRadius.circular(16),
                border: Border.all(color: Colors.white12),
              ),
              child: Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    const Icon(Icons.document_scanner_rounded, size: 64, color: Color(0xFF00E5FF)),
                    const SizedBox(height: 16),
                    const Text(
                      'Ready for Page Capture',
                      style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                    ),
                    const SizedBox(height: 8),
                    Text(
                      'Captured Pages: ${_sessionHistory.where((r) => r.mode == ScanMode.document).length}',
                      style: const TextStyle(color: Colors.white60),
                    ),
                    const SizedBox(height: 24),
                    ElevatedButton.icon(
                      style: ElevatedButton.styleFrom(
                        backgroundColor: const Color(0xFF00E5FF),
                        foregroundColor: Colors.black,
                        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
                        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
                      ),
                      icon: const Icon(Icons.picture_as_pdf_rounded),
                      label: const Text('Export Document Session to PDF', style: TextStyle(fontWeight: FontWeight.bold)),
                      onPressed: () {
                        final docs = _sessionHistory.where((r) => r.mode == ScanMode.document).toList();
                        ScaffoldMessenger.of(context).showSnackBar(
                          SnackBar(
                            content: Text(
                              docs.isNotEmpty
                                  ? 'PDF generated with ${docs.length} scanned pages ✓'
                                  : 'Scan document pages first in camera tab!',
                            ),
                          ),
                        );
                      },
                    ),
                  ],
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }

  // --- TAB 3: ID Parsers Demo ---
  Widget _buildIdParsersTab() {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        const Text(
          'Identity Document Parsers',
          style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white),
        ),
        const SizedBox(height: 4),
        const Text(
          'Instant rule-based parsing with Verhoeff & checksum validation.',
          style: TextStyle(color: Colors.white60, fontSize: 13),
        ),
        const SizedBox(height: 16),
        _buildParserCard(
          title: 'Indian Aadhaar Card (UIDAI)',
          icon: Icons.credit_card_rounded,
          samplePayload: '2345 6789 0124 DOB: 15/08/1990 Male Pincode: 560001',
          onTest: () {
            final res = ScannerPro.scanAadhaar('2345 6789 0124 DOB: 15/08/1990 Male');
            _showParserDialog('Aadhaar Result', res);
          },
        ),
        _buildParserCard(
          title: 'Income Tax PAN Card',
          icon: Icons.subtitles_rounded,
          samplePayload: 'ABCPE1234F Name: JOHN DOE DOB: 01/01/1985',
          onTest: () {
            final res = ScannerPro.scanPanCard('ABCPE1234F Name: JOHN DOE DOB: 01/01/1985');
            _showParserDialog('PAN Card Result', res);
          },
        ),
        _buildParserCard(
          title: 'ICAO Passport (MRZ)',
          icon: Icons.badge_rounded,
          samplePayload: 'P<INDXAVIER<<FRANCIS<<<<<<<<<<<<<<<<<<<<<<\nZ1234567<8IND9008151M3001017<<<<<<<<<<<<<<04',
          onTest: () {
            final res = ScannerPro.scanPassport('P<INDXAVIER<<FRANCIS<<<<<<<<<<<<<<<<<<<<<<\nZ1234567<8IND9008151M3001017<<<<<<<<<<<<<<04');
            _showParserDialog('Passport Result', res);
          },
        ),
        _buildParserCard(
          title: 'ISO 3779 VIN Number',
          icon: Icons.minor_crash_rounded,
          samplePayload: '1HGCR2F83HA000000',
          onTest: () {
            final res = ScannerPro.scanVin('1HGCR2F83HA000000');
            _showParserDialog('VIN Result', res);
          },
        ),
        _buildParserCard(
          title: 'Business Card Contact',
          icon: Icons.contact_page_rounded,
          samplePayload: 'Francis Xavier\nCEO\nTech Corp\nEmail: info@tech.com\nPhone: +1234567890',
          onTest: () {
            final res = ScannerPro.scanBusinessCard('Francis Xavier\nCEO\nTech Corp\nEmail: info@tech.com\nPhone: +1234567890');
            _showParserDialog('Business Card Result', res);
          },
        ),
      ],
    );
  }

  Widget _buildParserCard({
    required String title,
    required IconData icon,
    required String samplePayload,
    required VoidCallback onTest,
  }) {
    return Card(
      color: const Color(0xFF161B22),
      margin: const EdgeInsets.only(bottom: 12),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: ListTile(
        leading: Icon(icon, color: const Color(0xFF00E5FF)),
        title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
        subtitle: Text(samplePayload, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(color: Colors.white54, fontSize: 11)),
        trailing: ElevatedButton(
          style: ElevatedButton.styleFrom(
            backgroundColor: const Color(0xFF00E5FF).withValues(alpha: 0.2),
            foregroundColor: const Color(0xFF00E5FF),
          ),
          onPressed: onTest,
          child: const Text('Test Parser'),
        ),
      ),
    );
  }

  void _showParserDialog(String title, ScanResult result) {
    final validation = ScannerPro.validateResult(result);
    showDialog(
      context: context,
      builder: (ctx) => AlertDialog(
        backgroundColor: const Color(0xFF161B22),
        title: Text(title, style: const TextStyle(color: Colors.white)),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('Validation: ${validation.reason}', style: TextStyle(color: validation.isValid ? Colors.greenAccent : Colors.redAccent, fontWeight: FontWeight.bold)),
            const SizedBox(height: 12),
            ...result.fields.entries.map((e) => Padding(
                  padding: const EdgeInsets.symmetric(vertical: 2),
                  child: Text('${e.key}: ${e.value}', style: const TextStyle(fontSize: 12, color: Colors.white70)),
                )),
          ],
        ),
        actions: [
          TextButton(onPressed: () => Navigator.pop(ctx), child: const Text('Close')),
        ],
      ),
    );
  }

  // --- TAB 4: Batch Warehouse ---
  Widget _buildBatchWarehouseTab() {
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              const Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('Batch Inventory Scanner', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white)),
                  Text('High-speed continuous scan with deduplication', style: TextStyle(color: Colors.white60, fontSize: 12)),
                ],
              ),
              Container(
                padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
                decoration: BoxDecoration(
                  color: const Color(0xFFFFD600).withValues(alpha: 0.2),
                  borderRadius: BorderRadius.circular(12),
                ),
                child: Text('Count: ${_sessionHistory.length}', style: const TextStyle(color: Color(0xFFFFD600), fontWeight: FontWeight.bold)),
              ),
            ],
          ),
          const SizedBox(height: 16),
          Expanded(
            child: _sessionHistory.isEmpty
                ? const Center(child: Text('No items scanned in current session', style: TextStyle(color: Colors.white38)))
                : ListView.builder(
                    itemCount: _sessionHistory.length,
                    itemBuilder: (ctx, idx) {
                      final item = _sessionHistory[idx];
                      return ListTile(
                        leading: const Icon(Icons.qr_code_2_rounded, color: Color(0xFF00E5FF)),
                        title: Text(item.rawValue, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold)),
                        subtitle: Text('${item.mode.title} • ${item.timestamp.toIso8601String().substring(11, 19)}', style: const TextStyle(fontSize: 11, color: Colors.white54)),
                      );
                    },
                  ),
          ),
        ],
      ),
    );
  }

  // --- TAB 5: Benchmark & Telemetry ---
  Widget _buildBenchmarkTelemetryTab() {
    final telemetry = ScannerBenchmark.runLiveDiagnostic();

    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        const Text('Performance Telemetry', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white)),
        const SizedBox(height: 4),
        const Text('Real-time frame processing stats & hardware benchmarks.', style: TextStyle(color: Colors.white60, fontSize: 13)),
        const SizedBox(height: 16),
        Row(
          children: [
            _buildStatCard('Live FPS', '${_controller.fpsState.targetFps} FPS', Icons.speed_rounded, Colors.greenAccent),
            const SizedBox(width: 12),
            _buildStatCard('Latency', '${telemetry['Average Latency µs']} µs', Icons.timer_rounded, const Color(0xFF00E5FF)),
          ],
        ),
        const SizedBox(height: 12),
        Row(
          children: [
            _buildStatCard('Memory', telemetry['Memory Footprint'], Icons.memory_rounded, const Color(0xFFFFD600)),
            const SizedBox(width: 12),
            _buildStatCard('CPU Load', telemetry['Estimated CPU Usage'], Icons.developer_board_rounded, Colors.purpleAccent),
          ],
        ),
        const SizedBox(height: 24),
        const Text('Device Benchmark Standards', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white)),
        const SizedBox(height: 8),
        Container(
          decoration: BoxDecoration(
            color: const Color(0xFF161B22),
            borderRadius: BorderRadius.circular(12),
            border: Border.all(color: Colors.white12),
          ),
          child: DataTable(
            columns: const [
              DataColumn(label: Text('Device', style: TextStyle(fontWeight: FontWeight.bold))),
              DataColumn(label: Text('FPS', style: TextStyle(fontWeight: FontWeight.bold))),
              DataColumn(label: Text('Memory', style: TextStyle(fontWeight: FontWeight.bold))),
              DataColumn(label: Text('CPU', style: TextStyle(fontWeight: FontWeight.bold))),
            ],
            rows: ScannerBenchmark.getSampleDeviceMetrics()
                .map((m) => DataRow(cells: [
                      DataCell(Text(m['Device'] ?? '')),
                      DataCell(Text('${m['FPS']}')),
                      DataCell(Text('${m['Memory MB']} MB')),
                      DataCell(Text('${m['CPU %']}%')),
                    ]))
                .toList(),
          ),
        ),
      ],
    );
  }

  Widget _buildStatCard(String label, String value, IconData icon, Color color) {
    return Expanded(
      child: Container(
        padding: const EdgeInsets.all(16),
        decoration: BoxDecoration(
          color: const Color(0xFF161B22),
          borderRadius: BorderRadius.circular(12),
          border: Border.all(color: color.withValues(alpha: 0.3)),
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Icon(icon, color: color, size: 24),
            const SizedBox(height: 8),
            Text(value, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: color)),
            Text(label, style: const TextStyle(fontSize: 11, color: Colors.white54)),
          ],
        ),
      ),
    );
  }
}
1
likes
160
points
1.07k
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Universal Flutter scanning SDK with 18 Vision AI modes, offline OCR, MICR/MRZ/VIN parsers, image compression, encrypted storage, cloud sync, and PDF export.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

camera, flutter, google_mlkit_barcode_scanning, google_mlkit_face_detection, google_mlkit_text_recognition, image_picker, intl

More

Packages that depend on scannerpro