universal_beacon_sdk 1.0.2 copy "universal_beacon_sdk: ^1.0.2" to clipboard
universal_beacon_sdk: ^1.0.2 copied to clipboard

SDK genérico y robusto para detección de beacons BLE. Ideal para control de acceso, sistemas de asistencia, marketing de proximidad, automatización IoT, tracking de assets y aplicaciones empresariales.

example/lib/main.dart

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

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

class BeaconSDKExampleApp extends StatelessWidget {
  const BeaconSDKExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Universal Beacon SDK Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: const BeaconScannerPage(),
    );
  }
}

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

  @override
  State<BeaconScannerPage> createState() => _BeaconScannerPageState();
}

class _BeaconScannerPageState extends State<BeaconScannerPage> {
  final UniversalBeaconScanner _scanner = UniversalBeaconScanner();
  List<BeaconDevice> _detectedDevices = [];
  ScannerStatus _status = ScannerStatus.idle;
  String _statusMessage = '';

  @override
  void initState() {
    super.initState();
    _initializeSDK();
  }

  Future<void> _initializeSDK() async {
    // Registrar algunos beacons de ejemplo
    final exampleBeacons = [
      BeaconRegistry(
        id: 'holy_shun',
        uuid: 'FDA50693-A4E2-4FB1-AFCF-C6EB07647825',
        displayName: 'Holy-Shun',
        description: 'Dispositivo Holy Shun',
        category: 'holy_device',
        metadata: {'type': 'shun', 'version': '1.0'},
        registeredAt: DateTime.now(),
      ),
      BeaconRegistry(
        id: 'holy_jin',
        uuid: 'E2C56DB5-DFFB-48D2-B060-D0F5A7100000',
        displayName: 'Holy-Jin',
        description: 'Dispositivo Holy Jin',
        category: 'holy_device',
        metadata: {'type': 'jin', 'version': '1.0'},
        registeredAt: DateTime.now(),
      ),
    ];

    // Inicializar con configuración balanceada
    final success = await _scanner.initialize(
      config: ScannerConfig.balanced,
      registeredBeacons: exampleBeacons,
    );

    if (success) {
      // Escuchar eventos
      _scanner.events.listen(_onBeaconEvent);

      // Escuchar dispositivos detectados
      _scanner.devices.listen((devices) {
        setState(() {
          _detectedDevices = devices;
        });
      });

      // Escuchar estado del scanner
      _scanner.status.listen((status) {
        setState(() {
          _status = status;
          _statusMessage = _getStatusMessage(status);
        });
      });

      setState(() {
        _statusMessage = 'SDK inicializado correctamente';
      });
    } else {
      setState(() {
        _statusMessage = 'Error inicializando SDK';
      });
    }
  }

  void _onBeaconEvent(BeaconEvent event) {
    final message = _getEventMessage(event);

    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(message),
        backgroundColor: _getEventColor(event.type),
        duration: const Duration(seconds: 2),
      ),
    );
  }

  String _getEventMessage(BeaconEvent event) {
    switch (event.type) {
      case BeaconEventType.detected:
        return '📱 Nuevo beacon: ${event.device.name}';
      case BeaconEventType.registeredEntered:
        return '✅ ${event.device.name} entró en rango';
      case BeaconEventType.registeredExited:
        return '❌ ${event.device.name} salió de rango';
      case BeaconEventType.lost:
        return '👻 Beacon perdido: ${event.device.name}';
      default:
        return '🔄 ${event.device.name} actualizado';
    }
  }

  Color _getEventColor(BeaconEventType type) {
    switch (type) {
      case BeaconEventType.detected:
      case BeaconEventType.registeredEntered:
        return Colors.green;
      case BeaconEventType.lost:
      case BeaconEventType.registeredExited:
        return Colors.red;
      default:
        return Colors.blue;
    }
  }

  String _getStatusMessage(ScannerStatus status) {
    switch (status) {
      case ScannerStatus.idle:
        return 'Inactivo';
      case ScannerStatus.initializing:
        return 'Inicializando...';
      case ScannerStatus.ready:
        return 'Listo para escanear';
      case ScannerStatus.scanning:
        return 'Escaneando beacons...';
      case ScannerStatus.stopped:
        return 'Escaneo detenido';
      case ScannerStatus.bluetoothOff:
        return 'Bluetooth desactivado';
      case ScannerStatus.permissionDenied:
        return 'Permisos denegados';
      case ScannerStatus.error:
        return 'Error en el scanner';
    }
  }

  Future<void> _toggleScanning() async {
    if (_scanner.isScanning) {
      await _scanner.stopScanning();
    } else {
      await _scanner.startScanning();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Universal Beacon SDK'),
        centerTitle: true,
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            // Estado del scanner
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  children: [
                    Row(
                      children: [
                        Icon(
                          _getStatusIcon(_status),
                          color: _getStatusColor(_status),
                        ),
                        const SizedBox(width: 8),
                        Text(
                          'Estado: $_statusMessage',
                          style: Theme.of(context).textTheme.titleMedium,
                        ),
                      ],
                    ),
                    const SizedBox(height: 16),
                    Row(
                      children: [
                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _status.canStartScanning
                                ? _toggleScanning
                                : null,
                            icon: Icon(_scanner.isScanning
                                ? Icons.stop
                                : Icons.play_arrow),
                            label: Text(
                                _scanner.isScanning ? 'Detener' : 'Iniciar'),
                          ),
                        ),
                        const SizedBox(width: 16),
                        Text(
                          '${_detectedDevices.length} beacons',
                          style: Theme.of(context).textTheme.bodyLarge,
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 16),

            // Lista de dispositivos detectados
            Expanded(
              child: _detectedDevices.isEmpty
                  ? const Center(
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          Icon(Icons.bluetooth_searching,
                              size: 64, color: Colors.grey),
                          SizedBox(height: 16),
                          Text(
                            'No hay beacons detectados',
                            style: TextStyle(fontSize: 18, color: Colors.grey),
                          ),
                          SizedBox(height: 8),
                          Text(
                            'Inicia el escaneo para detectar beacons cercanos',
                            style: TextStyle(color: Colors.grey),
                          ),
                        ],
                      ),
                    )
                  : ListView.builder(
                      itemCount: _detectedDevices.length,
                      itemBuilder: (context, index) {
                        final device = _detectedDevices[index];
                        return BeaconCard(device: device);
                      },
                    ),
            ),
          ],
        ),
      ),
    );
  }

  IconData _getStatusIcon(ScannerStatus status) {
    switch (status) {
      case ScannerStatus.scanning:
        return Icons.bluetooth_searching;
      case ScannerStatus.ready:
        return Icons.bluetooth;
      case ScannerStatus.error:
      case ScannerStatus.bluetoothOff:
      case ScannerStatus.permissionDenied:
        return Icons.error;
      default:
        return Icons.bluetooth_disabled;
    }
  }

  Color _getStatusColor(ScannerStatus status) {
    switch (status) {
      case ScannerStatus.scanning:
        return Colors.blue;
      case ScannerStatus.ready:
        return Colors.green;
      case ScannerStatus.error:
      case ScannerStatus.bluetoothOff:
      case ScannerStatus.permissionDenied:
        return Colors.red;
      default:
        return Colors.grey;
    }
  }

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

class BeaconCard extends StatelessWidget {
  final BeaconDevice device;

  const BeaconCard({super.key, required this.device});

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.only(bottom: 8),
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                // Icono del tipo de beacon
                Container(
                  padding: const EdgeInsets.all(8),
                  decoration: BoxDecoration(
                    color: device.isRegistered ? Colors.green : Colors.grey,
                    borderRadius: BorderRadius.circular(8),
                  ),
                  child: Icon(
                    device.isRegistered ? Icons.verified : Icons.bluetooth,
                    color: Colors.white,
                    size: 20,
                  ),
                ),
                const SizedBox(width: 12),

                // Información principal
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        device.name,
                        style: const TextStyle(
                          fontWeight: FontWeight.bold,
                          fontSize: 16,
                        ),
                      ),
                      if (device.category != null)
                        Text(
                          device.category!,
                          style: TextStyle(
                            color: Colors.grey[600],
                            fontSize: 12,
                          ),
                        ),
                    ],
                  ),
                ),

                // RSSI y distancia
                Column(
                  crossAxisAlignment: CrossAxisAlignment.end,
                  children: [
                    Text(
                      '${device.rssi} dBm',
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                        color: _getRssiColor(device.rssi),
                      ),
                    ),
                    if (device.estimatedDistance != null)
                      Text(
                        '${device.estimatedDistance!.toStringAsFixed(1)}m',
                        style: const TextStyle(fontSize: 12),
                      ),
                  ],
                ),
              ],
            ),

            if (device.uuid != null) ...[
              const SizedBox(height: 8),
              Row(
                children: [
                  const Icon(Icons.fingerprint, size: 16, color: Colors.grey),
                  const SizedBox(width: 4),
                  Expanded(
                    child: Text(
                      device.uuid!,
                      style: const TextStyle(
                        fontSize: 12,
                        fontFamily: 'monospace',
                      ),
                    ),
                  ),
                ],
              ),
            ],

            if (device.major != null && device.minor != null) ...[
              const SizedBox(height: 4),
              Row(
                children: [
                  const Icon(Icons.tag, size: 16, color: Colors.grey),
                  const SizedBox(width: 4),
                  Text(
                    'Major: ${device.major}, Minor: ${device.minor}',
                    style: const TextStyle(fontSize: 12),
                  ),
                ],
              ),
            ],

            // Proximidad
            const SizedBox(height: 8),
            Row(
              children: [
                Container(
                  padding:
                      const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                  decoration: BoxDecoration(
                    color: _getProximityColor(device.proximityLevel),
                    borderRadius: BorderRadius.circular(12),
                  ),
                  child: Text(
                    device.proximityLevel.displayName,
                    style: const TextStyle(
                      color: Colors.white,
                      fontSize: 11,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ),
                const Spacer(),
                Text(
                  'Visto: ${_formatLastSeen(device.lastSeen)}',
                  style: TextStyle(
                    fontSize: 11,
                    color: Colors.grey[600],
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Color _getRssiColor(int rssi) {
    if (rssi > -50) return Colors.green;
    if (rssi > -70) return Colors.orange;
    return Colors.red;
  }

  Color _getProximityColor(ProximityLevel level) {
    switch (level) {
      case ProximityLevel.immediate:
        return Colors.green;
      case ProximityLevel.near:
        return Colors.orange;
      case ProximityLevel.far:
        return Colors.red;
      case ProximityLevel.unknown:
        return Colors.grey;
    }
  }

  String _formatLastSeen(DateTime lastSeen) {
    final diff = DateTime.now().difference(lastSeen);
    if (diff.inSeconds < 60) {
      return 'ahora';
    } else if (diff.inMinutes < 60) {
      return '${diff.inMinutes}m';
    } else {
      return '${diff.inHours}h';
    }
  }
}
2
likes
140
points
3
downloads

Publisher

unverified uploader

Weekly Downloads

SDK genérico y robusto para detección de beacons BLE. Ideal para control de acceso, sistemas de asistencia, marketing de proximidad, automatización IoT, tracking de assets y aplicaciones empresariales.

Documentation

API reference

License

MIT (license)

Dependencies

flutter, flutter_reactive_ble, permission_handler, shared_preferences, uuid

More

Packages that depend on universal_beacon_sdk