flutterdartdart-pubandroid-ble

How to get deviceServices from flutter_reactive_ble example


I'm working with the example that is provided in the flutter_reactive_ble project (https://github.com/PhilipsHue/flutter_reactive_ble), specifically on the device_detail_screen.dart file.

It works as much as I believe it should work, but I'd like to know how to save the result of discoverServices(device.id) as a variable, so that I can show it in a text widget or something (that's the plan at least). Every time I try to do this, I get an error saying: This expression has a type of 'void' so its value can't be used.

I believe that this is because it is of type Future<List< DiscoveredService>> (see below for declaration)

Future<List< DiscoveredService>> discoverServices (String deviceId)

I'm pretty new to this, so I'm just trying to figure out how all of this works.

Whenever I click on the "Discover Services" button, all of the services show up in the terminal of my computer, but I want them to ultimately show up on the device.

Here's the actual code if you don't want to check out the entire project. I'm pretty sure this is the file that applies. I've put "***" on the lines that I'm inquiring about.

Let me know if you need more info or clarification. Thanks!

import 'package:flutter/material.dart';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:flutter_reactive_ble_example/src/ble/ble_device_connector.dart';
import 'package:provider/provider.dart';

class DeviceDetailScreen extends StatelessWidget {
  final DiscoveredDevice device;

  const DeviceDetailScreen({@required this.device}) : assert(device != null);

  @override
  Widget build(BuildContext context) =>
      Consumer2<BleDeviceConnector, ConnectionStateUpdate>(
        builder: (_, deviceConnector, connectionStateUpdate, __) =>
            _DeviceDetail(
          device: device,
          connectionUpdate: connectionStateUpdate != null &&
                  connectionStateUpdate.deviceId == device.id
              ? connectionStateUpdate
              : ConnectionStateUpdate(
                  deviceId: device.id,
                  connectionState: DeviceConnectionState.disconnected,
                  failure: null,
                ),
          connect: deviceConnector.connect,
          disconnect: deviceConnector.disconnect,
          discoverServices: deviceConnector.discoverServices,
        ),
      );
}

class _DeviceDetail extends StatelessWidget {
  const _DeviceDetail({
    @required this.device,
    @required this.connectionUpdate,
    @required this.connect,
    @required this.disconnect,
    @required this.discoverServices,
    Key key,
  })  : assert(device != null),
        assert(connectionUpdate != null),
        assert(connect != null),
        assert(disconnect != null),
        assert(discoverServices != null),
        super(key: key);

  final DiscoveredDevice device;
  final ConnectionStateUpdate connectionUpdate;
  final void Function(String deviceId) connect;
  final void Function(String deviceId) disconnect;
  final void Function(String deviceId) discoverServices;

  bool _deviceConnected() =>
      connectionUpdate.connectionState == DeviceConnectionState.connected;

  @override
  Widget build(BuildContext context) => WillPopScope(
        onWillPop: () async {
          disconnect(device.id);
          return true;
        },
        child: Scaffold(
          appBar: AppBar(
            title: Text(device.name ?? "unknown"),
          ),
          body: Center(
            child: SafeArea(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Text(
                      "ID: ${connectionUpdate.deviceId}",
                      style: const TextStyle(fontWeight: FontWeight.bold),
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Text(
                      "Status: ${connectionUpdate.connectionState}",
                      style: const TextStyle(fontWeight: FontWeight.bold),
                    ),
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    children: <Widget>[
                      Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: ElevatedButton(
                          onPressed: !_deviceConnected()
                              ? () => connect(device.id)
                              : null,
                          child: const Text("Connect"),
                        ),
                      ),
                      Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: ElevatedButton(
                          onPressed: _deviceConnected()
                              ? () => disconnect(device.id)
                              : null,
                          child: const Text("Disconnect"),
                        ),
                      ),
                    ],
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: ElevatedButton(
                          // onPressed: _deviceConnected()                    // *** original code from example
                          //     ? () => discoverServices(device.id)          // *** original code from example
                          //     : null,                                      // *** original code from example
                          onPressed: (){
                            if (_deviceConnected()){                          // *** My attempt at storing as a variable (this entire anonymous function)
                              var services = discoverServices(device.id);
                              print("services: $services");                   // *** services is underlined with an error: This expression has a type of 'void' so its value can't be used.
                            }
                          },
                          child: const Text("Discover Services"),
                        ),
                      ),
                    ],
                  ),
                  Text("data"),                                               // *** Where I'd like to populate the results of the discoverServices(device.id)
                ],
              ),
            ),
          ),
        ),
      );
}

Solution

  • I'm expecting that your BleDeviceConnector is equal to this.

    If you want to use the value List<DiscoveredService> from FlutterReactiveBle::discoverServices(...), you must modify your BleDeviceConnector::discoverServices(...) function to...

    Future<List<DiscoveredService>> discoverServices(String deviceId) => _ble.discoverServices(deviceId);
    

    And your onPressed callback to...

    onPressed: () async {
        if (_deviceConnected()) {
            var services = await discoverServices(device.id);
            print("services: $services");
            // TODO: Insert your code to update the UI of "Text("data"),"
        }
    },
    

    EDIT: You also need to modify _DeviceDetail's discoverServices to...

    final Future<List<DiscoveredService>> Function(String deviceId) discoverServices;