Flutter's GetIt with the app in background - flutter

I'm having issues with using GetIt while the app is in background. With the app is in focus everything works great but the moment I switch focus everything breaks. What's my error here?
I'm using GetIt 6.1.1, flutter 2.0.5, here's the stacktrace
I/flutter ( 5125): FlutterFire Messaging: An error occurred in your background messaging handler:
I/flutter ( 5125): 'package:get_it/get_it_impl.dart': Failed assertion: line 315 pos 7: 'instanceFactory != null': Object/factory with type NotificationCubit is not registered inside GetIt.
I/flutter ( 5125): (Did you accidentally do GetIt sl=GetIt.instance(); instead of GetIt sl=GetIt.instance;
I/flutter ( 5125): Did you forget to register it?)
Here's my GetIt setup
get_it.dart
GetIt getIt = GetIt.instance;
void setupGetIt() {
getIt.registerSingleton<NotificationCubit>(NotificationCubit());
}
main.dart
void main() async {
// some other methods
setupGetIt();
runApp(MyApp());
}
And the method that throws the error.
void _handleMessage(RemoteMessage message) {
getIt
.get<NotificationCubit>()
.addNotification(NotificationClassName.fromRemoteMessage(message));
_displaySnackBar(message);
}

You need to call setupGetIt(); again in your _handleMessage, the code becomes:
void _handleMessage(RemoteMessage message) {
//Add it here
setupGetIt();
getIt
.get<NotificationCubit>()
.addNotification(NotificationClassName.fromRemoteMessage(message));
_displaySnackBar(message);
}
The error is thrown because when _handleMessage method is called, the compiler fails to find getIt.registerSingleton<NotificationCubit>(NotificationCubit()); which registers NotificationCubit>.

Related

Flutter: How to use GetIt.I in Integration test

I am trying to use GetIt in my integration test but I am having some difficulties.
When trying to access registered singletons in GetIt, I am always facing this error:
_AssertionError ('package:get_it/get_it_impl.dart': Failed assertion: line 370 pos 7: 'instanceFactory != null': Object/factory with type DrawScreenState is not registered inside GetIt.
(Did you accidentally do GetIt sl=GetIt.instance(); instead of GetIt sl=GetIt.instance;
Did you forget to register it?))
I have a function to register my singletons:
// Initialize GetIt by initializing and registering all the instances for it
Future<void> initGetIt() async {
...
// draw screen services
GetIt.I.registerSingleton<DrawScreenState>(DrawScreenState(
Strokes(), KanjiBuffer(), DrawingLookup(), DrawScreenLayout.Portrait)
);
...
}
This function is called when creating my app instance. However, when I call my function, the singletons are not registered in GetIt and the above Exception is thrown. My code looks like this:
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets("DrawScreen test", (WidgetTester tester) async {
IS_TESTING_DRAWSCREEN = true;
// create app instance and wait until it finished initializing
app.main(); // <-- this calls initGetIt()
print(GetIt.I<DrawScreenState>()); // <-- this raises the exception
await tester.pumpAndSettle(Duration(seconds: 2));
// check that the app does not show any predictions on start up
List<String> preds = (tester.widgetList(find.byType(PredictionButton))).map((e) => (e as PredictionButton).char).toList();
...
}
}
In the README integration tests are mentioned, therefore I assume that it should be possible to access GetIt in them. This leads me to the question what am I doing wrong?

Handeling and reporting errors that thrown inside runZonedGuarded callback [Crashlytics, Flutter]

I am implementing Crashlytics with flutter,
and following this docs Using Firebase Crashlytics
I was wondering what would actually happen if an error is thrown inside runZonedGuarded's
error callback: (see the arrow in the code)
void main() async {
runZonedGuarded<Future<void>>(() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
runApp(MyApp());
}, (error, stackTrace) async {
***** some buggy code here ******. <----------
await crashlytics.handleNonFlutterErrors(error,stackTrace);
});;
}
so I have tried it, and to my surprise it is still swallowed by flutter framework and stacktrace was dumped to console - I was expecting the app to crash but it didn't...
does any-body knows how to handle this types of errors?

Unit tests running in parallel in flutter when using GetIt

I am writing an flutter App and I am using GetIt for dependency injection.
When I write unit tests, I do something like this:
void main() {
group("tests", () {
test("test1", () {
GetIt.I.reset();
GetIt.I.registerSingleton(MyDependency());
// Some code using GetIt.I<MyDependency>()
});
test("test2", () {
GetIt.I.reset();
GetIt.I.registerSingleton(MyDependency());
// Some code using GetIt.I<MyDependency>()
});
});
}
When I run the tests one after the other, it works perfectly.
When I run all test at once, I get errors on the line GetIt.I<MyDependency>()... like this:
'package:get_it/get_it_impl.dart': Failed assertion: line 372 pos 7: 'instanceFactory != null': Object/factory with type HttpActions is not registered inside GetIt.
So my guess is, that the tests run in parallel and the setup of one test with GetIt.I.reset() conflicts with the setup of the other test.
So how can I solve this?
Can I somehow tell the tests, that they just cannot run in parallel?
Or is there some way of safely using GetIt in this context?
I think your issue is not about running your tests in parallel but related to the fact that GetIt.I.reset() is an asynchronous method:
Implementation
Future<void> reset({bool dispose = true});
And as mentionned in the documentation of the package for this method:
[...] As dispose functions can be async, you should await this function.
Code Sample
I've made a new test sample based on the code you've provided and inspired by the unit tests written for the package and it seems to be working properly.
import 'package:flutter_test/flutter_test.dart';
import 'package:get_it/get_it.dart';
import 'package:so_tests/my_dependency.dart';
void main() {
// This method provided by `flutter_test` is called before each test.
setUp(() async {
// Make sure the instance is cleared before each test.
await GetIt.I.reset();
});
group('tests', () {
test('test1', () {
final getIt = GetIt.instance;
getIt.registerSingleton(MyDependency());
// Some code using getIt<MyDependency>()
});
test('test2', () {
final getIt = GetIt.instance;
getIt.registerSingleton(MyDependency());
// Some code using getIt<MyDependency>()
});
});
}

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 in_app_purchase '_enablePendingPurchases': enablePendingPurchases() must be called before calling startConnection

I am using the simple code below
bool available = await InAppPurchaseConnection.instance.isAvailable();
however it is returning the error
E/flutter (14525): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: 'package:in_app_purchase/src/billing_client_wrappers/billing_client_wrapper.dart': Failed assertion: line 101 pos 12: '_enablePendingPurchases': enablePendingPurchases() must be called before calling startConnection
I was wondering if anyone knew a reason for this error and if so what should i fo about it, Happy to have any suggestions - thanks.
The documentation is very thin on this and should actually be more clear. You need include the line below in main() for it to work.
void main() {
///Include this in main() so purchases are enabled
InAppPurchaseConnection.enablePendingPurchases();
runApp(MyApp());
}
I could not import InAppPurchaseConnection to try the accepted solution, and fixed this issue with the following:
import 'package:flutter/foundation.dart';
import 'package:in_app_purchase_android/in_app_purchase_android.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
// Inform the plugin that this app supports pending purchases on Android.
// An error will occur on Android if you access the plugin `instance`
// without this call.
if (defaultTargetPlatform == TargetPlatform.android) {
InAppPurchaseAndroidPlatformAddition.enablePendingPurchases();
}
runApp(MyApp());
}