1

I am using flutter_blue_plus for getting bluetooth is on or off. I am getting results in log successfully. But in isOn function, I can't update automatically. What can I do? Do I have to use StreamBuilder in UI?

class BluetoothService with Logger {
  BluetoothService() {
    log.fine('listening to bluetooth status changes');

    FlutterBluePlus.instance.state.listen((result) {
      log.fine('bluetooth state changed to ${result.name}');
      _lastResult = result;
    });
  }
  BluetoothState _lastResult = BluetoothState.unknown;

  /// The last [BluetoothState] that is updated automatically whenever the
  /// state changes.

  bool get isOn => _lastResult == BluetoothState.on;

  Future<void> initialize() async {
    try {
      await FlutterBluePlus.instance.isAvailable;
    } catch (e, st) {
      log.warning('unable to initialize bluetooth', e, st);
    }
  }
}
        

class BluetoothDeviceList extends StatelessWidget {
  const BluetoothDeviceList({Key? key}) : super(key: key);
  static const route = 'bluetoothdevicelist';

  @override
  Widget build(BuildContext context) {
    return app<BluetoothService>().isOn
        ? Scaffold(
            body: Center(child: Text('DEVICES')),
          )
        : Scaffold(
            body: Center(child: Text('Bluetooth Close')),
          );
  }
}

1 Answers1

2

This is definitely a situation where you need to use some state management approach. In our team we use flutter_bloc, in which case your solution would look something like this.

class BluetoothCubit extends Cubit<BluetoothState> with Logger {
  BluetoothCubit() : super(BluetoothState.unknown) {
    log.fine('listening to bluetooth status changes');

    FlutterBluePlus.instance.state.listen((result) {
      log.fine('bluetooth state changed to ${result.name}');
      emit(result);
    });
  }

  Future<void> initialize() async {
    try {
      await FlutterBluePlus.instance.isAvailable;
    } catch (e, st) {
      log.warning('unable to initialize bluetooth', e, st);
    }
  }
}

class BluetoothDeviceList extends StatelessWidget {
  static const route = 'bluetoothdevicelist';

  const BluetoothDeviceList({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) => BlocProvider(
        create: (_) => BluetoothCubit()..initialize(),
        child: BlocBuilder<BluetoothCubit, BluetoothState>(
          builder: (context, state) => Scaffold(
            body: Center(
              child: Text(
                state == BluetoothState.on ? 'DEVICES' : 'Bluetooth Close',
              ),
            ),
          ),
        ),
      );
}

One of the key changes is that in your stream listener you call emit(result). This informs the BlocBuilder that a new state needs to be displayed.

There are other ways to handle this, but this is the standard approach in our company.

Guy Kogus
  • 7,251
  • 1
  • 27
  • 32
  • Can I ask you one more question? Should I write all Bluetooth functions like scanning, writing or reading, etc on one cubit or should I write cubits for each? – Mevlüt Tosun Apr 19 '22 at 10:48
  • 1
    I'd go for separate cubits, personally. Otherwise you can end up with cubits that are massive, and their states will be hard to manage cos of the different type of information they might hold. – Guy Kogus Apr 21 '22 at 10:46