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.