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, []);
});
Related
I am using the shared_preferences within my app to save some data
but they only update the second time I open them
prefs.setInt is Future method, try putting await before all of it.
async{
final prefs = await SharedPreferences.getInstance ();
///others
await prefs.setInt ('s$current month $current year', s);
setState (() { });
}
And create another method to fetch data on initState like
fetchData()async{
final prefs = await SharedPreferences.getInstance ();
hours = prefs.getInt(...);
///....
setState(() { });
}
#override
void initState() {
super.initState();
fetchData();
}
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.
I'm having an issue trying to call a document from Firestore in initState. I can pull data from a StreamBuilder just fine, but I just want to call the data once on initState.
Here is what I'm doing:
class _PainLevelState extends State<PainLevel> {
final FirebaseAuth _auth = FirebaseAuth.instance;
final CollectionReference userCollection =
Firestore.instance.collection('users');
static final now = DateTime.now();
String _uid;
double myPainLevel = 0;
Future<void> getCurrentUser() async {
final FirebaseUser user = await _auth.currentUser();
if (mounted) {
setState(() {
_uid = user.uid;
});
}
}
Future<void> getCurrentPainLevel() async {
await userCollection
.document(_uid)
.collection('pain')
.where('time',
isGreaterThanOrEqualTo: DateTime(now.year, now.month, now.day))
.getDocuments()
.then((QuerySnapshot docs) {
if (docs.documents.isNotEmpty) {
print('yes');
} else {
print('no');
}
});
}
#override
void initState() {
super.initState();
getCurrentUser();
getCurrentPainLevel();
}
...
I just get a "no" every time I print to console. It's not get any documents when there is one. If I take the same code inside the future and put it somewhere else, like in the build method, it works, but it constantly builds and I don't want that. Any suggestion as to why it is not working? Thanks in advance.
I'm guessing here that your code will not always work, because getCurrentPainLevel might get called before getCurrentUser is completed, which will cause _uid to be null and therefore, not work as expected. Try to put then keyword after getCurrentUser method, like this:
#override
void initState() {
super.initState();
getCurrentUser().then((_) {
getCurrentPainLevel();
});
}
By the way, you should NOT be calling async methods in initState. Try to put the code somewhere else, like WidgetsBinding.instance.addPostFrameCallback(...).
I tried to save a List (which is called test)with two variables with SharedPreferences. I tried the code below, but I get some errors. Does anybody see the mistake i made? (I think it´s kind of an easy to fix mistake, but I´m a beginner and can´t find it ;)
int counter1 = 0;
int counter2 = 20;
String nameKey = "eins";
var test = [counter1, counter2];
#override
void initState() {
super.initState();
}
Future<bool> save() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
return await preferences.setIntList(nameKey, test);
}
Future<List<int>> load() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
return preferences.getIntList(nameKey);
}
set() {
load().then((value) {
setState(() {
test = value;
});
});
}
Thanks in advance :)
Future<List<String>> load() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
return preferences.getStringList(nameKey);
}
I recently learned of the fabulous way of waiting for multiple async functions to complete using Future.wait([asyncFuncOne(), asyncFunctwo()])
However, I noticed two different outcomes when running either of these blocks of code. One awaiting each function to finish, the other using Future.wait for parallel processing. What am I doing wrong?
Method 1:
await msm.initProfileData();
await msm.initActivityFeed();
await msm.getRecentlyActiveUsers();
await msm.getRecommendedUsers();
await msm.getGroups();
await msm.getFollowing();
await msm.getFollowers();
Method 2:
await Future.wait([
msm.getFollowing(),
msm.initProfileData(),
msm.initActivityFeed(),
msm.getRecentlyActiveUsers(),
msm.getRecommendedUsers(),
msm.getGroups(),
msm.getFollowers(),
]);
in Method 1, all the async functions complete before my apps home screen appears. In Method 2 the home screen appears before all the async functions complete.
Cheers and thanks in advance.
EDIT: Additional code example.
#override
void initState() {
super.initState();
googleSignIn.onCurrentUserChanged.listen((account) {
handleSignIn(account);
}, onError: (err) {
print('Error signing in: $err');
});
googleSignIn.signInSilently(suppressErrors: false).then((account) {
handleSignIn(account);
}).catchError((err) {
setState(() => _showSignIn = true);
print('Error signing in: $err');
});
}
handleSignIn(GoogleSignInAccount account) async {
if (account != null) {
await createUserInFirestore();
setState(() {
isAuth = true;
});
} else {
setState(() {
isAuth = false;
_showSignIn = true;
});
}
}
createUserInFirestore() async {
final GoogleSignInAccount user = googleSignIn.currentUser;
DocumentSnapshot doc = await usersRef.document(user.id).get();
//...
//do stuff
//...
await someFunc1(); //Method1
// await comeFunc2(); //Method2
//do more stuff
}
someFunc1() async {
msm.asyncfunc1();
msm.asyncfunc2();
}
someFunc2() async {
await Future.wait([
msm.asyncFunc1(),
msm.asyncFunc2(),
]);
}
#override
Widget build(BuildContext context) {
return isAuth ? buildAuthScreen() : buildUnAuthScreen();
}
Using Future.wait(List<Future>) will wait for all the async operations without sequence as mentioned in the docs. While using await consecutively, it'll wait for the first await async operation to finish before running the next await async operation. If you have a prerequisite output before running the next async operation, it's better to use await async in sequence instead.