Disc
Disc

Reputation: 61

How to rebuild widget in stateful class when stream gets new value in a class outside of widget tree?

What are some ways to rebuild a class's widgets on value changes from a stream in a different class with no build context? Can't use setState from a different class unless I force it to update here using a timer, Provider needs context, ValueListenableBuilder didn't work. Do I need to add context to the class? That feels wrong though. I also could be using the above state management tools wrong, so if you think one of them can work, please point it out.

I have a version that functions using setState and periodic timers, but the accuracy of it is bad, so I need to have it update based on when the values from the stream change.

Code that outlines problem:

Here is a snippet of my singleton ble class that has a stream of data coming from a device.

Class outside of widget tree

Class BleInteractions {
  static BleInteractions? _instance;
  int? valueFromStream;
  FlutterReactiveBle? ble;

  static BleInteractions? get instance {
    if (_instance == null) {
      _instance = BleInteractions._internal();
    }
    return _instance;
  }

BleInteractions._internal() {
ble = FlutterReactiveBle();
valueFromStream = null;
}

//stream function snippet
    streamSubscription = ble?.subscribeToCharacteristic(characteristic).listen((data) async {
    valueFromStream = data;
    
    }
}

And here's a class that takes that value and needs to rebuild when the stream changes to a different value. There's a lot more happening in this class normally, but I've removed it all because even a basic example in an empty widget build would be useful to see.

Stateful widget class that needs to use values from above class

            class ForceGraph extends StatefulWidget {
            const ForceGraph({ Key? key}): super(key: key);
            
              @override
              State<ForceGraph> createState() => _ForceGraphState();
            
            class _ForceGraphState extends State<ForceGraph> {
            final bleInteractions = BleInteractions.instance;
            
              @override
              void initState() {
                super.initState();
                initPageAfterStream();
              }
        
          void initPageAfterStream() async {
            streamStarted = activateStream();
            } 
            
          Future<bool> activateStream() async {
        //await stream function happens here
        return Future.value(true);
          }

  @override
  Widget build(BuildContext context) {

    return FutureBuilder<bool>(
        future: streamStarted,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(child: Text('Please wait, page loading...'));
          } else   
return bleInteractions?.valueFromStream != null
       ? Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: [ 
    //normally have a graph that makes use of those values in here and in some other 
    functions, but even an updating text widget based on the new value from the stream 
    would be good.
    Text(bleInteractions.valueFromStream)]
    ) : Container();
    });
}
}

Upvotes: 0

Views: 407

Answers (1)

Robert Sandberg
Robert Sandberg

Reputation: 8607

There are several ways to do it. I myself have used flutter_bloc when dealing with streams from BLE devices. How it works:

  • Create a bloc/cubit which get a "BLE service" injected to it.
  • In the bloc/cubit have a listener that updates the blocs state when values have changed according to your preferences.
  • In the UI, listen to changes from the bloc/cubit and rebuild the UI based on these state changes.

Upvotes: 1

Related Questions