ble_sdk 0.2.4 copy "ble_sdk: ^0.2.4" to clipboard
ble_sdk: ^0.2.4 copied to clipboard

Bluetooth Low Energy (BLE) plugin that can communicate with single device

example/lib/main.dart

import 'dart:async';
import 'dart:developer';

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

void main() {
  runApp(const MaterialApp(home: ScanPage()));
}

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

  @override
  State<ScanPage> createState() => _ScanPageState();
}

class _ScanPageState extends State<ScanPage> {
  late StreamSubscription deviceStream;

  bool isScan = false;
  @override
  void initState() {
    super.initState();
    BleSdk.instance
        .isBluetoothAvailable()
        .then((value) => log(value.toString()));
  }

  @override
  void dispose() {
    if (isScan) {
      deviceStream.cancel();
    }
    super.dispose();
  }

  final ctlServices = TextEditingController(text: '1808');
  List<BluetoothBLEModel> devices = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: [
          const SliverAppBar(
            title: Text('Ble Sdk'),
          ),
          SliverToBoxAdapter(
            child: Row(
              children: [
                Expanded(
                  child: TextFormField(
                    decoration: const InputDecoration(
                      hintText: 'servicesUUID ex: 1808,1810',
                    ),
                    validator: (s) =>
                        s == null || !s.split(',').any((e) => e.length != 4)
                            ? null
                            : 'server is not validator',
                    autovalidateMode: AutovalidateMode.always,
                    style: const TextStyle(fontSize: 12),
                    controller: ctlServices,
                  ),
                ),
                GestureDetector(
                  onTap: () {
                    if (!ctlServices.text
                        .split(',')
                        .any((e) => e.length != 4)) {
                      setState(() => isScan = true);
                      BleSdk.instance.startScan(
                        services: ctlServices.text
                            .split(',')
                            .where((s) => s.length == 4)
                            .toList(),
                      );
                      setState(() => devices = []);
                      deviceStream =
                          BleSdk.instance.deviceResult.listen((event) {
                        setState(
                          () => devices = List.from(devices)..add(event),
                        );
                      });
                    }
                  },
                  child: Container(
                    padding: const EdgeInsets.symmetric(
                      vertical: 4,
                      horizontal: 6,
                    ),
                    color: Colors.blue,
                    child: const Text(
                      'Scan',
                    ),
                  ),
                ),
                const SizedBox(width: 2),
                GestureDetector(
                  onTap: () {
                    BleSdk.instance.stopScan();
                    if (isScan) {
                      deviceStream.cancel();
                      setState(() => isScan = false);
                    }
                  },
                  child: Container(
                    padding: const EdgeInsets.symmetric(
                      vertical: 4,
                      horizontal: 6,
                    ),
                    color: Colors.blue,
                    child: const Text(
                      'Stop',
                    ),
                  ),
                ),
                const SizedBox(width: 2),
              ],
            ),
          ),
          SliverList(
            delegate: SliverChildListDelegate(
              [
                for (final device in devices)
                  Container(
                    color: Colors.grey,
                    padding: const EdgeInsets.symmetric(
                      vertical: 10,
                      horizontal: 16,
                    ),
                    child: Row(
                      children: [
                        Expanded(
                          child: Text('${device.name}(${device.id})'),
                        ),
                        GestureDetector(
                          onTap: () async {
                            BleSdk.instance
                                .connect(deviceId: device.id)
                                .then((value) {
                              log('message $value');
                              if (value) {
                                Navigator.of(context).push(
                                  MaterialPageRoute(
                                    builder: (ctx) =>
                                        DevicePage(device: device),
                                  ),
                                );
                              }
                            });
                          },
                          child: Container(
                            padding: const EdgeInsets.symmetric(
                              vertical: 4,
                              horizontal: 6,
                            ),
                            color: Colors.blue,
                            child: const Text(
                              'Connect',
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class DevicePage extends StatefulWidget {
  const DevicePage({
    super.key,
    required this.device,
  });
  final BluetoothBLEModel device;

  @override
  State<DevicePage> createState() => _DevicePageState();
}

class _DevicePageState extends State<DevicePage> {
  List<Service> _services = [];
  List<String> _logs = [];
  late StreamSubscription logStream;
  late StreamSubscription subscription;
  @override
  void initState() {
    logStream = BleSdk.instance.logResult.listen((event) {
      setState(() => _logs = List.from(_logs)
        ..add(
            '${event.characteristic.characteristicId.replaceAll('0000', '').split('-').first}: ${event.message}'));
    });
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      subscription = BleSdk.instance.stateConnectResult.listen((event) {
        if (event == StateConnect.disconnected) {
          Navigator.of(context).pop();
        }
      });
    });
  }

  @override
  void dispose() {
    subscription.cancel();
    logStream.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: [
          SliverAppBar(
            title: Text(
              'Device ${widget.device.name}\n(${widget.device.id}) -- connected',
            ),
          ),
          SliverToBoxAdapter(
            child: Row(
              children: [
                GestureDetector(
                  onTap: () async {
                    final services = await BleSdk.instance.discoverServices();
                    setState(
                      () => _services = services,
                    );
                  },
                  child: Container(
                    padding: const EdgeInsets.symmetric(
                      vertical: 4,
                      horizontal: 6,
                    ),
                    color: Colors.blue,
                    child: const Text(
                      'Discover',
                    ),
                  ),
                ),
                const SizedBox(width: 2),
                GestureDetector(
                  onTap: () => BleSdk.instance.disconnect().then(
                        (value) => Navigator.of(context).pop(),
                      ),
                  child: Container(
                    padding: const EdgeInsets.symmetric(
                      vertical: 4,
                      horizontal: 6,
                    ),
                    color: Colors.blue,
                    child: const Text(
                      'Disconnect',
                    ),
                  ),
                ),
                const Spacer(),
                GestureDetector(
                  onTap: () => showDialog(
                    context: context,
                    builder: (ctx) => Material(
                      type: MaterialType.transparency,
                      child: Padding(
                        padding: const EdgeInsets.all(10),
                        child: Container(
                          color: Colors.white,
                          child: ListView(
                            children: [
                              for (final log in _logs) Text(log),
                            ],
                          ),
                        ),
                      ),
                    ),
                  ),
                  child: Container(
                    padding: const EdgeInsets.symmetric(
                      vertical: 4,
                      horizontal: 6,
                    ),
                    color: Colors.blue,
                    child: const Text(
                      'Log',
                    ),
                  ),
                ),
              ],
            ),
          ),
          SliverList(
            delegate: SliverChildListDelegate(
              [
                for (final service in _services)
                  ServiceWidget(service: service),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class ServiceWidget extends StatelessWidget {
  const ServiceWidget({
    super.key,
    required this.service,
  });

  final Service service;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Container(
        color: Colors.grey,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: [
            Container(
              padding: const EdgeInsets.all(8),
              child: Text(
                  'S: ${service.serviceId.replaceFirst('0000', '').split('-').first}'),
            ),
            for (final char in service.characteristics)
              CharacteristicWidget(char: char),
          ],
        ),
      ),
    );
  }
}

class CharacteristicWidget extends StatefulWidget {
  const CharacteristicWidget({
    super.key,
    required this.char,
  });

  final Characteristic char;

  @override
  State<CharacteristicWidget> createState() => _CharacteristicWidgetState();
}

class _CharacteristicWidgetState extends State<CharacteristicWidget> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(4).copyWith(left: 10),
      decoration: BoxDecoration(
        border: Border.all(color: Colors.black),
      ),
      child: Row(
        children: [
          Expanded(
            child: Text(
              '- C: ${widget.char.characteristicId.replaceAll('0000', '').split('-').first}(${widget.char.properties.map((e) => e.name).join(',')})',
            ),
          ),

          /// demo nipro
          if (widget.char.characteristicId.contains('2a52'))
            GestureDetector(
              onTap: () async {
                // final data = await BleSdk.instance.readCharacteristic(
                //   Characteristic(
                //     characteristicId: widget.char.characteristicId
                //         .replaceFirst('2a52', '2a28'),
                //     serviceId:
                //         widget.char.serviceId.replaceFirst('1808', '180a'),
                //   ),
                // );
                // print(data);
                // final data2 = await BleSdk.instance.readCharacteristic(
                //   Characteristic(
                //     characteristicId: widget.char.characteristicId
                //         .replaceFirst('2a52', '2a25'),
                //     serviceId:
                //         widget.char.serviceId.replaceFirst('1808', '180a'),
                //   ),
                // );
                // print(data2);

                // await Future.forEach([
                //   BleSdk.instance.setNotification(widget.char),
                //   BleSdk.instance.setNotification(
                //     Characteristic(
                //       characteristicId: widget.char.characteristicId
                //           .replaceFirst('2a52', '2a18'),
                //       serviceId: widget.char.serviceId,
                //     ),
                //   ),
                //   BleSdk.instance.setNotification(
                //     Characteristic(
                //       characteristicId: widget.char.characteristicId
                //           .replaceFirst('2a52', '2a34'),
                //       serviceId: widget.char.serviceId,
                //     ),
                //   ),
                // ], (element) => element);

                // await Future.forEach(
                //   List.generate(8, (index) => index),
                //   (i) async => await Future.delayed(
                //     const Duration(
                //       milliseconds: 150,
                //     ),
                //     () => BleSdk.instance.writeCharacteristic(
                //       CharacteristicValue(
                //         characteristic: widget.char,
                //         data: [1, 3, 1, i + 1, 0],
                //       ),
                //     ),
                //   ),
                // );

                final dataList =
                    await BleSdk.instance.writeCharacteristicVsNotify(
                  CharacteristicValue(
                    characteristic: widget.char,
                    data: [1, 1],
                  ),
                  notifications: [
                    Characteristic(
                      characteristicId: widget.char.characteristicId
                          .replaceFirst('2a52', '2a18'),
                      serviceId: widget.char.serviceId,
                      properties: [CharacteristicProperties.NOTIFY],
                    ),
                    Characteristic(
                      characteristicId: widget.char.characteristicId
                          .replaceFirst('2a52', '2a34'),
                      serviceId: widget.char.serviceId,
                      properties: [CharacteristicProperties.NOTIFY],
                    ),
                  ],
                );
                for (final data in dataList) {
                  log('data : ${data.data}');
                }
              },
              child: const Icon(
                Icons.download,
                size: 28,
                color: Colors.yellow,
              ),
            ),
          SizedBox(
            width: 120,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                if (widget.char.properties
                    .contains(CharacteristicProperties.READ))
                  GestureDetector(
                    onTap: () =>
                        BleSdk.instance.readCharacteristic(widget.char),
                    child: const Icon(
                      Icons.read_more,
                      size: 28,
                      color: Colors.white,
                    ),
                  ),
                if (widget.char.properties
                    .contains(CharacteristicProperties.WRITE))
                  GestureDetector(
                    onTap: () => _write(widget.char),
                    child: const Icon(
                      Icons.edit_note,
                      size: 28,
                      color: Colors.green,
                    ),
                  ),
                if (widget.char.properties
                    .contains(CharacteristicProperties.NOTIFY))
                  GestureDetector(
                    onTap: () => BleSdk.instance.setNotification(widget.char),
                    child: const Icon(
                      Icons.notifications_on,
                      size: 28,
                      color: Colors.redAccent,
                    ),
                  ),
                if (widget.char.properties
                    .contains(CharacteristicProperties.INDICATE))
                  GestureDetector(
                    onTap: () => BleSdk.instance.setNotification(widget.char),
                    child: const Icon(
                      Icons.drag_indicator_rounded,
                      size: 28,
                      color: Colors.redAccent,
                    ),
                  ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  _write(Characteristic char) {
    showDialog(
      context: context,
      builder: (context) => Material(
        type: MaterialType.transparency,
        child: Center(
          child: WriteCharacteristicWidget(char: widget.char),
        ),
      ),
    );
  }
}

class WriteCharacteristicWidget extends StatefulWidget {
  const WriteCharacteristicWidget({
    super.key,
    required this.char,
  });
  final Characteristic char;

  @override
  State<WriteCharacteristicWidget> createState() =>
      _WriteCharacteristicWidgetState();
}

class _WriteCharacteristicWidgetState extends State<WriteCharacteristicWidget> {
  final ctlValue = TextEditingController(text: '4');
  @override
  Widget build(BuildContext context) {
    return Container(
      width: MediaQuery.of(context).size.width - 50,
      padding: const EdgeInsets.all(10),
      height: 300,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Text(
            'Write value characteristic: ${widget.char.characteristicId.replaceAll('0000', '').split('-').first}',
            style: const TextStyle(
              fontSize: 16,
              fontWeight: FontWeight.w600,
            ),
          ),
          const Spacer(),
          TextFormField(
            controller: ctlValue,
            decoration: const InputDecoration(
              hintText: '0 or 0,1,2,3,4 ...',
              border: OutlineInputBorder(),
            ),
          ),
          const Spacer(),
          GestureDetector(
            onTap: () => BleSdk.instance.writeCharacteristic(
              CharacteristicValue(
                characteristic: widget.char,
                data: ctlValue.text.split(',').map(
                      (e) => int.parse(e),
                    ),
              ),
            ),
            child: Container(
              decoration: BoxDecoration(
                border: Border.all(
                  color: Colors.green,
                ),
                borderRadius: BorderRadius.circular(8),
                color: Colors.blue,
              ),
              padding: const EdgeInsets.symmetric(
                vertical: 6,
                horizontal: 20,
              ),
              child: const Text(
                'Write',
              ),
            ),
          ),
        ],
      ),
    );
  }
}
1
likes
140
points
79
downloads

Publisher

unverified uploader

Weekly Downloads

Bluetooth Low Energy (BLE) plugin that can communicate with single device

Repository (GitHub)

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

flutter, plugin_platform_interface, protobuf

More

Packages that depend on ble_sdk