Search code examples
flutterdartstreamflutter-getx

flutter getx web sockets - stream data to be cascaded to provider and controller


Objective is simple

  • flutter app makes a call to graphql api over websockets
  • app view calls the controller, controller calls the provider, provider calls the AWS appsync api over websockets or over HTTP api socket call
  • we receive a stream of data from appsync api or HTTP api socket call over websockets every now and then from backend
  • streams need to be cascaded back to provider , and then to controller (this is the critical step)
  • controller (not the provider) would update the obs or reactive variable, make the UI reflect the changes

problem : data is recieved via websockets in the caller, but never passed back as stream to provider or controller to reflect the changes

sample code

actual caller orderdata.dart

  @override
  Stream<dynamic> subscribe({
    String query,
    Map<String, dynamic> variables,
  }) async* {
    debugPrint('===->subscribe===');
    // it can be any stream here, http or file or image or media
    final Stream<GraphQLResponse<String>> operation = Amplify.API.subscribe(
      GraphQLRequest<String>(
        document: query,
        variables: variables,
      ),
      onEstablished: () {
        debugPrint(
          '===->subscribe onEstablished ===',
        );
      },
    );

    operation.listen(
      (event) async* {
        final jsonData = json.decode(event.data.toString());
        debugPrint('===->subscription data $jsonData');
        yield jsonData;
      },
      onError: (Object e) => debugPrint('Error in subscription stream: $e'),
    );
  }

in the provider orderprovider.dart

  Stream<Order> orderSubscription(String placeId) async* {
    debugPrint('===->=== $placeId');
    subscriptionResponseStream = orderData.subscribe(
      query: subscribeToMenuOrder,
      variables: {"place_id": placeId},
    );

    subscriptionResponseStream.listen((event) async* {
      debugPrint(
        "===->=== yielded $event",
      );
      yield event;
    });
    debugPrint('===->=== finished');
  }

in the controller homecontroller.dart

  Future<void> getSubscriptionData(String placeId) async {
    debugPrint('===HomeController->getSubscriptionData===');
    OrderProvider().orderSubscription(placeId).listen(
          (data) {
            //this block is executed when data event is receivedby listener
            debugPrint('Data: $data');
            Get.snackbar('orderSubscription', data.toString());
          },
          onError: (err) {
            //this block is executed when error event is received by listener
            debugPrint('Error: $err');
          },
          cancelOnError:
              false, //this decides if subscription is cancelled on error or not
          onDone: () {
            //this block is executed when done event is received by listener
            debugPrint('Done!');
          },
        );
  }

homeview calls homecontroller


Solution

  • Try using map for transforming Streams:

     @override
      Stream<dynamic> subscribe({
        String query,
        Map<String, dynamic> variables,
      }) {
        debugPrint('===->subscribe===');
        // it can be any stream here, http or file or image or media
        final Stream<GraphQLResponse<String>> operation = Amplify.API.subscribe(
          GraphQLRequest<String>(
            document: query,
            variables: variables,
          ),
          onEstablished: () {
            debugPrint(
              '===->subscribe onEstablished ===',
            );
          },
        );
        
        return operation.map((event) {
          return json.decode(event.data);
        });
      }
    
      // elsewhere
    
      final subscription = subscribe(
        query: 'some query', 
        variables: {},
      );
    
      subscription.listen(
        (jsonData) {
          debugPrint('===->subscription data $jsonData');
        },
        onError: (Object e) => debugPrint('Error in subscription stream: $e'),
      );