zino-hofmann / graphql-flutter

A GraphQL client for Flutter, bringing all the features from a modern GraphQL client to one easy to use package.
https://zino-hofmann.github.io/graphql-flutter
MIT License
3.25k stars 620 forks source link

Subscription doesn't disconnect from the server when you stop listening to the stream. #1211

Open inc16sec opened 2 years ago

inc16sec commented 2 years ago

When I listen to a subscription it connects to the server but when I stop listening to the subscription by canceling the StreamSubscription it stays connected to the server and if I navigate to the page again (which listens to the subscription) then I will have two connections and data is returned twice.

This is how I implement my subscription;

The stream controller:

StreamSubscription<QueryResult<Object?>>? _newMessageSubscription;

Initializing the subscription:

_newMessageSubscription = client.subscribe(MessagesOptions().newMessageCreatedOptions).listen(handleUpdatedData);

Canceling the stream Controller:

void dispose() {
    _newMessageSubscription?.cancel();
  }

The desired result is for the subscription to disconnect from the server once I call dispose() when popping out of a screen. But the subscription stays connected and the server's console shows that it hasn't disconnected, unlike when I connect from the browser, once I click on disconnect, the server's console logs "DISCONNECTED".

Is this a bug or do I just have to implement it a different way?

vincenzopalazzo commented 2 years ago

I need to look into it

GujjuFlutterGuy commented 2 years ago

@vincenzopalazzo Kind of same with me too. I want to dispose of my subscription call once I go to the background. Currently, If I killed the app it gets disconnected. But when we go to the background the connection is still connected, And I am getting ping/pong on the server side.

This is the code:

import 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';

void main() => runApp(
      MaterialApp(
        title: 'GraphQL Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: Builder(
          builder: (BuildContext context) => Scaffold(
            appBar: AppBar(
              title: const Text('GraphQL Demo App'),
            ),
            body: ReviewFeed(),
          ),
        ),
      ),
    );

class ReviewFeed extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Link link = HttpLink('http://192.168.43.97:4000/graphql');

    final websocketLink = WebSocketLink('ws://192.168.43.97:4000/graphql',
        subProtocol: GraphQLProtocol.graphqlTransportWs,
        config: const SocketClientConfig(
          autoReconnect: true,
          inactivityTimeout: null,
        ));

    link = Link.split((request) => request.isSubscription, websocketLink, link);

    final client = ValueNotifier<GraphQLClient>(
      GraphQLClient(
        cache: GraphQLCache(),
        link: link,
      ),
    );

    return GraphQLProvider(
      client: client,
      child: Subscription(
        options: SubscriptionOptions(
          document: gql(
            r'''
                subscription{
                  newNotification {
                    message
                  }
                }
              ''',
          ),
        ),
        builder: (result) {
          if (result.hasException) {
            return Text(result.exception.toString());
          }

          if (result.isLoading) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }

          if (result.isNotLoading) {
            print(result.data.toString());
          }
          return ResultAccumulator.appendUniqueEntries(
              latest: result.data,
              builder: (context, {results}) {
                String msg = result.data!['newNotification']['message'];
                return Center(child: Text("Messgae: ${msg}"));
              });
        },
      ),
    );
  }
}

I am using the 5.1.1-beta.4 version right now. I want to know how it will be done.

I want to stop the connection once goes to the background and need to resume the connection once came to foreground.

Arpit1496 commented 2 years ago

@inc16sec Does your subscription connect again when you come back to screen or you get "bad state: stream has already been listened to." error ?

inc16sec commented 2 years ago

it stays connected to the server and if I navigate to the page again (which listens to the subscription) then I will have two connections and data is returned twice

As mentioned above it stays connected to the server and if I navigate to the page again (which listens to the subscription) then I will have two connections and data is returned twice. I get no errors.

PcolBP commented 1 year ago

I think that it should be priority: High. Right now in mobile as well in web application, once we will redirect to another page and cancel subscription from subscribe we are receiving data all the time. It should stop emiting data once the subscription is canceled.

On mobile app stream still lives even after we will minimize application. In scenario of "chat" stream under the hood marks messages as seen because stream lives underneath.

Im using 5.1.2 graphql_flutter.

Flutter doctor:

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.3.10, on macOS 13.0.1 22A400 darwin-arm (Rosetta), locale pl-PL)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0-rc2)
[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.3)
[✓] VS Code (version 1.74.3)
[✓] Connected device (3 available)
[✓] HTTP Host Availability

@vincenzopalazzo If you need more information just tell me. I'll be glad to help.

irshad-hz commented 1 year ago

Any update on this? while dispose If we also dispose of the websocketlink, it stop listening. Is that the proper way to dispose the websocket?