Flutter null check operator used on a null value flutter - flutter

I am trying to check if user make a payment or not.If payment is done, user will see homepage.If payment is not done User will see the payment page. The problem is that I am getting null check operator used on a null value. What I am doing wrong?
class TwoPage extends StatelessWidget {
Package? offer;
PurchaserInfo? _purchaserInfo;
bool? payment;
Future<bool> ispaymentdone() async {
await Purchases.setDebugLogsEnabled(true);
await Purchases.setup("public_key");
PurchaserInfo purchaserInfo = await Purchases.getPurchaserInfo();
print(purchaserInfo);
print("buraya kadar iyi");
Offerings offerings = await Purchases.getOfferings();
print(offerings);
// optional error handling
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
//if (!mounted) return;
_purchaserInfo = purchaserInfo;
if(purchaserInfo.entitlements.all["content-usage"]!=null){
if ( purchaserInfo.entitlements.all["content-usage"]!.isActive) {
print("trueee");
return true;
}
}
return false;
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: ispaymentdone(),
builder: (context, snapshot) {
if (snapshot.data == null)
return SizedBox(
child: Center(child: CircularProgressIndicator(color:Colors.purple)),
height: 10.0,
width: 10.0,
);
else if (snapshot.data == true)
return NewHomeScreen();
else
return MainPayment(purchaserInfo: _purchaserInfo, offer: offer);
},
);
}
}

This is because u r using null check operator on your variables but the value of that particular variable is NULL at a point when u want to use it ,either u have to give some initial value to that variable or make sure value is not null.

It is my mistake:
return MainPayment(purchaserInfo: _purchaserInfo, offer: offer);
I didn't assign anything to offer variable.

Related

Flutter, How handle state when awaiting for firebase data

checkBorrowerAlreadyExists() Function checks if a user input name already exists in firebase data and returns a bool. I am using this function in an if condition after onPressed of a button, I am providing user input as an argument to the function in if condition and display the snackbar based on the bool value. This works perfectly fine when firebase already has data, But initially when the user register and opens the app, firebase won't have any data to check if name already exists and nothing happens on the screen.
I used else condition to return false if no data, move on and add input in firebase, but didn't work. I also tried using the bool in different way but didn't work. How do I solve this.
checkBorrowerAlreadyExists(String textfieldname) async {
await for (var snapshots in _firestorem
.collection('lender')
.doc(auth.currentUser?.uid)
.collection('borrowers')
.snapshots()) {
for (var message in snapshots.docs) {
bool isThere;
if (message.data().containsValue(textfieldname)) {
return isThere = true;
} else {
return isThere = false;
}
}
}
}
onPressed: () async {
if (borrowerNameController.text.isEmpty) {
Get.snackbar(
'Error:', "Borrower Name can't be empty",
backgroundColor: Colors.red);
} else if (await checkBorrowerAlreadyExists(
borrowerNameController.text)) {
Get.snackbar('Error',
'Borrower Name alread exists, Enter new name',
backgroundColor: Colors.red);
} else{ upload data in firebase}
You can't use await in the build method, as there's no way to hold up rendering.
The solution is actually in the return type of your function, which you left implicit not. If you make the return type explicit, it is:
Future<bool> checkBorrowerAlreadyExists(String textfieldname) async {
...
Since this returns a Future, you need to use a FutureBuilder in your build method to render the boolean value (and to determine what to render until that value is available).
So something like:
onPressed: () async {
if (borrowerNameController.text.isEmpty) {
Get.snackbar('Error:', "Borrower Name can't be empty", backgroundColor: Colors.red);
}
else {
return FutureBuilder<String>(
future: checkBorrowerAlreadyExists(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
List<Widget> children;
if (snapshot.hasData) {
return Text(borrowerNameController.text)
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}'),
} else {
return Text('Awaiting result...'),
}
},
),
I recommend checking out the documentation on FutureBuilder and Flutter's codelab on asynchronous programming.

Flutter Dart "unconditionally accessed because the receiver can be 'null'." problem

Here is my flutter code where I try to use FutureBuilder but encounter problems due to the null protection in Dart.
class AbcClass extends StatefulWidget {
#override
_AbcClassState createState() =>
_AbcClassState();
}
class _AbcClassState
extends State<AbcClass>
with AutomaticKeepAliveClientMixin {
_AbcClassState();
Future? _purchaserInfoSnapshot;
#override
void initState() {
_purchaserInfoSnapshot = setPurchaserInfo();
super.initState();
}
setPurchaserInfo() async {
PurchaserInfo purchaserInfo = await getPurchaserInfo();
Purchases.addPurchaserInfoUpdateListener((purchaserInfo) async {
if (this.mounted) {
setState(() {
_purchaserInfoSnapshot = Future.value(purchaserInfo);
});
}
});
return purchaserInfo;
}
#override
Widget build(BuildContext context) {
super.build(context);
return FutureBuilder(
future: _purchaserInfoSnapshot,
builder: (context, snapshot) {
if (!snapshot.hasData ||
snapshot.data == null ||
snapshot.connectionState != ConnectionState.done) {
return Center(
child: Text(
'Connecting...',
style: Theme.of(context).textTheme.headline3,
));
} else {
if (snapshot.data.entitlements.active.isNotEmpty) {
return Scaffold(...);
} else {
return MakePurchase();
}
}
});
}
}
The part that creates the problem is the following:
if (snapshot.data.entitlements.active.isNotEmpty)
And the error message:
The property 'entitlements' can't be unconditionally accessed because the receiver can be 'null'.
Try making the access conditional (using '?.') or adding a null check to the target ('!').
I tried updating it as the following:
else if (snapshot.data!.entitlements.active.isNotEmpty)
... but it did not help.
Any ideas on how I am supposed to deal with it?
Note: I did not paste the entire code as it involves a lot of opther logic that is not relevant to this question. I hope the pseudo code above will still help.
You've probably enabled null-safety in your project. Now the compiler won't let you compile code that it is not sure that won't throw an Null Exception error (like trying to access a property of a nullable variable without checking if its not null first)
You can either mark it with the bang ! operator and tell the compiler this variable will never be null, which may not be true, so be aware when using it.
Or you could check if the variable is not null before accessing its properties or trying to call methods on it.
Try this:
final active = snapshot.data?.entitlements?.active;
if (active != null && active.isNotEmpty)
I've also seen that you tried to check if snapshot.data was not null first. But remember that for the flow-analysis to work, you have to assign the variable you're trying to test to a local final variable.
Like:
final data = snapshot.data;
if (!snapshot.hasData || data == null || snapshot.connectionState != ConnectionState.done) {
return ...

Displaying multiple screens based on ternary condition in dart

I have a scenario where I am showing a spinner when a page loads and while it's loading, it fetches some data in DB and sets a bool value to either true or false based on data availability.
I then want to share either screen A or B based on the boolean result.
I have done the following in my code but the app keeps showing the spinner. Any ideas what I might be doing incorrectly?
return _isLoading?
Center(child:Loading(),):
_isPersonalInfoSubmitted?ScreenA():ScreenB();
Second Attempt (Using Future Builder)
I want to show categories if the loggedin user is an admin. Else, for the rest of the users, I want to fetch address of the user from the DB. If the address is null, show Personal Details Screen else show Categories.
return FutureBuilder (
future: userId=='ADMIN_ID'?
Provider.of<Categories>(context,listen:false).fetchAndReturnCategories():
Provider.of<Addresses>(context,listen: false).fetchAndReturnAddress(userId)!=null?
Provider.of<Categories>(context,listen:false).fetchAndReturnCategories():null,
builder: (context, snap) {
inspect(snap);
if (snap.hasData) {
var categoriesData = Provider.of<Categories>(context);
return snap.hasData?
Scaffold(...) : PersonalDetails();
What happens here is that the method fetchAndReturnCategories gets executed even if the userID is not admin id. Do I have the correct setup?
prefer to use FutureBuilder
FutureBuilder<SomeClass>(
future: fetchdatFuture,
builder: (ctx, snap) {
if (snap.hasData) {
return snap.data?ScreenA():ScreenB();
} else if (snap.connectionState == ConnectionState.waiting)
{
return Center(child:Loading());
}
return Text("Error");
},
)
I think you have missed the setState to rebuild your widget
var bool _isLoading = true;
return _isLoading?
Center(child:Loading(),):
_isPersonalInfoSubmitted?ScreenA():ScreenB();
void _apiCall() {
// After Success of API Call
setState((){
_isLoading = false;
})
}

Flutter: Future<String> returns null but if printing it has value

username is null but if I'm printing 'value' it contains some string, how can I get 'value'?
class HomeWrapper extends StatelessWidget {
final DataBaseServices _db = DataBaseServices();
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
String username;
_db.getUsername(user).then((value) => username = value);
print(username);
if(username != null){
return Home();
}else{
_db.createBlankUser(user);
return EditProfile();
}
}
.then() is called when the value of the Future is returned. So the value of value is always non null, whereas username is null when you print it.
Try the difference by replacing .then(...) with:
.then((value){
username = value;
print(username);
});
Additionally, you can have a look at how to handle Asynchronous data in Flutter
I'm guessing _db.getUsername is returning a Future?
In that case you should look into using FutureBuilder
https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
return FutureBuilder(
builder: (context, snap) {
//snap.data will be the username
if(snap.hasData) {
return Home();
} else {
//you need to wait for another Future here I guess?
return FutureBuilder(
builder: (context, snap2){
if(snap2.connectionState == ConnectionState.done){
return EditProfile();
} else {
//return some sort of circular loader icon.
}
},
future: _db.createBlankUser(user)
);
}
},
future: _db.getUsername(user),
);

How to inform FutureBuilder that database was updated?

I have a group profile page, where a user can change the description of a group. He clicks on the description, gets on a new screen and saves it to Firestore. He then get's back via Navigator.pop(context) to the group profile page which lists all elements via FutureBuilder.
First, I had the database request for my FutureBuilder inside the main build method (directly inside future builder 'future: request') which was working but I learnt it is wrong. But now I have to wait for a rebuild to see changes. How do I tell FutureBuilder that there is a data update?
I am loading Firestore data as follows within the group profile page:
Future<DocumentSnapshot> _future;
#override
void initState() {
super.initState();
_getFiretoreData();
}
Future<void> _getFiretoreData() async{
setState(() {
this._future = Firestore.instance
.collection('users')
.document(globals.userId.toString())
.get();});
}
The FutureBuilder is inside the main build method and gets the 'already loaded' future like this:
FutureBuilder(future: _future, ...)
Now I would like to tell him: a change happened to _future, please rebuild ;-).
Ok, I managed it like this (which took me only a few lines of code). Leave the code as it is and get a true callback from the navigator to know that there was a change on the second page:
// check if second page callback is true
bool _changed = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ProfileUpdate(userId: globals.userId.toString())),
);
// if it's true, reload future data
_changed ? _getFiretoreData() : Container();
On the second page give the save button a Navigator.pop(context, true).
i would advice you not to use future builder in this situation and use future.then() in an async function and after you get your data update the build without using future builder..!
Future getData() async {
//here you can call the function and handle the output(return value) as result
getFiretoreData().then((result) {
// print(result);
setState(() {
//handle your result here.
//update build here.
});
});
}
How about this?
#override
Widget build(BuildContext context) {
if (_future == null) {
// show loading indicator while waiting for data
return Center(child: CircularProgressIndicator());
} else {
return YourWidget();
}
}
You do not need to set any state. You just need to return your collection of users in your GetFirestoreData method.
Future<TypeYouReturning> _getFirestoreData() async{
return Firestore.instance
.collection('users')
.document(globals.userId.toString())
.get();
}
Inside your FutureBuilder widget you can set it up something like Theo recommended, I would do something like this
return FutureBuilder(
future: _getFirestoreData(),
builder: (context, AsyncSnapshot<TypeYouReturning> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
if (snapshot.data.length == 0)
return Text("No available data just yet");
return Container();//This should be the desire widget you want the user to see
}
},
);
Why don't you use Stream builder instead of Future builder?
StreamBuilder(stream: _future, ...)
You can change the variable name to _stream for clarity.