rfid_zebra_reader 0.0.3 copy "rfid_zebra_reader: ^0.0.3" to clipboard
rfid_zebra_reader: ^0.0.3 copied to clipboard

PlatformAndroid

plugin to interact with Zebra RFID readers

example/lib/main.dart

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:rfid_zebra_reader/rfid_zebra_reader.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  // Just initialize - status updates will come through stream
  ZebraRfidReader.initialize();

  runApp(const MaterialApp(home: RfidReaderPage()));
}

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

  @override
  State<RfidReaderPage> createState() => _RfidReaderPageState();
}

class _RfidReaderPageState extends State<RfidReaderPage> {
  // Status - now managed by stream
  RfidStatus? _status;
  bool _isScanning = false;

  // Tags
  final List<RfidTag> _scannedTags = [];
  final Set<String> _uniqueTagIds = {};

  // Power
  int _powerLevel = 270;
  int _maxPower = 270;

  // Event subscriptions
  StreamSubscription<RfidEvent>? _eventSubscription;
  StreamSubscription<RfidStatus>? _statusSubscription;

  @override
  void initState() {
    super.initState();
    _setupEventListener();
    _setupStatusListener();
  }

  void _setupStatusListener() {
    _statusSubscription = ZebraRfidReader.statusStream.listen(
      (status) {
        setState(() {
          _status = status;
          _maxPower = status.maxPower;

          // Update power level when first connected
          if (status.isReady && _powerLevel == 270) {
            _powerLevel = status.maxPower;
          }
        });

        // Show user feedback for important state changes
        if (status.isReady && (_status == null || !_status!.isReady)) {
          _showSnackBar('Connected to ${status.readerName}');
          AppLogger().info('Ready: ${status.readerName}', source: 'UI');
        } else if (status.hasError) {
          _showSnackBar(status.error!);
          AppLogger().error('Status error: ${status.error}', source: 'UI');
        }
      },
      onError: (error) {
        _showSnackBar('Status stream error: $error');
        AppLogger().error('Status stream error', error: error);
      },
    );
  }

  void _setupEventListener() {
    _eventSubscription = ZebraRfidReader.eventStream.listen(
      _handleRfidEvent,
      onError: (error) {
        _showSnackBar('Event error: $error');
        AppLogger().error('Event stream error', error: error);
      },
    );
  }

  void _handleRfidEvent(RfidEvent event) {
    switch (event.type) {
      case RfidEventType.connected:
        _showSnackBar('Connected to ${event.readerName}');
        break;

      case RfidEventType.disconnected:
        setState(() {
          _isScanning = false;
        });
        _showSnackBar('Disconnected');
        break;

      case RfidEventType.tagRead:
        final tags = event.tags;
        if (tags != null) {
          setState(() {
            for (final tag in tags) {
              if (!_uniqueTagIds.contains(tag.tagId)) {
                _uniqueTagIds.add(tag.tagId);
                _scannedTags.insert(0, tag);
              }
            }
          });
        }
        break;

      case RfidEventType.trigger:
        final pressed = event.triggerPressed ?? false;
        if (pressed && _status?.isReady == true && !_isScanning) {
          _startScanning();
        } else if (!pressed && _isScanning) {
          _stopScanning();
        }
        break;

      case RfidEventType.error:
        _showSnackBar('Error: ${event.errorMessage}');
        break;

      case RfidEventType.readerAppeared:
        _showSnackBar('Reader detected: ${event.readerName}');
        break;

      case RfidEventType.readerDisappeared:
        _showSnackBar('Reader removed');
        break;

      default:
        break;
    }
  }

  Future<void> _startScanning() async {
    if (_status?.isReady != true) {
      _showSnackBar('Reader not ready');
      return;
    }

    final success = await ZebraRfidReader.startInventory();
    if (success) {
      setState(() => _isScanning = true);
    } else {
      _showSnackBar('Failed to start scanning');
    }
  }

  Future<void> _stopScanning() async {
    final success = await ZebraRfidReader.stopInventory();
    if (success) {
      setState(() => _isScanning = false);
    }
  }

  Future<void> _setPower(int power) async {
    final success = await ZebraRfidReader.setAntennaPower(power);
    if (success) {
      setState(() => _powerLevel = power);
      _showSnackBar('Power set to $power');
    } else {
      _showSnackBar('Failed to set power');
    }
  }

  void _clearTags() {
    setState(() {
      _scannedTags.clear();
      _uniqueTagIds.clear();
    });
  }

  Future<void> _retryInitialization() async {
    _showSnackBar('Re-initializing...');
    AppLogger().info('Manual re-initialization triggered', source: 'UI');
    await ZebraRfidReader.initialize();
  }

  void _showSnackBar(String message) {
    if (!mounted) return;
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message), duration: const Duration(seconds: 2)),
    );
  }

  @override
  void dispose() {
    _eventSubscription?.cancel();
    _statusSubscription?.cancel();
    ZebraRfidReader.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(
      title: const Text('Zebra RFID Reader'),
      actions: [
        IconButton(
          icon: const Icon(Icons.delete_sweep),
          onPressed: _clearTags,
          tooltip: 'Clear tags',
        ),
        IconButton(
          icon: const Icon(Icons.article),
          onPressed: () => Navigator.push(
            context,
            MaterialPageRoute(builder: (_) => const LogViewerScreen()),
          ),
          tooltip: 'View logs',
        ),
      ],
    ),
    body: Column(
      children: [
        // Status Card
        _buildStatusCard(),

        // Control Buttons
        if (_status?.isReady == true) _buildControlButtons(),

        // Power Slider
        if (_status?.isReady == true) _buildPowerSlider(),

        const Divider(),

        // Tags Header
        _buildTagsHeader(),

        // Tags List
        Expanded(child: _buildTagsList()),
      ],
    ),
  );

  Widget _buildStatusCard() {
    final status = _status;
    final isInitializing = status == null || status.isInitializing;

    Color statusColor;
    IconData statusIcon;

    if (status == null) {
      statusColor = Colors.orange;
      statusIcon = Icons.hourglass_empty;
    } else if (status.hasError) {
      statusColor = Colors.red;
      statusIcon = Icons.error;
    } else if (status.isReconnecting) {
      statusColor = Colors.orange;
      statusIcon = Icons.sync;
    } else if (status.isReady) {
      statusColor = Colors.green;
      statusIcon = Icons.check_circle;
    } else if (!status.permissionsGranted) {
      statusColor = Colors.red;
      statusIcon = Icons.lock;
    } else if (!status.sdkInitialized) {
      statusColor = Colors.orange;
      statusIcon = Icons.settings;
    } else if (!status.readerConnected) {
      statusColor = Colors.orange;
      statusIcon = Icons.bluetooth_searching;
    } else {
      statusColor = Colors.grey;
      statusIcon = Icons.bluetooth_disabled;
    }

    return Card(
      margin: const EdgeInsets.all(16),
      color: statusColor.withValues(alpha: .1),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            Row(
              children: [
                Icon(statusIcon, color: statusColor, size: 32),
                const SizedBox(width: 12),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        status?.statusMessage ?? 'Initializing...',
                        style: const TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      const SizedBox(height: 4),
                      Text(
                        'Tags: ${_uniqueTagIds.length} | Scanning: ${_isScanning ? "Yes" : "No"}',
                        style: TextStyle(fontSize: 14, color: Colors.grey[600]),
                      ),
                      if (status != null && status.readerName != null)
                        Padding(
                          padding: const EdgeInsets.only(top: 4),
                          child: Text(
                            'Reader: ${status.readerName}',
                            style: TextStyle(
                              fontSize: 12,
                              color: Colors.grey[600],
                              fontStyle: FontStyle.italic,
                            ),
                          ),
                        ),
                    ],
                  ),
                ),
                if (isInitializing || (status.isReconnecting == true))
                  const SizedBox(
                    width: 24,
                    height: 24,
                    child: CircularProgressIndicator(strokeWidth: 2),
                  ),
              ],
            ),
            const SizedBox(height: 12),
            // Status indicators
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                _buildStatusIndicator(
                  'Permissions',
                  status?.permissionsGranted ?? false,
                ),
                _buildStatusIndicator('SDK', status?.sdkInitialized ?? false),
                _buildStatusIndicator(
                  'Connected',
                  status?.readerConnected ?? false,
                ),
              ],
            ),
            // Retry button if error
            if (status?.hasError == true)
              Padding(
                padding: const EdgeInsets.only(top: 12),
                child: ElevatedButton.icon(
                  onPressed: _retryInitialization,
                  icon: const Icon(Icons.refresh),
                  label: const Text('Retry'),
                ),
              ),
            // Debug info
            if (status != null)
              Padding(
                padding: const EdgeInsets.only(top: 8),
                child: Text(
                  'State: ${status.shortStatus}',
                  style: TextStyle(
                    fontSize: 10,
                    color: Colors.grey[500],
                    fontFamily: 'monospace',
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }

  Widget _buildStatusIndicator(String label, bool active) => Column(
    children: [
      Icon(
        active ? Icons.check_circle : Icons.cancel,
        color: active ? Colors.green : Colors.grey,
        size: 20,
      ),
      const SizedBox(height: 4),
      Text(
        label,
        style: TextStyle(
          fontSize: 12,
          color: active ? Colors.green : Colors.grey,
        ),
      ),
    ],
  );

  Widget _buildControlButtons() => Padding(
    padding: const EdgeInsets.symmetric(horizontal: 16),
    child: Row(
      children: [
        Expanded(
          child: ElevatedButton.icon(
            onPressed: !_isScanning ? _startScanning : null,
            icon: const Icon(Icons.play_arrow),
            label: const Text('Start'),
            style: ElevatedButton.styleFrom(
              backgroundColor: Colors.green,
              foregroundColor: Colors.white,
              padding: const EdgeInsets.all(16),
            ),
          ),
        ),
        const SizedBox(width: 8),
        Expanded(
          child: ElevatedButton.icon(
            onPressed: _isScanning ? _stopScanning : null,
            icon: const Icon(Icons.stop),
            label: const Text('Stop'),
            style: ElevatedButton.styleFrom(
              backgroundColor: Colors.red,
              foregroundColor: Colors.white,
              padding: const EdgeInsets.all(16),
            ),
          ),
        ),
      ],
    ),
  );

  Widget _buildPowerSlider() => Padding(
    padding: const EdgeInsets.all(16),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          'Antenna Power: $_powerLevel / $_maxPower',
          style: const TextStyle(fontWeight: FontWeight.bold),
        ),
        Slider(
          value: _powerLevel.toDouble(),
          min: 100,
          max: _maxPower.toDouble(),
          divisions: (_maxPower - 100) ~/ 10,
          label: _powerLevel.toString(),
          onChanged: (value) {
            setState(() => _powerLevel = value.toInt());
          },
          onChangeEnd: (value) => _setPower(value.toInt()),
        ),
      ],
    ),
  );

  Widget _buildTagsHeader() => Padding(
    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
    child: Row(
      children: [
        const Text(
          'Scanned Tags',
          style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
        ),
        const Spacer(),
        Text(
          '${_uniqueTagIds.length} unique',
          style: const TextStyle(color: Colors.grey),
        ),
      ],
    ),
  );

  Widget _buildTagsList() {
    if (_scannedTags.isEmpty) {
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(Icons.nfc, size: 64, color: Colors.grey[400]),
            const SizedBox(height: 16),
            Text(
              _status?.isReady == true
                  ? 'Press "Start" or trigger to scan'
                  : 'Waiting for reader connection...',
              style: TextStyle(fontSize: 16, color: Colors.grey[600]),
            ),
          ],
        ),
      );
    }

    return ListView.builder(
      itemCount: _scannedTags.length,
      itemBuilder: (context, index) {
        final tag = _scannedTags[index];
        return Card(
          margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
          child: ListTile(
            leading: CircleAvatar(
              backgroundColor: Colors.blue,
              child: Text(
                '${index + 1}',
                style: const TextStyle(color: Colors.white),
              ),
            ),
            title: Text(
              tag.tagId,
              style: const TextStyle(
                fontFamily: 'monospace',
                fontSize: 12,
                fontWeight: FontWeight.bold,
              ),
            ),
            subtitle: Text(
              'RSSI: ${tag.rssi} dBm | Antenna: ${tag.antennaId} | Reads: ${tag.count}',
              style: const TextStyle(fontSize: 11),
            ),
            trailing: Icon(Icons.nfc, color: Colors.blue[300]),
          ),
        );
      },
    );
  }
}
5
likes
150
points
97
downloads

Publisher

unverified uploader

Weekly Downloads

plugin to interact with Zebra RFID readers

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on rfid_zebra_reader

Packages that implement rfid_zebra_reader