Flutter : 'onError' is deprecated on runZoned function - flutter

I got this error message when I using newest Flutter v 1.17.1 and Dart 2.8.2 version on main.dart file,
'onError' is deprecated and shouldn't be used. Use runZonedGuarded instead.
Try replacing the use of the deprecated member with the replacement.
and this is the code,
runZoned<Future<Null>>(() async {
runApp(MyApp());
}, onError: (error, stackTrace) async {
await FlutterCrashlytics().reportCrash(error, stackTrace, forceCrash: false);
});
}
Any solution to get rid of this error message?

You can do this:
runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}, (Object error, StackTrace stack) async {
await FlutterCrashlytics().reportCrash(error, stackTrace, forceCrash: false);
});

Related

Undefined name 'HydratedBlocOverrides'

I am a Beginner in flutter and i was learning hydratedBloc, i imported all the needed dependencies as in the tutorial i was following and i run into a problem where my HydratedBlocOverrrides.runZoned is marked as an error
[This is the screenshot containing the errorthis is the Bloc_Imports files containg the exported hydratedBloc](https://i.stack.imgur.com/fVTcR.png)
Why do i have this error?
I tried using HydratedBloc to locally store data,i imported hydratedBloc but this syntax was marked and error "Undefined Name" refering to this HydratedBlocOverrrides.runZoned
According to hydrated_bloc's Changelog in version 9.0.0. It has removed HydratedBlocOverrides
BREAKING: feat!: reintroduce HydratedBloc.storage and remove HydratedBlocOverrides (#3479)
upgrade to bloc: ^8.1.0
Therefore change your code from:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final storage = await HydratedStorage.build(
storageDirectory: await getApplicationDocumentsDirectory());
HydratedBlocOverrides.runZoned(
() => runApp(const MyApp()),
storage: storage,
);
}
to
void main() async {
WidgetsFlutterBinding.ensureInitialized();
HydratedBloc.storage = await HydratedStorage.build(
storageDirectory: await getTemporaryDirectory(),
);
runApp(const MyApp());
}

Exception thrown by async function is intercepted by crashlytics

I have the following function:
class CannotOpenMapException implements Exception {}
void launchMap(String address) async {
…
throw CannotOpenMapException();
}
And then in an onTap handler:
onTap: () {
try {
launchMap(my_address);
} on CannotOpenMapException {
print('caught exception!');
}
}
Thing is, the exception is not caught, the print statement is never executed.
I think the problem is with the way I start Crashlytics (though this is how the official docs recommend it):
void main() async {
runZonedGuarded<Future<void>>(() async {
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;
…
runApp(const MyApp());
},
(error, stack) =>
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true));
}
Is there a way I can make Crashlytics only catch the exception I don't catch?
You should use the second argument "onError" of runZoneGuarder, this way only the exceptions that's you don't catch are intercepted.
runZonedGuarded<Future<void>>(() async {
WidgetsFlutterBinding.ensureInitialized();
... and stuff
runApp(const MyApp());
}, (error, stack) {
FirebaseCrashlytics.instance.recordError(error, stack);
});
The doc:
https://api.flutter.dev/flutter/dart-async/runZonedGuarded.html
I found the problem: The onTap handler was not async, so it just started launchMap and then exited. Now the launchMap execution was running "detached" (not sure that is the correct word for it). In order to catch it, onTap needs to be async and use the await keyword:
onTap: () async {
try {
await launchMap(widget.listing.address());
} on CannotOpenMapException {
…
}
})
onTap: () {
try {
await launchMap(my_address);
} on CannotOpenMapException {
print('caught exception!');
}
}
if you want to use async then use await.

PlatformException(channel-error, Unable to establish connection on channel., null, null)

PlatformException(channel-error, Unable to establish connection on channel., null, null)
package:firebase_core_platform_interface/src/pigeon/messages.pigeon.dart 205:7 FirebaseCoreHostApi.initializeCore
I had the same problem. I made the solution by :
Upgrading plugins & Flutter SDK
Cold Booting the emulator
The app properly runs now and that error is gone .
I think I found solution. Take a look into:
https://github.com/firebase/flutterfire/tree/master/packages/firebase_analytics/firebase_analytics/test
There are 2 files. You need to copy mock.dart:
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:firebase_analytics_platform_interface/firebase_analytics_platform_interface.dart';
import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
typedef Callback = void Function(MethodCall call);
final List<MethodCall> methodCallLog = <MethodCall>[];
void setupFirebaseAnalyticsMocks([Callback? customHandlers]) {
TestWidgetsFlutterBinding.ensureInitialized();
setupFirebaseCoreMocks();
MethodChannelFirebaseAnalytics.channel
.setMockMethodCallHandler((MethodCall methodCall) async {
methodCallLog.add(methodCall);
switch (methodCall.method) {
case 'Analytics#getAppInstanceId':
return 'ABCD1234';
default:
return false;
}
});
}
And then in your test:
void main() {
setupFirebaseAnalyticsMocks();
FirebaseAnalytics? analytics;
group('$FirebaseAnalytics', () {
setUpAll(() async {
await Firebase.initializeApp();
analytics = FirebaseAnalytics.instance;
});
setUp(() async {
methodCallLog.clear();
});
tearDown(methodCallLog.clear);
group('logEvent', () {
test('reject events with reserved names', () async {
expect(
analytics!.logEvent(name: 'app_clear_data'),
throwsArgumentError,
);
});
test('reject events with reserved prefix', () async {
expect(analytics!.logEvent(name: 'firebase_foo'), throwsArgumentError);
});
test('custom event with correct parameters', () async {
await analytics!.logEvent(
name: 'test-event',
parameters: {'a': 'b'},
);
expect(
methodCallLog,
<Matcher>[
isMethodCall(
'Analytics#logEvent',
arguments: {
'eventName': 'test-event',
'parameters': {'a': 'b'},
},
)
],
);
});
For me it works.

Dart: How to mock and stub Sqflite transaction (inner callback)?

i am trying to mock the following method of sqlite_api.dart by (https://pub.dev/packages/sqflite):
Future<T> transaction<T>(Future<T> Function(Transaction txn) action, {bool? exclusive});
my implementation/adapting of the method is like:
Future<void> _transaction(Set<DatabaseLocalRequest> payload) async {
await this._api.transaction((txn) async => {
for (final req in payload) {
await txn.rawInsert(req.query.sql, req.query.arguments)
}
});
}
my db_test.dart using Mocktail (https://pub.dev/packages/mocktail):
test('if [single] put succeeds', () async {
// SETUP
sut = DatabaseLocalProvider(db: mockDb);
final query = Statement(sql: 'INSERT INTO Test(name, value, num) VALUES("some name", 1234, 456.789)');
final req = DatabaseLocalRequest(query: query);
// MOCK
when(() => mockDb.transaction((txn) => txn.rawInsert(req.query.sql, req.query.arguments)))
.thenAnswer((_) async => 1);
// ACT, ASSERT
await sut.put(req: req, bulkReq: null).then((response) => {
expect(response, ...
});
}); // test end
I got the following response from the console ERROR:
🚨🚨
type 'Null' is not a subtype of type 'Future<Set<Set<int>>>'
How do I stub the inner txn.rawInsert() method that should respond with the Future<Set<Set<int>>> with {{1}}?
Thanks in advance!
I might not respond exactly to your question but you can mock sqflite by using a real implementation with sqflite_common_ffi since it works on all desktop (MacOS, Linux, Windows) on the dart VM so also in flutter and dart unit tests:
More information here: https://pub.dev/packages/sqflite_common_ffi#unit-test-code
One solution is open a database in memory for each test so that you start with an empty database.
import 'package:test/test.dart';
import 'package:sqflite_common/sqlite_api.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
void main() {
// Init ffi loader if needed.
sqfliteFfiInit();
test('simple sqflite example', () async {
var db = await databaseFactoryFfi.openDatabase(inMemoryDatabasePath);
expect(await db.getVersion(), 0);
await db.close();
});
}
when(() => mockDb.transaction(any())).thenAnswer((_) async => {{1}});
when(() => mockDb.rawInsert(any())).thenAnswer((_) async => 1);
this did the trick! but it is not 100 solution, because the closure is not stubbed but bypassed.

Sentry is NOT reporting error inside Flutter

I have my Sentry setup like this:
void main() => runZonedGuarded(() {
runApp(MyApp());
}, (Object error, StackTrace stackTrace) {
reportError(error, stackTrace);
});
and related functions
final SentryClient sentry = new SentryClient(dsn: '<my-dsn>');
Future<void> reportError(dynamic error, dynamic stackTrace) async {
sentry.captureException(
exception: error,
stackTrace: stackTrace,
);
}
I added throw Exception("my-error") inside a widget's build method, I can't see the error is showing on the Sentry web console.
I create a single file to throw exception and sentry capture, and I do see sentry is reporting the error.
Something must wrong with runZonedGuarded.
Check in your sentry dashboard if you are using the free version and if the monthly quota hasnt been surpassed. If that is the case you will not receive any events.
After a number of Sentry setups that didn't seem to work right, I arrived at this one that works:
Future<void> main() async {
final sentry = Sentry.SentryClient(Sentry.SentryOptions(dsn: '[Add dsn URI here]'));
runZonedGuarded(() {
WidgetsFlutterBinding.ensureInitialized();
FlutterError.onError = (FlutterErrorDetails errorDetails) {
sentry.captureException(
errorDetails.exception,
stackTrace: errorDetails.stack,
);
};
runApp(MyApp());
}, (Object error, StackTrace stackTrace) {
sentry.captureException(
error,
stackTrace: stackTrace,
);
});
}
you must make the func async to send error to sentry console
be sure import this file for mobile app:
import 'package:sentry/io_client.dart';
E.g:
main.dart
import 'package:sentry/io_client.dart';
final SentryClient sentry = new SentryClient(dsn: YOUR_DSN);
main() async {
try {
throw new StateError('This is a Dart exception.');
} catch(error, stackTrace) {
await sentry.captureException(
exception: error,
stackTrace: stackTrace,
);
}
}
HomeScreen
floatingActionButton: FloatingActionButton(
onPressed: () async{
throw new StateError('This is a Dart exception.');
},
By the way in release version it will send every exception, because in debug flutter doesn't catch every error that displays in the console, and to simplify that for you can use one of these packages :
https://pub.dev/packages/catcher
https://pub.dev/packages/flutter_sentry