zebra_rfid_plugin 1.0.0 copy "zebra_rfid_plugin: ^1.0.0" to clipboard
zebra_rfid_plugin: ^1.0.0 copied to clipboard

PlatformAndroid

A Flutter plugin for Zebra RFID SDK (RFD40XX) with support for tag reading, writing, and locating.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:zebra_rfid_plugin/zebra_rfid_plugin.dart';
import 'dart:async';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Zebra RFID Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: const RfidHomePage(),
    );
  }
}

class RfidHomePage extends StatefulWidget {
  const RfidHomePage({Key? key}) : super(key: key);

  @override
  State<RfidHomePage> createState() => _RfidHomePageState();
}

class _RfidHomePageState extends State<RfidHomePage> {
  bool _isInitialized = false;
  bool _isConnected = false;
  bool _isScanning = false;
  bool _isLocationing = false;

  List<ReaderDevice> _readers = [];
  ReaderDevice? _selectedReader;

  final Map<String, TagReadEvent> _scannedTags = {};
  String? _statusMessage;
  int _batteryLevel = 0;

  StreamSubscription<TagReadEvent>? _tagReadSubscription;
  StreamSubscription<StatusEvent>? _statusSubscription;

  @override
  void initState() {
    super.initState();
    _initializePlugin();
    _setupEventListeners();
  }

  @override
  void dispose() {
    _tagReadSubscription?.cancel();
    _statusSubscription?.cancel();
    ZebraRfidPlugin.dispose();
    super.dispose();
  }

  Future<void> _initializePlugin() async {
    try {
      final result =
          await ZebraRfidPlugin.initialize(transportType: 'SERVICE_USB');
      setState(() {
        _isInitialized = result;
        _statusMessage = result ? 'SDK Initialized' : 'Initialization Failed';
      });

      if (result) {
        await _loadReaders();
      }
    } catch (e) {
      setState(() {
        _statusMessage = 'Error: $e';
      });
    }
  }

  void _setupEventListeners() {
    // Listen to tag read events
    _tagReadSubscription = ZebraRfidPlugin.tagReadStream.listen(
      (event) {
        setState(() {
          _scannedTags[event.tagId] = event;
        });
      },
      onError: (error) {
        _showSnackBar('Tag read error: $error');
      },
    );

    // Listen to status events
    _statusSubscription = ZebraRfidPlugin.statusEventStream.listen(
      (event) {
        setState(() {
          _statusMessage = event.message;
        });

        // Handle specific status events
        switch (event.type) {
          case StatusEventType.TRIGGER_PRESSED:
            _startScanning();
            break;
          case StatusEventType.TRIGGER_RELEASED:
            _stopScanning();
            break;
          case StatusEventType.READER_DISCONNECTED:
            setState(() {
              _isConnected = false;
              _isScanning = false;
            });
            break;
          case StatusEventType.BATTERY_LOW:
          case StatusEventType.BATTERY_CRITICAL:
            if (event.data != null && event.data!['level'] != null) {
              setState(() {
                _batteryLevel = event.data!['level'];
              });
            }
            break;
          default:
            break;
        }
      },
      onError: (error) {
        _showSnackBar('Status error: $error');
      },
    );
  }

  Future<void> _loadReaders() async {
    try {
      final readers = await ZebraRfidPlugin.getAvailableReaders();
      setState(() {
        _readers = readers;
        if (readers.isNotEmpty && _selectedReader == null) {
          _selectedReader = readers.first;
        }
      });
    } catch (e) {
      _showSnackBar('Error loading readers: $e');
    }
  }

  Future<void> _connect() async {
    if (_selectedReader == null) {
      _showSnackBar('Please select a reader first');
      return;
    }

    try {
      final result = await ZebraRfidPlugin.connect(_selectedReader!.name);
      setState(() {
        _isConnected = result;
        _statusMessage = result
            ? 'Connected to ${_selectedReader!.name}'
            : 'Connection failed';
      });

      if (result) {
        // Get battery level
        try {
          _batteryLevel = await ZebraRfidPlugin.getBatteryLevel();
          setState(() {});
        } catch (e) {
          // Battery info might not be available for all readers
        }

        // Configure reader with default settings
        await _configureDefaultSettings();
      }
    } catch (e) {
      _showSnackBar('Connection error: $e');
    }
  }

  Future<void> _configureDefaultSettings() async {
    try {
      final config = ReaderConfig(
        antennaPower: 270, // Medium power
        beeperEnabled: true,
        session: 'S1',
        tagPopulation: 100,
      );
      await ZebraRfidPlugin.configureReader(config);
    } catch (e) {
      _showSnackBar('Configuration error: $e');
    }
  }

  Future<void> _disconnect() async {
    try {
      await ZebraRfidPlugin.disconnect();
      setState(() {
        _isConnected = false;
        _isScanning = false;
        _statusMessage = 'Disconnected';
      });
    } catch (e) {
      _showSnackBar('Disconnect error: $e');
    }
  }

  Future<void> _startScanning() async {
    if (!_isConnected) {
      _showSnackBar('Please connect to a reader first');
      return;
    }

    try {
      setState(() {
        _scannedTags.clear();
      });

      final result = await ZebraRfidPlugin.startInventory();
      setState(() {
        _isScanning = result;
        _statusMessage = result ? 'Scanning...' : 'Failed to start scanning';
      });
    } catch (e) {
      _showSnackBar('Scan error: $e');
    }
  }

  Future<void> _stopScanning() async {
    try {
      final result = await ZebraRfidPlugin.stopInventory();
      setState(() {
        _isScanning = !result;
        _statusMessage =
            result ? 'Scanning stopped' : 'Failed to stop scanning';
      });
    } catch (e) {
      _showSnackBar('Stop scan error: $e');
    }
  }

  Future<void> _writeTag(String tagId, String data) async {
    if (!_isConnected) {
      _showSnackBar('Please connect to a reader first');
      return;
    }

    try {
      final result = await ZebraRfidPlugin.writeTag(
        tagId: tagId, 
        data: data,
        memoryBank: 'EPC',
        offset: 2,
      );

      _showSnackBar(
          result ? 'Tag written successfully' : 'Failed to write tag');
    } catch (e) {
      _showSnackBar('Write error: $e');
    }
  }

  Future<void> _locateTag(String tagId) async {
    if (!_isConnected) {
      _showSnackBar('Please connect to a reader first');
      return;
    }

    try {
      if (_isLocationing) {
        await ZebraRfidPlugin.stopLocationing();
        setState(() {
          _isLocationing = false;
          _statusMessage = 'Locationing stopped';
        });
      } else {
        final result = await ZebraRfidPlugin.startLocationing(tagId);
        setState(() {
          _isLocationing = result;
          _statusMessage =
              result ? 'Locating tag $tagId...' : 'Failed to start locationing';
        });
      }
    } catch (e) {
      _showSnackBar('Locationing error: $e');
    }
  }

  void _showSnackBar(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message)),
    );
  }

  void _showWriteDialog(String tagId) {
    final controller = TextEditingController();

    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Write to EPC'),
        // 1. Wrap content in SingleChildScrollView to prevent overflow
        content: SingleChildScrollView(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text('Tag ID: $tagId'),
              const SizedBox(height: 16),
              const Text('Enter hex data (multiple of 4 chars for full words):'),
              TextField(
                controller: controller,
                decoration: const InputDecoration(
                  labelText: 'Data (Hex)',
                  hintText: 'e.g., 300833FF (8 chars = 2 words)',
                ),
                // 2. Ensure keyboard doesn't hide input
                scrollPadding: EdgeInsets.only(
                    bottom: MediaQuery.of(context).viewInsets.bottom + 20),
                onChanged: (value) {
                  // Note: The UI won't rebuild here to show the "Words:" text 
                  // unless you wrap this part in a StatefulBuilder or 
                  // ValueListenableBuilder.
                },
              ),
              // This part logic technically requires a rebuild to show up dynamically
              if (controller.text.isNotEmpty)
                Text(
                  'Words: ${controller.text.length ~/ 4} (Offset 0: Ensure <= tag EPC size)',
                  style: Theme.of(context).textTheme.bodySmall,
                ),
            ],
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Cancel'),
          ),
          ElevatedButton(
            onPressed: () {
              Navigator.pop(context);
              _writeTag(tagId, controller.text.toUpperCase());
            },
            child: const Text('Write'),
          ),
        ],
      ),
    );
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Zebra RFID Demo'),
        actions: [
          if (_isConnected && _batteryLevel > 0)
            Padding(
              padding: const EdgeInsets.all(16.0),
              child: Row(
                children: [
                  Icon(
                    Icons.battery_full,
                    color: _batteryLevel < 20 ? Colors.red : Colors.white,
                  ),
                  const SizedBox(width: 4),
                  Text('$_batteryLevel%'),
                ],
              ),
            ),
        ],
      ),
      body: Column(
        children: [
          // Status card
          Card(
            margin: const EdgeInsets.all(8),
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                children: [
                  Row(
                    children: [
                      Icon(
                        _isConnected ? Icons.check_circle : Icons.error,
                        color: _isConnected ? Colors.green : Colors.grey,
                      ),
                      const SizedBox(width: 8),
                      Expanded(
                        child: Text(
                          _statusMessage ?? 'Ready',
                          style: Theme.of(context).textTheme.bodyLarge,
                        ),
                      ),
                    ],
                  ),
                  const SizedBox(height: 8),
                  if (_isInitialized && !_isConnected)
                    DropdownButton<ReaderDevice>(
                      isExpanded: true,
                      value: _selectedReader,
                      hint: const Text('Select Reader'),
                      items: _readers.map((reader) {
                        return DropdownMenuItem(
                          value: reader,
                          child: Text(reader.name),
                        );
                      }).toList(),
                      onChanged: (reader) {
                        setState(() {
                          _selectedReader = reader;
                        });
                      },
                    ),
                ],
              ),
            ),
          ),

          // Control buttons
          Padding(
            padding: const EdgeInsets.all(8),
            child: Wrap(
              spacing: 8,
              runSpacing: 8,
              alignment: WrapAlignment.center,
              children: [
                if (!_isConnected)
                  ElevatedButton.icon(
                    onPressed: _isInitialized ? _connect : null,
                    icon: const Icon(Icons.link),
                    label: const Text('Connect'),
                  )
                else
                  ElevatedButton.icon(
                    onPressed: _disconnect,
                    icon: const Icon(Icons.link_off),
                    label: const Text('Disconnect'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.red,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ElevatedButton.icon(
                  onPressed:
                      _isConnected && !_isScanning ? _startScanning : null,
                  icon: const Icon(Icons.play_arrow),
                  label: const Text('Start Scan'),
                ),
                ElevatedButton.icon(
                  onPressed: _isConnected && _isScanning ? _stopScanning : null,
                  icon: const Icon(Icons.stop),
                  label: const Text('Stop Scan'),
                ),
                ElevatedButton.icon(
                  onPressed: _isConnected
                      ? () {
                          setState(() {
                            _scannedTags.clear();
                          });
                        }
                      : null,
                  icon: const Icon(Icons.clear),
                  label: const Text('Clear'),
                ),
              ],
            ),
          ),

          // Tags list
          Expanded(
            child: Card(
              margin: const EdgeInsets.all(8),
              child: Column(
                children: [
                  Padding(
                    padding: const EdgeInsets.all(16),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        Text(
                          'Scanned Tags: ${_scannedTags.length}',
                          style: Theme.of(context).textTheme.titleMedium,
                        ),
                        Text(
                          'Total Reads: ${_scannedTags.values.fold(0, (sum, tag) => sum + tag.count)}',
                          style: Theme.of(context).textTheme.titleMedium,
                        ),
                      ],
                    ),
                  ),
                  const Divider(height: 1),
                  Expanded(
                    child: _scannedTags.isEmpty
                        ? const Center(
                            child: Text('No tags scanned yet'),
                          )
                        : ListView.builder(
                            itemCount: _scannedTags.length,
                            itemBuilder: (context, index) {
                              final tag = _scannedTags.values.elementAt(index);
                              return ListTile(
                                leading: CircleAvatar(
                                  child: Text('${tag.count}'),
                                ),
                                title: Text(
                                  tag.tagId,
                                  style:
                                      const TextStyle(fontFamily: 'monospace'),
                                ),
                                subtitle: Row(
                                  children: [
                                    Text('RSSI: ${tag.rssi} dBm'),
                                    if (tag.locationInfo != null) ...[
                                      const SizedBox(width: 16),
                                      Text(
                                          'Distance: ${tag.locationInfo!.toStringAsFixed(2)}'),
                                    ],
                                  ],
                                ),
                                trailing: Row(
                                  mainAxisSize: MainAxisSize.min,
                                  children: [
                                    IconButton(
                                      icon: const Icon(Icons.edit),
                                      onPressed: () =>
                                          _showWriteDialog(tag.tagId),
                                      tooltip: 'Write to tag',
                                    ),
                                    IconButton(
                                      icon: Icon(
                                        Icons.location_searching,
                                        color:
                                            _isLocationing ? Colors.blue : null,
                                      ),
                                      onPressed: () => _locateTag(tag.tagId),
                                      tooltip: 'Locate tag',
                                    ),
                                  ],
                                ),
                              );
                            },
                          ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}
1
likes
150
points
119
downloads
screenshot

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for Zebra RFID SDK (RFD40XX) with support for tag reading, writing, and locating.

Repository (GitHub)
View/report issues

Topics

#zebra #rifd

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on zebra_rfid_plugin

Packages that implement zebra_rfid_plugin