Search code examples
flutterflutter-streambuilder

How can I update my get function when stream changes state?


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')),
          );
  }
}

Solution

  • 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.