Below is a code that i have to initialize an run my app with.
As a test i have a future that will throw an exception (out of range), this is how ever not sent to crashlystics?
If i issue a FirebaseCrashlystics.instance.crash() it will send a report.
I have changed the filter to Event-type="non-fatals" but i can not see my errors there.
I don't know what i am missing?
class AppConfig {
final String appTitle;
final BuildFlavor buildFlavor;
final bool initializeCrashlytics, enableCrashlyticsInDevmode;
AppConfig(
{#required this.appTitle,
#required this.buildFlavor,
this.initializeCrashlytics = true,
this.enableCrashlyticsInDevmode = true});
Future<void> _testAsyncErrorOnInit() async {
Future<void>.delayed(
const Duration(seconds: 2),
() {
final List<int> list = <int>[];
print(list[100]);
},
);
}
Future startCrashlytics() async {
Function originalError = FlutterError.onError;
if (this.initializeCrashlytics) {
await FirebaseCrashlytics.instance
.setCrashlyticsCollectionEnabled(this.enableCrashlyticsInDevmode);
FlutterError.onError = (FlutterErrorDetails errorDetails) async {
await FirebaseCrashlytics.instance.recordFlutterError(errorDetails);
originalError(errorDetails);
};
FirebaseCrashlytics.instance.crash(); << This works
// await _testAsyncErrorOnInit(); << This doesn't Yes i do comment crash and uncomment this.
}
}
Future run() async {
// Lägg in initiering av firebase, crashlytics
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await startCrashlytics();
runApp(ElectronicCupongs(appTitle: appTitle));
}
}
I see you checked the package's example, but is your app wrapped in runZonedGuarded?
The example page contains the required details, but you can also check the package's files or, a more readable format, FlutterFire.
Related
I am writing a basic programme to teach myself Dart/Flutter. Part of the programme uses the http.dart package to get some data and the http.get command returns a Future value. In order to unpack this value, I need to use an await command, which then changes the execution order of my code. I cannot work out how to preserve the intended execution order whilst using async/await. I am new to this, so appreciate that I am probably missing something obvious.
Code example 1 below uses async/await through a series of functions. This approach gives more or less the correct output order (other than the end of main()), but would mean (I think) that I would need to have an async build() method, which is not valid in Flutter.
// Cascading async methods with local variables and await statements
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() async {
print('build: Before getName.');
String name = await _getName();
print('build: After getName.');
}
// Get some data, make some decisions
Future<String> _getName() async {
print('getName: before getData');
String name = await _getData();
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: returning body.data');
return name;
} else {
print('getName: returning Bob');
return 'Bob';
}
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
final data = await http.get(Uri.parse('http://www.google.co.uk'));
print('getData: After http get.');
return data.body;
}
The output from this is (I have truncated the html data that is returned):
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
Main: end.
getData: After http get.
getName: after getData
getName: returning body.data
build: After getName. Name is: <html data>
The second code example below uses a global variable to capture data in the _getName() method so that I can avoid using async/await in the build() method. This does not give the correct execution order or the correct output.
// Use global variable to receive awaited data and avoid cascading async methods
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
String name = "";
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() {
print('build: Before getName.');
_getName();
print('build: After getName. Name is: $name');
}
// Get some data, make some decisions
Future<void> _getName() async {
print('getName: before getData');
String data = await _getData();
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: setting name = body.data');
name = data;
} else {
print('getName: setting name = Bob');
name = 'Bob';
}
return;
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
final data = await http.get(Uri.parse('http://www.google.co.uk'));
print('getData: After http get.');
return data.body;
}
The output from this code is shown below. Note that the build() method completed before _getData and _getName and the name printed in build() is empty in the 5th row.
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
build: After getName. Name is:
Main: end.
getData: After http get.
getName: after getData
getName: setting name = body.data
In the third example below, I have tried using .then to ensure that the code in each function only executes after the await command. I didn't think this would work (and it didn't) because I think I have a problem controlling the flow between functions, not a problem controlling the flow within functions, but I thought I should give it a go and I was clutching at straws by this point.
// Use global variable to avoid using await in build() method
// Use .then to ensure that method actions follow await command
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
String name = ""; // Global variable for async data return
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() {
print('build: Before getName.');
_getName();
print('build: After getName. Name is: $name');
}
// Get some data, make some decisions
Future<void> _getName() async {
print('getName: before getData');
await _getData().then((data) {
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: setting name = body.data');
name = data;
} else {
print('getName: setting name = Bob');
name = 'Bob';
}
});
return;
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
String value = "";
await http.get(Uri.parse('http://www.google.co.uk')).then((data) {
print('getData: After http get.');
value = data.body;
});
return value;
}
The output from this code is shown below. As with the second example, the execution is not in the correct order and the name printed in the build() method is empty.
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
build: After getName. Name is:
Main: end.
getData: After http get.
getName: after getData
getName: setting name = Bob
Ideally, the output from the programme should be:
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
getData: After http get.
getName: after getData
getName: setting name = Bob
build: After getName. Name is: Bob
Main: end.
How do I write my code so that I can use the http.get method and ensure that my code executes in the order that I want? I'll just add that I have read A LOT of stackoverflow questions, flutter documentation and general help online, but not found anything that answers my question so far.
Or nothing that I understand. :D Apologies if this is a stupid question. I am a noob at this.
I should have added that this example is an simplification of the problem in a Flutter app I am writing (noughts and crosses). This is checking for a win/draw after each move, then reading data from a DB, updating the results and writing them back to the DB. It also updates the game state to show that the game is over. The problem caused by async/await is that gamestate isn't being updated whilst the functions await the data and the game continues in the "playing" state even though the game is over. Pseudo code of the programme below (this is a bit scrappy, but hopefully it illustrates the problem).
build() {
checkState(win, draw or continue?);
if (continue){
_humanMove();
_computerMove();
drawTheWidgets;
} else {
drawDifferentWidgets; // New game
}
}
void _humanMove() async {
processMove;
if (win/draw) {
await _updateResults;
updateGameState(game over);
}
}
void _computerMove() async {
processMove;
if (win/draw) {
await _updateResults;
updateGameState(game over);
}
}
results _updateResults() async {
await http.get data results in database;
updateWithResult;
await http.put data results in database;
}
You need to wait ("await") for all the function calls, see:
void main(List<String> arguments) async {
print('Main: start.');
await _build();
print('Main: end.');
}
// Draw some stuff, make some decisions
Future<void> _build() async {
print('build: Before getName.');
await _getName();
print('build: After getName. Name is: $name');
}
// Get some data, make some decisions
Future<void> _getName() async {
print('getName: before getData');
String data = await Future.delayed(const Duration(milliseconds: 500), () {
return 'x';
});
print('getName: after getData');
double val = 5;
if (val < 0.5) {
print('getName: setting name = body.data');
name = data;
} else {
print('getName: setting name = Bob');
name = 'Bob';
}
return;
}
One comment: I believe it's easier for you to learn to do this right, if you try to learn this not based on console output, but with a Flutter app. You would see that your program goes through different states that actually need time - before, during and after your http request.
In your build method of a widget, you would need to provide something to show for for each state of your program. So you actually do not wait (await) for results, but update the state based on the results. And depending on that, your build method is prepared to take the different states and show something adequate. Then async/await is quite nice.
--- adding the following in response to the clarifying comment ---
The build method of a widget is not where you put business logic of your app. Build is called whenever a widget is rebuild (if you have a http request writing in a database, it would be triggered a whole number of times!). Trying to apply the steps you wanted your app to do in your first description with the intended output on the console, I tried to write this clarifying app (and to have different states that your app passes through I included that you have to click the icon button / FloatingActionButton to trigger the process). Please have a look how the build method of the second widget deals with all the different states (it prints them to the console and shows a text - you could do more fancy stuff there of course, based on what the state of your app implies). The real "action" (changes of the state of your app) is happening elsewhere:
import 'package:flutter/material.dart';
class ExampleWidget extends StatefulWidget {
const ExampleWidget({Key? key}) : super(key: key);
#override
_ExampleWidgetState createState() => _ExampleWidgetState();
}
class _ExampleWidgetState extends State<ExampleWidget> {
String _showWhatsHappening = 'Before getName.';
Future<String> _getData() async {
setState(() {
_showWhatsHappening = 'getData: Before http get.';
});
final data = await Future.delayed(const Duration(milliseconds: 500), () {
return 'Bob';
});
setState(() {
_showWhatsHappening = 'getData: After http get.';
});
await Future.delayed(const Duration(milliseconds: 300));
return data;
}
#override
void initState() {
print('not main, but initstate: start.');
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
theme: ThemeData(brightness: Brightness.light, fontFamily: 'Example'),
home: Scaffold(
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.star_outline),
onPressed: () async {
setState(() {
_showWhatsHappening = 'before getData ';
});
final x = await _getData();
await Future.delayed(const Duration(milliseconds: 300));
setState(() {
_showWhatsHappening = 'after getData ';
});
await Future.delayed(const Duration(milliseconds: 300));
setState(() {
_showWhatsHappening = 'setting name = $x';
});
},
),
body: Center(child: ShowItWidget(showIt: _showWhatsHappening)),
),
);
}
}
class ShowItWidget extends StatelessWidget {
final String showIt;
const ShowItWidget({Key? key, required this.showIt}) : super(key: key);
#override
Widget build(BuildContext context) {
print(showIt);
return Text(showIt);
}
}
void main() {
runApp(const ExampleWidget());
}
For completeness, here is my Dart-specific solution using Streams.
// Use global variable to receive awaited data
// Use StreamController to wait for async methods to finish
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
String name = "";
StreamController streamController = StreamController.broadcast();
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() {
print('build: Before getName.');
_getName();
streamController.stream.listen((args) {
print('build: After getName. Name is: $name');
});
}
Future<void> _getName() async {
print('getName: before getData');
String data = await _getData();
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: setting name = body.data');
name = data.length.toString();
} else {
print('getName: setting name = Bob');
name = 'Bob ${data.length.toString()}';
}
print('gateName. Name is $name');
streamController.add(name);
return;
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
final data = await http.get(Uri.parse('http://www.google.co.uk'));
print('getData: After http get.');
return data.body;
}
I have a page that writes a color on file, called "colors.txt".Then the page is closed, when it will be opened again this file will be read and its content (String) printed on the screen.
This is the class that handles reads and writes :
class Pathfinder {
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/colors.txt');
}
Future<File> writeColor(String color) async {
final file = await _localFile;
// Write the file
return file.writeAsString('$color');
}
Future<String> readColor() async {
try {
final file = await _localFile;
// Read the file
final contents = await file.readAsString();
return contents;
} catch (e) {
// If encountering an error, return 0
return "Error while reading colors";
}
}
}
Before page closure, the color has been saved with writeColor, we just need to read the file and print its content.
And this is how I read the color :
void initState() {
super.initState();
String colorRead;
() async {
pf = new Pathfinder();
colorRead = await pf.readColor();
}();
print("Color in initState: " + colorRead.toString());
}
The problem is that colorRead is always null. I already tried .then() and .whenCompleted() but nothing changed.
So my doubt is :
Am I not waiting read operation in right way or the file, for some reasons, is deleted when page is closed?
I think that if file wouldn't exists then readColor should throw an error.
EDIT : How writeColor is called :
Color bannerColor;
//some code
await pf.writeColor(bannerColor.value.toRadixString(16));
void initState() {
super.initState();
String colorRead;
() async {
pf = new Pathfinder();
colorRead = await pf.readColor();
}();
print("Color in initState: " + colorRead.toString()); /// << this will execute before the async code in the function is executed
}
It's null because of how async/await works. The print statement is going to be called before the anonymous async function finishes executing. If you print in inside the function you should see the color if everything else is working correctly.
I am trying to create an Isolate un Flutter and then use this isolate to fetch some data from Firebase Realtime Database.
I am creating de Isolate in a file called home.dart (not main) and here is my code for that file. I have a class to create the Isolate and the function for the Isolate to execute. Inside this function I am trying to fetch the data.
void elIsolate(SendPort sPort) async {
print("Fetching data");
final databaseReference = FirebaseDatabase.instance.reference().child("categories");
DataSnapshot info;
/*databaseReference.once().then((DataSnapshot snapshot) {
info = snapshot;
print(info.value);
});*/
print("new isolate created");
IsolateChannel channel = IsolateChannel.connectSend(sPort);
channel.stream.listen((data) {
print('newIsolate received : $data');
});
channel.sink.add("hi");
}
class _MyHomePageState extends State<MyHomePage> {
List list = [];
void initState(){
WidgetsFlutterBinding.ensureInitialized();
super.initState();
print("Init state");
loadIsolate();
}
Future loadIsolate() async {
await Firebase.initializeApp();
print("Load isolate");
ReceivePort rPort = ReceivePort();
IsolateChannel channel = IsolateChannel.connectReceive(rPort);
channel.stream.listen((data) {
print('rootIsolate received : $data');
channel.sink.add('How are you');
});
await Isolate.spawn(elIsolate, rPort.sendPort);
/*await Isolate.spawn(getAllWorkers, receivePort.sendPort);
receivePort.listen((message) {
print(message);
});*/
}
}
Then I have my main.dart. I added this line inside the main function: WidgetsFlutterBinding.ensureInitialized();
Here is my code
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
bool resp;
await SharedPreferences.getInstance().then((prefs) {
resp = prefs.getBool('isUser');
if (resp == null) {
FirebaseAuth _auth = FirebaseAuth.instance;
resp = (_auth.currentUser != null);
prefs.setBool('isUser', resp);
}
});
runApp(MyApp(user: resp));
}
flutter_isolate: ^2.0.2
onPressed: () {
FlutterIsolate.spawn(_isolateEntrypoint, "");
}
// A "top level" function (i.e. not inside a class or make it static)
_isolateEntrypoint(String foo) {
WidgetsFlutterBinding.ensureInitialized();
...
}
Make sure that authorization and initialization were made on the same main thread (top level or static).
Now this FlutterEngine will be able to communicate with Firebase Realtime Database but the main FlutterEngine won't. In practice, depending on the app, an app may want to communicate with Realtime Database from either engine (or both). In background apps, more likely from here rather than the main isolate, but again that depends on the app.
I'm trying to create a reactive SharedPreferences utility but I'm stuck with this issue.
This is my class
class SPUtil {
final _workoutsStreamController = StreamController<
Result<Iterable<PreferencesWorkout>, Exception>>.broadcast();
#override
Stream<Result<Iterable<PreferencesWorkout>, Exception>> getWorkouts() async* {
final prefs = await SharedPreferences.getInstance();
_workoutsStreamController.sink.add(success(_getStoredWorkouts(prefs)));
yield* _workoutsStreamController.stream;
}
}
And this is my test
test("getWorkouts SHOULD return empty list WHEN nothing is stored",
() async {
SharedPreferences.setMockInitialValues({});
final actual = await _sut.getWorkouts().first;
expect((actual as Success).value, []);
});
Whenever I run this test it loops for 30 seconds and it returns this error
dart:async _startMicrotaskLoop
TimeoutException after 0:00:30.000000: Test timed out after 30 seconds. See https://pub.dev/packages/test#timeouts
Everything works fine if I use this implementation instead
class SPUtil {
#override
Stream<Result<Iterable<PreferencesWorkout>, Exception>> getWorkouts() async* {
final prefs = await SharedPreferences.getInstance();
yield success(_getStoredWorkouts(prefs));
}
}
So I assume my test is correct.
Thanks in advance.
I finally found the issue, StreamController doesn't emit anything if is not listened, hence the test goes in timeout.
test("getWorkouts SHOULD return empty list WHEN nothing is stored",
() async {
SharedPreferences.setMockInitialValues({});
final actual = _sut.getWorkouts().first;
actual.listen((event) {});
final actualResult = await actual;
expect((actualResult as Success).value, []);
});
When working with HIVE database in flutter. If you ever get error like this:
"Box not found. Did you forget to call Hive.openBox()?"
It means you haven't opened your box to
To resolve this issue call
await Hive.openBox("boxname");
before using the box
It means you haven't opened your box. To resolve this issue call
await Hive.openBox("boxname");
before using the box.
The box needs to be open either at the beginning, after database initialization or right before doing the operation on the box.
For example in my AppDatabase class I have only one box ('book') and I open it up in the initialize() method, like below:
The whole application and tutorial is here.
const String _bookBox = 'book';
#Singleton()
class AppDatabase {
AppDatabase._constructor();
static final AppDatabase _instance = AppDatabase._constructor();
factory AppDatabase() => _instance;
late Box<BookDb> _booksBox;
Future<void> initialize() async {
await Hive.initFlutter();
Hive.registerAdapter<BookDb>(BookDbAdapter());
_booksBox = await Hive.openBox<BookDb>(_bookBox);
}
Future<void> saveBook(Book book) async {
await _booksBox.put(
book.id,
BookDb(
book.id,
book.title,
book.author,
book.publicationDate,
book.about,
book.readAlready,
));
}
Future<void> deleteBook(int id) async {
await _booksBox.delete(id);
}
Future<void> deleteAllBooks() async {
await _booksBox.clear();
}
}
You have to open the box you want to use and make sure to use await while using the openBox() function.
await Hive.openBox("boxname");