Flutter firebase onBackgroundMessage throws exception - flutter

When i add this function to my code.
FirebaseMessaging.onBackgroundMessage(
(message) => _handleBG(message, currentUserId));
I get exception
[ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: NoSuchMethodError: The method 'toRawHandle' was called on null.
onMessage method below is working fine.
FirebaseMessaging.onMessage.listen((event) async {
// event.messageType
print('hearing to foreground messsage');
if (event.data['messageType'] == 'chat') {
await ChatHelper.handleMessageReceived(event, context, currentUserId);
//setState(() {});
}
});
FirebaseMessaging.onBackgroundMessage(
(message) => _handleBG(message, currentUserId));

The background message handler should be declared as a top-level function so that it can be called independently, as you never know the state of your app and its object when the notification is received. It looks like that your _handleBG method is a private method available to a specific class only.
From code documentation
/// This provided handler must be a top-level function and cannot be
/// anonymous otherwise an [ArgumentError] will be thrown.

Related

How to unit test Flutter's StreamSubscription onData?

This is what I tried so far.
Let's say result is a StreamSubscription.
This is my flutter file
try {
result.listen(
(event) async {
// This converts the JSON data
final news = NewsModel.fromJSON(jsonDecode(event));
// This saves the data to local database
await localDataSource.saveNews([news]);
},
onError: (e) {
debugPrint('$e');
},
);
} catch (e) {
debugPrint('$e');
}
this is my flutter test since I want to test if method localDataSource.saveNews() fails
await newsRepository.subscribe(); calls the try catch above
controller is a StreamController to add new data to the stream
news is a dummy data, it doesn't matter because whatever the localDataSource do it will throw a LocalDBException
also I am using Mockito https://pub.dev/packages/mockito to mock the localDataSource above
test(
'should handle fail save news method',
() async {
// arrange
when(mockLocalDataSource.saveNews(any)).thenThrow(LocalDBException());
// act
await newsRepository.subscribe();
controller.add(news)
// assert
},
);
As you can see I don't have any condition to pass the flutter test, but that's beyond the point as this flutter test already breaks the stream even if I have a onError on my listener.
if I use controller.addError(LocalDBException()) the onError works, but if I deliberately throw an exception from the method localDataSource.saveNews() it breaks the stream.
Given this context I want to know 2 things:
I want to know how to handle the error inside the onData of StreamSubscription, if a method / function throw an exception, as it ignores the onError if a method / function throw an exception inside the listener.
Is adding an error through addError() function the same as throwing an exception inside the stream?

Issues handling Navigation when exception occurs [Bloc]

I'm creating a flutter app using bloc/cubit pattern. I'm having issues preventing navigation when an exception occurs, here's my button onTap function:
function: () async {
if (state.termsAndConditionsAccepted &&
state.status.isValid &&
!state.status.isPure) {
await context
.read<SignUpCubit>()
.onClickSignUp()
.then(((value) => {Navigator.pop(context)}));
}
and here's the logic of sign in
Future<void> onClickSignUp() async {
try {
// sign in logic...
// Provoking an exception...
throw Exception('An error ocrred. Please try again.');
} on Exception catch (exception) {
String exMessage = exception.toString();
log(exMessage);
// This emits a new state with the error and shows an error snackbar based on this new state
emit(state.copyWith(
status: FormzStatus.submissionFailure,
exceptionErrorMessage: exMessage));
}
As far as I know, the .then() callback function will only be executed if the Future function completes successfully(onClickSignUp()) but the Navigation occurs even if the exception is thrown.
I managed make it work by returning a boolean value to the then() callback and do the navigation depending in that value(if true navigate/if false do nothing), but that would require me to return true when function completes successfully and return false in all my catch blocks which does not seems a good practice.
Since I'm handling the states from my cubit class I cannot use .onError or .catchError() callbacks on the button's function.
Hopefully someone can suggest a better error handling in this case or what could be an appropriate error handling for bloc pattern

Flutter: FCM Unhandled Exception: Null check operator used on a null value

E/flutter (26872): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)]
Unhandled Exception: Null check operator used on a null value
E/flutter (26872): #0
MethodChannelFirebaseMessaging.registerBackgroundMessageHandler
(package:firebase_messaging_platform_interface/src/method_channel/method_channel_messaging.dart:173:53)
E/flutter (26872): #1
FirebaseMessagingPlatform.onBackgroundMessage=
(package:firebase_messaging_platform_interface/src/platform_interface/platform_interface_messaging.dart:108:16)
// Background Messaging Set Up
Future<void> _firebaseMessagingBackgroundHandler(
RemoteMessage message) async {
print('background message');
}
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(....)
I am getting an error for this code on Android system. Everything works except when the app is terminated.
What works on Android:
Notification when Terminated, onBackground and onForeground
Date only when onForeground
What does not work on Android:
Data only when Terminated and onBackground
What works on iOS:
Notification when Terminated, onBackground and onForeground
Date only when onForeground
What does not work on iOS:
Data only when Terminated,
I have no clue why I am getting that null value error on Android system and how can I fix this issue? Also, is it true that I can not receive the Data only push notification on iOS when the app is terminated?
I had the same error as like you, on the same line. I checked out docs and it says 2 things about background message handler.
It must not be an anonymous function.
It must be a top-level function (e.g. not a class method which requires initialization).
In my case it was not a top-level function, it was declared inside a class. When you move your handler out of any class or function, so that it is a top-level function and doesn't require any class or method initialisation then the error will be gone.
_firebaseMessagingBackgroundHandler function should be outside of the main function.
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
}
Future<void> main() async {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(
...
);
}
In my case, doing what docs said was not enough. So I realized that I should add WidgetsFlutterBinding.ensureInitialized() before everything in main function like this:
void main() {
WidgetsFlutterBinding.ensureInitialized();
FirebaseMessaging.onBackgroundMessage(_handleMessage);
runApp(const Homino());
}

Flutter: Consider canceling any active work during "dispose" when internet changes its state

I am getting the following message when internet goes off.
E/flutter (26162): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: This widget has been unmounted, so the State no longer has a context (and should be considered defunct).
E/flutter (26162): Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.
It is showing the message from this section of my code.
#override
void initState() {
super.initState();
try {
InternetAddress.lookup('google.com').then((result) {
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
// internet conn available
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) =>
(Constants.prefsMobile.getString("mobile") == null
? Login()
// : SignupPayoutPassword(signupdata: [])),
: Home(signindata: signinData)),
));
} else {
// no conn
_showdialog();
}
}).catchError((error) {
// no conn
_showdialog();
});
} on SocketException catch (_) {
// no internet
_showdialog();
}
Connectivity()
.onConnectivityChanged
.listen((ConnectivityResult connresult) {
if (connresult == ConnectivityResult.none) {
} else if (previous == ConnectivityResult.none) {
// internet conn
Navigator.of(context).pop();
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) =>
(Constants.prefsMobile.getString("mobile") == null
? Login()
: Home(signindata: signinData)),
));
}
previous = connresult;
});
}
I have not used any dispose method for this. If any one know please let me know how can I solve this problem. How to dispose. I am getting a crash report after my app close as follows
E/AndroidRuntime( 8064): java.lang.RuntimeException: Unable to destroy activity {com.example.aa_store/com.example.aa_store.MainActivity}: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter activity
is this crash message for the above problem? Please help.
Please use.
#override
void dispose() {
Connectivity().onConnectivityChanged.cancel();
super.dispose();
}
Better, define your stream outside the initState:
Stream _connectivityStream = Connectivity().onConnectivityChanged;
and in dispose use _connectivityStream.cancel();.
The error means that you instantiated a stream, which on changes of events, triggers build changes. This stream is setup during initState, meaning when the widget is first created. Connectivity().onConnectivityChanged.listen(....etc).
But you never tell flutter to cancel listening to this stream when the widget is disposed.
This is the role of the dispose method. Similar to how you want logic to be performed when the widget is built, you use initState, you should also tell it when you are no longer interested in these changes in logic.
Failing to do so, will result in the error you are having, aside from memory leaks also.
This is the translation of the error This widget has been unmounted, so the State no longer has a context (and should be considered defunct). which you posted. "Hey, this widget isn't in the tree anymore, its state is not mounted, I can't rebuild it, and you need to pay attention to it.
Please consider using the dispose method for these Flutter elements, not to mention all of them, but from the top of my mind:
AnimationControllers.
Timers.
Streams listeners.

Flutter, testing future that throws an Error makes the test unloadable

I'm trying to run a flutter test where a widget displays an error page when the Future provided to it throws an error (via FutureBuilder).
However the line where I create the future seems to be making the test fail.
final futureError = Future.delayed(Duration(milliseconds: 20))
.then((value) => throw Error());
with the message
Failed to load "D:\Projects\flutter\....dart": Instance of 'Error'
Putting it inside the test function resolved the issue
testWidgets('...',
(WidgetTester tester) async {
await tester.runAsync(() async {
final futureError = Future.error('error');
// ...
(it was in the group method prior to this)