how to test the GraphQL subscription with flutter

199 Views Asked by At

I have this endpoint for graphql api:

http://localhost:2000/graphql

when I try this on the playground, its works:

mutation {
  createMessage(messageInput: {text: "noti", username: "osama alatraqchi"}) {
    text, createdBy
  }
}
and run subscription:
subscription {
  messageCreated {
     createdBy text
  }
}

no problem and it works both mutation and subscription in the apollo-server playground, now I have tried to implement this in Flutter using bloc pattern, I did this: in repository.dart:

import 'dart:convert';
import 'package:graphql_flutter/graphql_flutter.dart';


class MessageRepository {
  late final GraphQLClient _client;

  MessageRepository() {
    final HttpLink httpLink = HttpLink('http://localhost:2000/graphql');
    final WebSocketLink webSocketLink = WebSocketLink('ws://localhost:2000/graphql');

    _client = GraphQLClient(
      cache: GraphQLCache(),
      link: Link.split(
            (request) => request.isSubscription,
        webSocketLink,
        httpLink,
      ),
    );
  }

  Stream<dynamic> subscribeToMessageCreated() {
    final requestBody = gql(r'''
      subscription {
        messageCreated {
          text
          createdBy
        }
      }
    ''');

    return _client.subscribe(Operation(document: requestBody) as SubscriptionOptions).map((response) {
      final messageCreated = response.data?['messageCreated'];
      return messageCreated;
    });
  }

  Future<List<dynamic>> fetchTodos() async {
    final requestBody = gql(r'''
      query {
        messages {
          text
          createdBy
        }
      }
    ''');

    final result = await _client.query(QueryOptions(document: requestBody));

    if (!result.hasException) {
      final messages = result.data?['messages'];
      return messages;
    } else {
      print('Error fetching messages: ${result.exception}');
      return [];
    }
  }
}

and in bloc.dart:

import 'dart:async';

import 'package:flutter_bloc/flutter_bloc.dart';
import '../data/repository.dart';
import 'message_events.dart';
import 'message_states.dart';

class MessageBloc extends Bloc<MessageEvent, MessageState> {
  final MessageRepository messageRepository;
  StreamSubscription<dynamic>? _subscription;

  MessageBloc(this.messageRepository) : super(MessageLoading()) {
    on<FetchTodos>((event, emit) async {
      emit(MessageLoading());
      try {
        final todos = await messageRepository.fetchTodos();
        emit(MessageLoaded(todos));
      } catch (e) {
        emit(MessageError(msg: e.toString()));
        print('Error fetching todos: $e');
      }
    });

    on<SubscribeToMessageCreated>((event, emit) async {
      _subscription?.cancel();
      // Listen to the subscription stream
      _subscription = messageRepository.subscribeToMessageCreated().listen((message) {
        if (message != null) {
          emit(MessageCreated(message['createdBy'], message['text']));
        }
      });
    });
  }

  @override
  Future<void> close() {
    // Cancel the subscription when the bloc is closed
    _subscription?.cancel();
    return super.close();
  }
}

and in main.dart:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gql_subscription/ui/messages_screen.dart';
import '../bloc/message_bloc.dart';
import 'data/repository.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: RepositoryProvider(
        create: (context) => MessageRepository(),
        child: BlocProvider<MessageBloc>(
          create: (context) => MessageBloc(context.read<MessageRepository>())
           ,
          child: MessagesScreen(),
        ),
      ),
    );
  }
}

and with scree.dart:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../bloc/message_bloc.dart';
import '../bloc/message_events.dart';
import '../bloc/message_states.dart';
import 'widgets/cart_widget.dart';

class MessagesScreen extends StatefulWidget {
  @override
  State<MessagesScreen> createState() => _MessagesScreenState();
}

class _MessagesScreenState extends State<MessagesScreen> {

  @override
  void initState() {
    super.initState();
    _fetchData();
    _subscribeToMessages();
  }

  void _fetchData() {
    final messageBloc = BlocProvider.of<MessageBloc>(context);
    messageBloc.add(FetchTodos());
  }

  void _subscribeToMessages() {
    final messageBloc = BlocProvider.of<MessageBloc>(context);
    messageBloc.add(SubscribeToMessageCreated());
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.pinkAccent,
        title: Text('GraphQL API - Subscription'),
      ),
      body: BlocConsumer<MessageBloc, MessageState>(
        listener: (context, state) {
          if(state is MessageError) {
             ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text(state.msg))
            );
          } else if(state is MessageCreated) {
            ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(content: Text("new message has came")));
          }
        },
        builder: (context, state) {
          if (state is MessageLoading) {
            return Center(child: CircularProgressIndicator());
          } else if (state is MessageLoaded) {
            final messages = state.messages;
            return ListView.builder(
              itemCount: messages.length,
              itemBuilder: (context, index) {
                final todo = messages[index];
                return cartWidget(todo);
              },
            );
          }else if (state is MessageCreated) {
            return ListTile(
              title: Text(state.text),
              subtitle: Text('Created by: ${state.createdBy}'),
            );
          }
          return Container();
        },
      ),
    );
  }
}

now I have tried it with a browser (flutter run -d edge) because it doesn't run in an emulator with (localhost). However, in the browser, it did work, and fetched all messages without any problem, but still, when I tried to add data from the Apollo-server playground, nothing happened in the application, without making a restart.

0

There are 0 best solutions below