so I'm having this really weird bug connecting to the backend ws server.
flutter 2.10.3
dart 2.16.1
socket_io_client ^2.0.0-beta.4-nullsafety.0
If I start the app, and go through the login process, it doesn't connect, doesn't show any error, or any event. there's nothing at all. (but it sometimes works, it's totally random but let's say it does 1/10 times)
And if I reload the app, it connects successfully. also when I add breakpoints in the code, it works fine.
// ignore_for_file: avoid_print, cascade_invocations
import 'package:bloc/bloc.dart';
import 'package:dealize/core/helpers/flutter_secure_storage_helper.dart';
import 'package:dealize/features/chat/data/models/channel_model.dart';
import 'package:dealize/features/chat/data/models/message_model.dart';
import 'package:dealize/features/chat/domain/entities/channel.dart';
import 'package:dealize/features/chat/domain/entities/message.dart';
import 'package:equatable/equatable.dart';
import 'package:socket_io_client/socket_io_client.dart' as io;
part 'chat_event.dart';
part 'chat_state.dart';
class ChatBloc extends Bloc<ChatEvent, ChatState> {
ChatBloc({
required this.socketUrl,
required this.secureStorageHelper,
}) : super(
const ChatState(
channels: <Channel>[],
messages: <Message?>[],
),
) {
on<Connect>(
(event, emit) async {
try {
final accessToken = await secureStorageHelper.read('accessToken');
_socket = io.io(
socketUrl,
io.OptionBuilder()
.setTransports(['websocket'])
.setAuth(
<String, dynamic>{
'token': accessToken,
},
)
.disableAutoConnect()
.build(),
);
_socket
..onAny((event, dynamic data) {
print('EVENT ===> $event');
print('DATA ===> $data');
})
..onDisconnect((dynamic data) {
print('Disconnected: $data');
})
..on('channel', (dynamic data) async {
final channel =
ChannelModel.fromJson(data as Map<String, dynamic>);
add(AddedChannel(channel: channel));
})
..on('participant', (dynamic data) async {
print('Participant ===> $data');
// todo find participant and update it
// todo add updatedChannel event
})
..on('message', (dynamic data) async {
final message =
MessageModel.fromJson(data as Map<String, dynamic>);
add(AddedMessage(message: message));
add(MessageDelivered());
})
..on('exception', (dynamic data) async {
print('Error ===> $data');
// todo show toast message
});
_socket.connect();
} catch (e) {
print(e.toString());
}
},
);
on<AddedChannel>(
(event, emit) =>
emit(state.copyWith(channels: [...state.channels, event.channel])),
);
on<AddedMessage>(
(event, emit) {
final currentChannelIndex = state.channels.indexWhere(
(channel) => channel.id == event.message.channel,
);
final currentChannel = state.channels.removeAt(currentChannelIndex);
final messageIndex = currentChannel.messages
.indexWhere((element) => element!.id == event.message.id);
if (messageIndex == -1) {
currentChannel.messages.add(event.message);
} else {
currentChannel.messages[messageIndex] = event.message;
}
emit(
state.copyWith(
channels: [...state.channels, currentChannel],
),
);
},
);
on<CreateChannel>((event, emit) {
final phone = '+${state.dialCode}${state.phone}';
_socket.emit('create_channel', {
'phone': phone,
});
});
on<CreateMessage>((event, emit) {
_socket.emit('create_message', {
'channelId': event.channelId,
'type': event.type.name,
'text': state.newMessage,
});
});
on<MessageDelivered>((event, emit) {
_socket.emit('message_delivered', {
'': '',
});
});
on<MessageRead>((event, emit) {
_socket.emit('message_read', {
'': '',
});
});
on<PhoneNumberUpdated>(
(event, emit) => emit(state.copyWith(phone: event.phone)),
);
on<DialCodeUpdated>(
(event, emit) => emit(state.copyWith(dialCode: event.dialCode)),
);
on<NewMessageUpdated>(
(event, emit) => emit(state.copyWith(newMessage: event.message)),
);
on<EnterChannel>((event, emit) {
emit(
state.copyWith(
channelId: event.channelId,
messages: state.channels
.firstWhere((element) => element.id == event.channelId)
.messages,
),
);
add(MessageRead());
});
on<ExitChannel>(
(event, emit) => emit(
state.copyWith(
channelId: '',
messages: [],
),
),
);
}
final String socketUrl;
final FlutterSecureStorageHelper secureStorageHelper;
late final io.Socket _socket;
}
If anyone has an idea it would be really helpful, or a better way to implement socket io connection in flutter through BLoC
Related
I'm using flutter_app_badger and Firebase Messaging to set a notification system which would update number in app badger when a new notification is coming. I'm working in Flutterflow so my approach might be a bit awkward. So far I managed to do two things:
Create a separate function in the app which updates app badger while app is working;
Set the notification system that is triggered by the action in app;
But I can't combine the two. I have the following push_notification_handler code and trying how to put FlutterAppBadger.updateBadgeCount() function it.
import 'dart:async';
import 'dart:convert';
import 'serialization_util.dart';
import '../backend.dart';
import '../../flutter_flow/flutter_flow_theme.dart';
import '../../flutter_flow/flutter_flow_util.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import '../../index.dart';
import '../../main.dart';
final _handledMessageIds = <String?>{};
class PushNotificationsHandler extends StatefulWidget {
const PushNotificationsHandler({Key? key, required this.child})
: super(key: key);
final Widget child;
#override
_PushNotificationsHandlerState createState() =>
_PushNotificationsHandlerState();
}
class _PushNotificationsHandlerState extends State<PushNotificationsHandler> {
bool _loading = false;
Future handleOpenedPushNotification() async {
if (isWeb) {
return;
}
final notification = await FirebaseMessaging.instance.getInitialMessage();
if (notification != null) {
await _handlePushNotification(notification);
}
FirebaseMessaging.onMessageOpenedApp.listen(_handlePushNotification);
}
Future _handlePushNotification(RemoteMessage message) async {
if (_handledMessageIds.contains(message.messageId)) {
return;
}
_handledMessageIds.add(message.messageId);
if (mounted) {
setState(() => _loading = true);
}
try {
final initialPageName = message.data['initialPageName'] as String;
final initialParameterData = getInitialParameterData(message.data);
final pageBuilder = pageBuilderMap[initialPageName];
if (pageBuilder != null) {
final page = await pageBuilder(initialParameterData);
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => page),
);
}
} catch (e) {
print('Error: $e');
} finally {
if (mounted) {
setState(() => _loading = false);
}
}
}
#override
void initState() {
super.initState();
handleOpenedPushNotification();
}
#override
Widget build(BuildContext context) => _loading
? Container(
color: FlutterFlowTheme.of(context).primaryBtnText,
child: Image.asset(
'assets/images/Screen_Shot_2022-11-22_at_12.59.57.png',
fit: BoxFit.contain,
),
)
: widget.child;
}
final pageBuilderMap = <String, Future<Widget> Function(Map<String, dynamic>)>{
'SignInSignUpPage': (data) async => SignInSignUpPageWidget(),
'ForgotPasswordPage': (data) async => ForgotPasswordPageWidget(),
'ContactsListPage': (data) async => ContactsListPageWidget(
tabToOpen: getParameter(data, 'tabToOpen'),
),
'SyncProcessPage': (data) async => SyncProcessPageWidget(),
'SyncConfirmationPage': (data) async => SyncConfirmationPageWidget(),
'ContactDetailsPage': (data) async => ContactDetailsPageWidget(
idReference: getParameter(data, 'idReference'),
comment: getParameter(data, 'comment'),
),
'ContactDeletePage': (data) async => ContactDeletePageWidget(
idReference: getParameter(data, 'idReference'),
comment: getParameter(data, 'comment'),
),
'ContactInvitationPage': (data) async => ContactInvitationPageWidget(
idReference: getParameter(data, 'idReference'),
),
'AddConfirmationAfterInvitePage': (data) async =>
AddConfirmationAfterInvitePageWidget(
uID: getParameter(data, 'uID'),
phraseState: getParameter(data, 'phraseState'),
),
'AddContactsOptionPage': (data) async => AddContactsOptionPageWidget(),
'SearchNewContactsPage': (data) async => SearchNewContactsPageWidget(),
'NewContactInvitationConfirmationPage': (data) async =>
NewContactInvitationConfirmationPageWidget(),
'NewContactScanPage': (data) async => NewContactScanPageWidget(),
'AddConfirmationAfterScanPageNew': (data) async =>
AddConfirmationAfterScanPageNewWidget(
uID: getParameter(data, 'uID'),
phraseState: getParameter(data, 'phraseState'),
),
'AddConfirmationAfterScanPageOld': (data) async =>
AddConfirmationAfterScanPageOldWidget(
uID: getParameter(data, 'uID'),
phraseState: getParameter(data, 'phraseState'),
),
'ShareProfileOptionsPage': (data) async => ShareProfileOptionsPageWidget(),
'InAppQRcodeSharePage': (data) async => InAppQRcodeSharePageWidget(),
'WebQRcodeSharePage': (data) async => WebQRcodeSharePageWidget(),
'ProfileEditPage': (data) async => ProfileEditPageWidget(),
'GameSettingsPage': (data) async => GameSettingsPageWidget(),
'GamePage': (data) async => GamePageWidget(),
'TutorialPage': (data) async => TutorialPageWidget(),
'AboutPage': (data) async => AboutPageWidget(),
'FAQPage': (data) async => FAQPageWidget(),
'PrivacyPolicyPage': (data) async => PrivacyPolicyPageWidget(),
};
bool hasMatchingParameters(Map<String, dynamic> data, Set<String> params) =>
params.any((param) => getParameter(data, param) != null);
Map<String, dynamic> getInitialParameterData(Map<String, dynamic> data) {
try {
final parameterDataStr = data['parameterData'];
if (parameterDataStr == null ||
parameterDataStr is! String ||
parameterDataStr.isEmpty) {
return {};
}
return jsonDecode(parameterDataStr) as Map<String, dynamic>;
} catch (e) {
print('Error parsing parameter data: $e');
return {};
}
}
Would be super grateful for any advice!
In the above error, I tried to get the parsedResponse and add handling for the statusCode, but I couldn't catch the error.
The linkException doesn't seem to recognize the parsedResponse. If you know a solution, please let me know.
thank you
Below is the graphql client I made and use using graphql_flutter
import 'package:firebase_auth/firebase_auth.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
// ignore: implementation_imports
import 'package:gql/src/ast/ast.dart';
import 'package:noling_app/config/parser.dart';
class MyGraphQLClient {
late GraphQLClient _client;
GraphQLClient get client => _client;
GraphQLClient setClient(String idToken) {
HttpLink _httpLink = HttpLink(
'http://localhost:5005/noling-develop/asia-northeast3/graphql',
parser: CustomResponseParser(),
defaultHeaders: {
'X-USER-TOKEN': idToken,
},
);
Link _link;
if (idToken != '' && idToken.isNotEmpty) {
final AuthLink authLink = AuthLink(
getToken: () => idToken,
headerKey: 'X-USER-TOKEN',
);
_link = authLink.concat(_httpLink);
} else {
_link = _httpLink;
}
_client = GraphQLClient(
cache: GraphQLCache(),
link: _link,
);
return _client;
}
Future<dynamic> query(
DocumentNode document, {
Map<String, dynamic>? data,
}) async {
try {
QueryResult result = await _client.query(QueryOptions(
document: document,
variables: data ?? {},
));
if (result.hasException) {
print(result);
var message = result.exception!.graphqlErrors.first.message;
throw Exception(message);
}
return result.data;
} catch (e) {
print("error catch ?");
rethrow;
}
}
Future<dynamic> mutate(
DocumentNode document, {
Map<String, dynamic>? data,
}) async {
var result = await _client.mutate(MutationOptions(
document: document,
variables: data ?? {},
));
if (result.hasException) {
var message = result.exception!.graphqlErrors.first.message;
throw GraphQLError(message: message);
}
return result.data;
}
}
MyGraphQLClient graphQLClient = MyGraphQLClient();
In the MyGraphQLClient class, I created and used a CustomResponseParser like the one I saw in another issue post, but to no avail.
class CustomResponseParser extends ResponseParser {
#override
Response parseResponse(Map<String, dynamic> body) {
Map<String, String> errors = new Map();
if (body["errors"] != null) {
errors['message'] = body["errors"][0];
}
Response res = Response(
response: body,
errors: (body["errors"] as List?)
?.map(
(dynamic error) => parseError(errors),
)
.toList(),
data: body["data"] as Map<String, dynamic>?,
context: const Context().withEntry(
ResponseExtensions(
body["extensions"],
),
),
);
print(res);
return res;
}
#override
GraphQLError parseError(Map<String, dynamic> error) {
return GraphQLError(
message: error['message'],
);
}
}
How to implement socket_io_client to flutter_bloc with freezed?
socket_bloc.dart
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:socket_io_client/socket_io_client.dart';
part 'socket_bloc.freezed.dart';
part 'socket_event.dart';
part 'socket_state.dart';
class SocketBloc extends Bloc<SocketEvent, SocketState> {
late final Socket _socket;
SocketBloc() : super(SocketState.initial()) {
_socket = io(
'http://localhost:1337',
OptionBuilder()
.setTimeout(3000)
.setReconnectionDelay(5000)
.disableAutoConnect()
.build(),
);
_socket.onConnecting((data) => add(_SocketConnectingEvent()));
_socket.onConnect((_) => add(_SocketOnConnect()));
_socket.onConnectError((data) => add(_SocketConnectErrorEvent()));
_socket.onConnectTimeout((data) => add(_SocketConnectTimeoutEvent()));
_socket.onDisconnect((_) => add(_SocketOnDisconnect()));
_socket.onError((data) => add(_SocketErrorEvent()));
_socket.on('joined', (data) => add(_SocketJoinedEvent()));
// User events
on<_SocketConnect>((event, emit) {
_socket.connect();
});
on<_SocketDisconnect>((event, emit) {
_socket.disconnect();
});
// Socket events
on<_SocketConnectingEvent>((event, emit) {
emit(SocketState.connected("Connecting"));
});
on<_SocketOnConnect>((event, emit) {
emit(SocketState.connected(_socket.id!));
});
on<_SocketConnectErrorEvent>((event, emit) {
emit(SocketState.connected("Connection Error"));
});
on<_SocketConnectTimeoutEvent>((event, emit) {
emit(SocketState.connected("Connection timeout"));
});
on<_SocketOnDisconnect>((event, emit) {
emit(SocketState.disconnected());
});
on<_SocketErrorEvent>((event, emit) {
emit(SocketState.connected("ErrorEvent"));
});
on<_SocketJoinedEvent>((event, emit) {
emit(SocketState.connected("JoinedEvent"));
});
}
#override
Future<void> close() {
_socket.dispose();
return super.close();
}
}
socket_event.dart
part of 'socket_bloc.dart';
#freezed
class SocketEvent with _$SocketEvent {
const factory SocketEvent.connect() = _SocketConnect;
const factory SocketEvent.connecting() = _SocketConnectingEvent;
const factory SocketEvent.onConnect() = _SocketOnConnect;
const factory SocketEvent.onConnectError() = _SocketConnectErrorEvent;
const factory SocketEvent.onConnectTimeout() = _SocketConnectTimeoutEvent;
const factory SocketEvent.onError() = _SocketErrorEvent;
const factory SocketEvent.onJoined() = _SocketJoinedEvent;
const factory SocketEvent.disconnect() = _SocketDisconnect;
const factory SocketEvent.onDisconnect() = _SocketOnDisconnect;
}
socket_state.dart
part of 'socket_bloc.dart';
#freezed
class SocketState with _$SocketState {
const factory SocketState.initial() = _SocketInitial;
const factory SocketState.connected(String status) = _SocketConnected;
const factory SocketState.disconnected() = _SocketDisonnected;
}
Also dont forget to run flutter pub run build_runner build --delete-conflicting-outputs
So I'm implementing blocTesting for my flutter project and I'm using real apis in testing as that is what I've been asked to do. Apparently when I'm trying out blocTesting for a feature that requires authentication the api is giving back a null response but in the running app it is working fine...
I'm adding the respective files below for the same. Also the name of feature is client and I'm trying to fetch all clients here.
Client_bloc_test.dart
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:injectable/injectable.dart' as inject;
import 'package:mynovatium/app/helper/injections/shared_preference/shared_preference_injection_configuration.dart';
import 'package:mynovatium/features/buy/models/test_model.dart';
import 'package:mynovatium/features/client/bloc/client_bloc.dart';
import 'package:mynovatium/features/client/models/private_client_model.dart';
import 'package:mynovatium/features/login/bloc/authentication/authentication_bloc.dart';
import 'package:mynovatium/features/login/repositories/authentication_repository.dart';
void main() async {
await configureInjection(inject.Environment.test);
final AuthenticationBloc authenticationBloc = AuthenticationBloc();
group('ClientBloc', () {
late PrivateClientModel client;
test('initial state of the bloc is [SignupInitial]', () {
expect(
ClientBloc(authenticationBloc: authenticationBloc).state,
ClientInitial(),
);
});
group('ClientCreateClient', () {
blocTest<ClientBloc, ClientState>(
'emits [ClientLoadInProgress, ClientLoadSuccess] '
'state when successfully fetched clients',
setUp: () async {
client = PrivateClientModel(1, List<TestModel>.empty(), createdAt: DateTime.now(), gender: 'Male', firstName: 'Nikunj', lastName: 'goyal', birthDate: '03/07/2001', ssnr: '0000', email: 'nik#gmail.com', telephoneNumber: '6641234123', street: 'Abcd', zipCode: '6020', city: 'Tyrol');
},
build: () => ClientBloc(authenticationBloc: authenticationBloc),
act: (ClientBloc bloc) => bloc..add(const ClientGetClients(101010)),
expect: () => <dynamic>[
const ClientLoadInProgress(),
],
);
});
});
}
Client_bloc.dart
Future<void> _onGetClients(ClientGetClients event, Emitter<ClientState> emit) async {
// Skip double loadings
if (state is ClientLoadInProgress) return;
PrivateClientModelList _pcml = PrivateClientModelList(<PrivateClientModel>[]);
final PrivateRedeemModel _prm = PrivateRedeemModel(customerId: event.userId, clients: <RedeemClient>[]);
if (state is ClientLoadSuccess) {
final ClientLoadSuccess _currentState = state as ClientLoadSuccess;
_pcml = _currentState.pcml;
try {
emit(const ClientLoadInProgress());
_pcml = await _clientRepository.getClientsForUser(event.userId);
} catch (e) {
final KBMException _exception = e as KBMException;
emit(ClientLoadFailure(exception: _exception));
}
return emit(_currentState.copyWith(pcml: _pcml));
} else {
try {
emit(const ClientLoadInProgress());
_pcml = await _clientRepository.getClientsForUser(event.userId);
for (final PrivateClientModel _client in _pcml.data) {
_prm.clients.add(RedeemClient(clientId: _client.id, quantity: 0));
}
} catch (e) {
final KBMException _exception = e as KBMException;
emit(ClientLoadFailure(exception: _exception));
}
return emit(ClientLoadSuccess(_pcml, _prm));
}
}
In the above code when 'getClientsForUser' is being called the response is null.
I'm using awesome_notifications and flutter_background_service in conjunction to update some app state when receiving data notifications from FirebaseMessaging. As noted in the awesome_notifications, the background message handler must be a top-level function, so I am using flutter_background_service to pass data to the main isolate and update app state.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeBackgroundService();
FirebaseMessaging.onBackgroundMessage(_backgroundMessageHandler);
_initLocalNotifications();
runApp(MyApp());
}
I'm initializing the background service similarly to the example in flutter_background_service:
Future<void> initializeBackgroundService() async {
final service = FlutterBackgroundService();
await service.configure(
androidConfiguration: AndroidConfiguration(
onStart: onStart,
autoStart: true,
isForegroundMode: true,
),
iosConfiguration: IosConfiguration(
autoStart: true,
onForeground: onStart,
onBackground: onIosBackground,
),
);
await service.startService();
}
and invoking update in the _backgroundMessageHandler when a notification is received:
Future<void> _backgroundMessageHandler(
RemoteMessage message,
) async {
final service = FlutterBackgroundService();
...
service.invoke('update', {
'key1': 'val1',
'key2': 'val2',
});
}
And in the StatefulWidget for my app in the main isolate, I'm listening on the update call to receive the data:
void listenForNotificationData() {
final backgroundService = FlutterBackgroundService();
backgroundService.on('update').listen((event) async {
print('received data message in feed: $event');
}, onError: (e, s) {
print('error listening for updates: $e, $s');
}, onDone: () {
print('background listen closed');
});
}
It's never invoking the listen callback on the 'update' event. I can confirm it's calling the invoke('update') portion and calling on('update').listen, but never receiving the update. It also doesn't seem to be erroring out. Am I missing a step somewhere here?
I was encountering the same issue on flutter background service. I solved it by removing the async keyword from the callback and creating a separate async function to perform the callback operations.
void listenForNotificationData() {
final backgroundService = FlutterBackgroundService();
backgroundService.on('update').listen((event) {
print('received data message in feed: $event');
}, onError: (e, s) {
print('error listening for updates: $e, $s');
}, onDone: () {
print('background listen closed');
});
}
void action(Map? event) async {
print('received data message in feed: $event');
}
Hope it helps, forgive me if there are syntax error
You can try this.
main(){
....
}
Future<void> readyForShared() async {
var sharedPreferences = await SharedPreferences.getInstance();
counterValue = sharedPreferences.getString("yourVariable") ?? "0";
}
Future<void> saveData(String value) async {
var sharedPreferences = await SharedPreferences.getInstance();
sharedPreferences.setString("yourVariable", value);
}
#pragma('vm:entry-point')
void onStart(ServiceInstance service) async {
// Only available for flutter 3.0.0 and later
DartPluginRegistrant.ensureInitialized();
// For flutter prior to version 3.0.0
// We have to register the plugin manually
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString("hello", "world");
/// OPTIONAL when use custom notification
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
if (service is AndroidServiceInstance) {
service.on('setAsForeground').listen((event) {
service.setAsForegroundService();
});
service.on('setAsBackground').listen((event) {
service.setAsBackgroundService();
});
}
service.on('stopService').listen((event) {
service.stopSelf();
});
// bring to foreground
Timer.periodic(const Duration(seconds: 1), (timer) async {
final receivePort = ReceivePort();
// here we are passing method name and sendPort instance from ReceivePort as listener
await Isolate.spawn(computationallyExpensiveTask, receivePort.sendPort);
if (service is AndroidServiceInstance) {
if (await service.isForegroundService()) {
//It will listen for isolate function to finish
// receivePort.listen((sum) {
// flutterLocalNotificationsPlugin.show(
// 888,
// 'Title',
// 'Description ${DateTime.now()}',
// const NotificationDetails(
// android: AndroidNotificationDetails(
// 'my_foreground',
// 'MY FOREGROUND SERVICE',
// icon: 'ic_bg_service_small',
// ongoing: true,
// ),
// ),
// );
// });
var sharedPreferences = await SharedPreferences.getInstance();
await sharedPreferences.reload(); // Its important
service.setForegroundNotificationInfo(
title: "My App Service",
content: "Updated at ${sharedPreferences.getString("yourVariable") ?? 'no data'}",
);
}
}
/// you can see this log in logcat
if (kDebugMode) {
// print('FLUTTER BACKGROUND SERVICE: ${deee.toString()}');
}
// test using external plugin
final deviceInfo = DeviceInfoPlugin();
String? device;
if (Platform.isAndroid) {
final androidInfo = await deviceInfo.androidInfo;
device = androidInfo.model;
}
if (Platform.isIOS) {
final iosInfo = await deviceInfo.iosInfo;
device = iosInfo.model;
}
service.invoke(
'update',
{
"current_date": '400',
"device": device,
},
);
});
}
....
....
....
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
readyForShared(); // init shared preferences
});
}
...
...
...
ElevatedButton(onPressed:(){saveData('Your Updated data.');}....