Displays the same cart-quantity and total price - firebase

I am having issue with the cart notification panel as it displays the product-quantity and price of the previous user and is not updated even after i add products from new user account.
This is the code for notification panel
Widget build(BuildContext context) {
final _cartProvider = Provider.of<CartProvider>(context);
_cartProvider.getCartTotal();
return Container(
height: 45,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: AppColors.buttonnavigation,
borderRadius: BorderRadius.only(
topRight: Radius.circular(18), topLeft: Radius.circular(18))),
child: Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Center(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'${_cartProvider.cartQty}${_cartProvider.cartQty == 1 ? 'Item' : 'Items'}',
style:
TextStyle(color: Colors.white, fontSize: 14)),
Text(
' | ',
style: TextStyle(color: Colors.white),
),
Text('Rs.${_cartProvider.subTotal}',
style:
TextStyle(fontSize: 18, color: Colors.white)),
],
),
],
),
),
Here is the Provider i've used
class CartProvider with ChangeNotifier {
final CartServices _cart = CartServices();
double subTotal = 0.0;
int cartQty = 0;
QuerySnapshot? snapshot;
List cartList = [];
Future<double?> getCartTotal() async {
var cartTotal = 0.0;
List _newList = [];
QuerySnapshot snapshot =
await _cart.cart.doc(_cart.user!.uid).collection('products').get();
if (snapshot == null) {
return null;
}
snapshot.docs.forEach((doc) {
if (!_newList.contains(doc.data())) {
_newList.add(doc.data());
cartList = _newList;
notifyListeners();
}
cartTotal = cartTotal + doc['total'];
});
subTotal = cartTotal;
cartQty = snapshot.size;
this.snapshot = snapshot;
notifyListeners();
return cartTotal;
}
}
I am not exactly sure what went wrong in this as i've just started in flutter and firebase.

You might want to check what documents you are listening to. From what you say, your app may still be listening to the old user's document and thus you see no change.
P.S.: I believe you should provide a minimal piece of code to recreate the issue you are facing and using pictures is not a very good way. You should try to add the code here as text instead.

Related

Is there a way to compare two list and check for the value that are the same

I am create a social media type app I want to create a follower and following list like Instagram, I want when I go on some else profile and click on their followers or following list it shows a list of all the users that is following that person and if I am also following someone in the list it give me the option to unfollow that person and if I am not following the same person it gives me the option to follow them.
The code below is what I was to check if I am following any one in profile user followers list, What I have done is I query the data from firestore of all the user id the I am following and the same for user profile that is currently open and store the list in two separate variable and what it should do is check both list and if a user Id is in both list it means I am also following that user and should show that I am following that user and give the option to unfollow but what happens is instead of showing that I am only following the user who's id is in both list it show that I am following every one.
checkfollowers(BuildContext context, String? id) async {
final auth = Provider.of<AuthState>(context, listen: false);
List<String> followingList = [];
List<String> myFollowingList = [];
try {
final QuerySnapshot following = await _firestore
.collection('following')
.doc(auth.getCurrentUser.uid)
.collection('userFollowing')
.get();
QuerySnapshot userfollowing = await _firestore
.collection('followers')
.doc(id)
.collection('userFollowers')
.get();
following.docs.forEach((element) {
myFollowingList.add(element.id);
});
userfollowing.docs.forEach((element) {
followingList.add(element.id);
});
// followingList.where((item) => myFollowingList.contains(item));
check(value) => myFollowingList.contains(value);
isFollowedByMe = followingList.any(check);
notifyListeners();
print(followingList);
print(myFollowingList);
} catch (err) {
print(err.toString() + 'this error is coming from profileState');
}
}
below code is how I build the follower/following list
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
UserModel? users;
#override
void initState() {
final profileState = Provider.of<ProfileState>(context, listen: false);
profileState.checkfollowers(context, widget.proFileId);
super.initState();
}
userComponent(UserModel? model, BuildContext context) {
final profileState = Provider.of<ProfileState>(context);
final auth = Provider.of<AuthState>(context);
return Container(
margin: EdgeInsets.symmetric(horizontal: 10),
padding: EdgeInsets.only(top: 10, bottom: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ProfilePage(profileId: '${model?.userId}'),
),
);
},
child: Row(
children: [
Container(
width: 60,
height: 60,
child: CircleAvatar(
radius: 50,
backgroundImage: NetworkImage('${model?.profilePic}'),
),
),
SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text('${model?.userName}',
style: TextStyle(
color: Colors.black, fontWeight: FontWeight.w500)),
SizedBox(
height: 5,
),
Text(
'${model?.displayName}',
style: TextStyle(
color: Colors.grey[500],
),
),
],
)
],
),
),
auth.getCurrentUser.uid == model?.userId
? Container()
: GestureDetector(
onTap: () {
if (auth.getCurrentUser.uid == model?.userId) {
print('you can not follow your self');
} else if (profileState.isFollowedByMe == true) {
profileState.setIsFollowedByMeToFalse();
profileState.handleUnFollow(context, model?.userId);
} else if (profileState.isFollowedByMe == false) {
profileState.setIsFollowedByMeToTrue();
profileState.handleFollow(context, model?.userId);
}
},
child: AnimatedContainer(
height: 35,
width: 110,
duration: Duration(milliseconds: 300),
decoration: BoxDecoration(
color: profileState.isFollowedByMe == true
? AppColors.white
: AppColors.pinkaccent,
borderRadius: BorderRadius.circular(5),
border: Border.all(
color: Colors.grey.shade700,
),
),
child: Center(
child: Text(
profileState.isFollowedByMe == true
? 'UnFollow'
: 'Follow',
style: TextStyle(
color: profileState.isFollowedByMe == true
? Colors.black
: Colors.white),
),
),
),
)
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.white,
title: Container(
height: 38,
child: TextField(
onChanged: (value) {},
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
contentPadding: EdgeInsets.all(0),
prefixIcon: Icon(
Icons.search,
color: Colors.grey.shade500,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(50),
borderSide: BorderSide.none),
hintStyle: TextStyle(fontSize: 14, color: Colors.grey.shade500),
hintText: "Search users"),
),
),
),
body: StreamListWrapper(
stream: _firestore
.collection('followers')
.doc(widget.proFileId)
.collection('userFollowers')
.snapshots(),
itemBuilder: (context, DocumentSnapshot snapshot) {
var data = snapshot.data() as Map<String, dynamic>;
users = UserModel.fromJson(data);
return userComponent(users, context);
},
text: '${widget.user?.userName} as no Followers',
),
);
It would be simpler (and more performant) to use the Set data structure rather than List. The intersection method on Set returns the items contained in both sets.
for example:
void main() {
Set<String> followingSet = {'Jeff', 'Mike', 'Joe', 'Jess'};
Set<String> myFollowingSet = {'Jess', 'Matt', 'Mike', 'Frank'};
Set<String> usersInBothSets = followingSet.intersection(myFollowingSet);
print(usersInBothSets); // {Mike, Jess}
}

Update Button Color in ListView based on FireBase entry

I have the following problem and just cannot solve it... I am building some kind of question/answer (Yes/No) app and want to achieve that if a button was pressed (an answer was given) the button stays highlighted with a color. So if a user goes back to a previous button he can see which answer he gave. Right now it is like that, all the questions come out of firebase with the structure
final CollectionReference _questionsCollectionReference =
FirebaseFirestore.instance
.collection("content")
.doc(content)
.collection("block")
.doc(block)
.collection("questions");
If a user answers a question it will get saved in his User Profile under
final firestoreInstance = FirebaseFirestore.instance;
await firestoreInstance
.collection("users")
.doc(user!.id)
.collection("content")
.doc(content)
.collection("block")
.doc(block)
.collection("questions")
.doc(question)
.set({
"answer": answer, //FieldValue.arrayUnion([someData]),
}).then((_) {
print("success!");
});
Now it should basically be like, if the answer in the user path == "yes", colorize the "yes" button.
the question_view.dart
Here we build the view with a PageViewBuilder and give it a QuestionItem
import 'package:fbapp/ui/shared/ui_helpers.dart';
import 'package:fbapp/ui/widgets/question_item.dart';
import 'package:fbapp/viewmodels/questions_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:stacked/stacked.dart';
class QuestionsView extends StatelessWidget {
final String block;
final String content;
const QuestionsView({Key? key, required this.block, required this.content})
: super(key: key);
#override
Widget build(BuildContext context) {
return ViewModelBuilder<QuestionsViewModel>.reactive(
viewModelBuilder: () => QuestionsViewModel(),
onModelReady: (model) => model.fetchPosts(content, block),
builder: (context, model, child) => Scaffold(
backgroundColor: Colors.white,
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
verticalSpace(35),
Row(
children: <Widget>[
SizedBox(
height: 80,
child: Image.asset('assets/images/logo.png'),
),
],
),
Expanded(
child: model.questions != null
? Center(
child: Container(
width: 700,
height: 450,
child: PageView.builder(
controller: model.getPageController(),
scrollDirection: Axis.vertical,
itemCount: model.questions!.length,
itemBuilder: (context, index) =>
QuestionItem(
question: model.questions![index],
content: content,
block: block,
nextPage: model.nextPage,
saveCurrentUserAnswer:
model.saveCurrentUserAnswer,
getCurrentUserAnswer:
model.getCurrentUserAnswer),
),
),
)
: Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(
Theme.of(context).primaryColor),
),
))
],
),
),
));
}
}
question_item.dart
import 'package:fbapp/app/app.locator.dart';
import 'package:fbapp/models/question.dart';
import 'package:flutter/material.dart';
import 'package:stacked_services/stacked_services.dart';
class QuestionItem extends StatelessWidget {
final Question? question;
final String? content;
final String? block;
final String? id;
final void Function()? nextPage;
final Future Function(
String content, String block, String? the question, String answer)?
saveCurrentUserAnswer;
final Future Function(String content, String block, String? question)?
getCurrentUserAnswer;
const QuestionItem(
{Key? key,
this.question,
this.nextPage,
this.saveCurrentUserAnswer,
this.content,
this.block,
this.id,
this.getCurrentUserAnswer})
: super(key: key);
#override
Widget build(BuildContext context) {
return Flex(
direction: Axis.horizontal,
children: [
Expanded(
child: Card(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width * 0.6,
child: ListTile(
leading: Icon(Icons.security),
trailing: IconButton(
icon: Icon(Icons.info),
onPressed: () {
final DialogService _dialogService =
locator<DialogService>();
_dialogService.showDialog(
dialogPlatform: DialogPlatform.Material,
title: "Info",
description: question!.info);
},
),
subtitle: Text("some nice text"),
title: Text(question!.q!),
),
),
const SizedBox(height: 50),
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
margin: EdgeInsets.all(10),
height: 50.0,
child: SizedBox.fromSize(
size: Size(50, 50), // button width and height
child: ClipOval(
child: Material(
color: "yes" ==
getCurrentUserAnswer!(
content!, block!, question!.id)
.toString()
? Color.fromRGBO(0, 144, 132, 1)
: Colors.grey, // button color
child: InkWell(
splashColor: Color.fromRGBO(0, 144, 132, 1),
// splash color
onTap: () {
nextPage!();
saveCurrentUserAnswer!(
content!, block!, question!.id, "yes");
},
// button pressed
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.thumb_up,
color: Colors.white,
), // icon
Text(
"Yes",
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
), // text
],
),
),
),
),
),
),
const SizedBox(width: 100, height: 100),
Container(
margin: EdgeInsets.all(10),
height: 50.0,
child: SizedBox.fromSize(
size: Size(50, 50), // button width and height
child: ClipOval(
child: Material(
color: "no" ==
getCurrentUserAnswer!(
content!, block!, question!.id)
.toString()
? Color.fromRGBO(0, 144, 132, 1)
: Colors.grey, // button colorr
child: InkWell(
splashColor: Color.fromRGBO(0, 144, 132, 1),
// splash color
onTap: () {
nextPage!();
saveCurrentUserAnswer!(
content!, block!, question!.id, "no");
},
// button pressed
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.thumb_down,
color: Colors.white,
), // icon
Text(
"No",
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
), // text
],
),
),
),
),
),
),
],
)
],
),
),
),
],
);
}
}
That's how I build my page with the "yes" and "no" buttons.
This is the question_view_model.dart where we reference the firebase functions to save and get answers like so:
...
Future saveCurrentUserAnswer(
String content, String block, String? question, String answer) async {
await _fireStoreService!.saveCurrentUserAnswer(
_authenticationService!.currentUser, content, block, question, answer);
}
Future getCurrentUserAnswer(
String content, String block, String? question) async {
await _fireStoreService!.getCurrentUserAnswer(
_authenticationService!.currentUser, content, block, question);
}
...
and the firebase functions to do it:
...
Future saveCurrentUserAnswer(User? user, String content, String block,
String? question, String answer) async {
final firestoreInstance = FirebaseFirestore.instance;
await firestoreInstance
.collection("users")
.doc(user!.id)
.collection("content")
.doc(content)
.collection("block")
.doc(block)
.collection("questions")
.doc(question)
.set({
"answer": answer, //FieldValue.arrayUnion([someData]),
}).then((_) {
print("success!");
});
}
Future<String> getCurrentUserAnswer(
User? user, String content, String block, String? question) async {
String answer = "";
try {
final DocumentReference _answerCollectionReference = FirebaseFirestore
.instance
.collection("users")
.doc(user!.id)
.collection("content")
.doc(content)
.collection("block")
.doc(block)
.collection("questions")
.doc(question);
var answerDocumentSnapshot = await _answerCollectionReference;
await answerDocumentSnapshot.get().then((a) {
if (a.exists) {
answer = a["answer"];
} else {
answer = "";
}
});
print("Answer: $answer");
return answer;
} catch (e) {
return e.toString();
}
}
...
The getCurrentUserAnswer prints successfully the answer (always 2 times, not sure why...)
Cont: 00_DSGVO -- Block: b1
2
Answer: no
2
Answer: yes
success!
But the button never changes the color. I also tried it with Stateful and setState and Stateless and ValueNotifier, but somehow it doesn't work. One problem is that it have to check first if there is an answer, than rebuild the UI (or only the button) but it is building it first and afterwards checking for the answer...
I figured it out with some help, the answer was to use a FuturBuilder to wrap the question_item and set the future to getCurrentUserAnswer.
return FutureBuilder(
future: getCurrentUserAnswer!(content!, block!, question!.id),
builder: (BuildContext context, AsyncSnapshot snapshot) { return Flex(...

Still raise "firebase_auth/email-already-in-use" exception, although registered an unique email in firebase_auth flutter

I have a registration state that would handled registration of firebase authentication. From my code, there are two widgets. First (registerForm(context)), it shows registration forms. When "Register" Elevated Button is pressed, they will changed to second widgets (registerProcess(context)) there are processed a registrations.
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:connectivity/connectivity.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:path_provider/path_provider.dart';
class SignUp extends StatefulWidget {
static const routeName = "/signUp";
#override
_SignUpState createState() => _SignUpState();
}
class _SignUpState extends State<SignUp> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
final _nameController = TextEditingController();
final auth = FirebaseAuth.instance;
final fireStore = FirebaseFirestore.instance;
final storage = FirebaseStorage.instance;
bool isConnected = false;
bool passwordNotMatch = false;
bool networkWarning = false;
bool emailWarning = false;
bool isSignedUp;
#override
void initState() {
super.initState();
isSignedUp = false;
checkConnectivity();
}
Future<void> checkConnectivity() async {
var conRes = await Connectivity().checkConnectivity();
if (conRes != ConnectivityResult.none) {
isConnected = true;
}
}
Future<UserCredential> _signUp() async {
final validate = await auth.createUserWithEmailAndPassword(
email: _emailController.text,
password: _passwordController.text);
await auth.currentUser.sendEmailVerification();
final file = await getImgFromAssets("default.png");
await storage.ref("profile/${_emailController.text}.png").putFile(file);
return validate;
}
Future<File> getImgFromAssets(String path) async {
final byteData = await rootBundle.load('assets/$path');
final file = File('${(await getTemporaryDirectory()).path}/$path');
await file.writeAsBytes(byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes));
return file;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: isSignedUp ? registerProcess(context) : registerForm(context));
}
Widget registerForm(BuildContext context) {
double height = MediaQuery.of(context).size.height;
// double width = MediaQuery.of(context).size.width;
return SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Sign Up",style: Theme.of(context).textTheme.headline4,),
SizedBox(height: height * 0.02,),
Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Profile Name',
),
controller: _nameController,
),
SizedBox(height: height * 0.02,),
TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Email Address',
),
controller: _emailController,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text("Note: Please be unique"),
],
),
emailWarning ? Text("Email is already used", style: TextStyle(color: Colors.red),)
: Container(),
SizedBox(height: height * 0.02,),
TextField(
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
),
controller: _passwordController,
),
passwordNotMatch ? Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text("Password Not Match", style: TextStyle(color: Colors.red),),
],
) : SizedBox(height: 0,),
SizedBox(height: height * 0.02,),
TextField(
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Confirm Password',
),
controller: _confirmPasswordController,
),
ElevatedButton(
onPressed: () {
if (!isConnected) {
setState(() {
networkWarning = !networkWarning;
});
} else if (_confirmPasswordController.text != _passwordController.text) {
setState(() {
passwordNotMatch = !passwordNotMatch;
});
}
// else if(_confirmPasswordController.text == null || _passwordController.text == null){}
else {
setState(() {
isSignedUp = !isSignedUp;
});
}
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Register", style: TextStyle(fontSize: height * 0.025),),
),
),
],
),
),
],
),
),
);
}
Widget registerProcess(BuildContext context) {
return DefaultTextStyle(
style: Theme.of(context).textTheme.headline3,
textAlign: TextAlign.center,
child: FutureBuilder<UserCredential>(
future: _signUp(),
builder: (context, snapshot) {
List<Widget> children;
if (snapshot.hasData) {
children = <Widget>[
const Icon(
Icons.check_circle_outline,
color: Colors.green,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('You\'re registered. Please check your email for verification'),
)
];
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: children,
),
);
} else if (snapshot.hasError) {
print(snapshot.error.toString());
children = <Widget>[
const Icon(
Icons.error_outline,
color: Colors.red,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: Email isn\'t valid or already use'),
)
];
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: children,
),
);
} else {
children = <Widget>[
SizedBox(
child: CircularProgressIndicator(),
width: 60,
height: 60,
),
Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Awaiting result...'),
)
];
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: children,
),
);
}
}
)
);
}
}
Although I registered any unique email, it still raise an exception: [firebase_auth/email-already-in-use] The email address is already in use by another account. (Output from print(snapshot.error.toString())). I've checked at my firebase console repeatedly. Are there any mistakes?
Firebase is known for storing some cached data (including authentication and firestore data) in your device once you install the app and start using it's services.
In other words, it's very likely that you're getting this exception because you've deleted the user from firebase but the cached data (which firebase always checks first) is still in your device. Make sure that you really deleted the user in firebase, completely uninstall the app from your device, run flutter clean and then try again. Let me know if it works

Flutter: How to sync data from sqlite?

TextEditingController textEditingController = TextEditingController();
bool editState = false;
StatefulBuilder(
builder: (context, setState){
return AlertDialog(
content: SingleChildScrollView(
child: Column(
children: [
Container(
child: Padding(
padding: const EdgeInsets.only(bottom: 8, left: 8, right: 8),
child: Align(
alignment: Alignment.centerLeft,
child: editState ?
TextField(
controller: textEditingController,
) :
Text(checkpoint["memo"])
),
),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: GestureDetector(
child: editState ? Text("Save") : Text("Edit"),
onTap: () async {
if(editState == true){
DBHelper dbHelper = DBHelper();
await dbHelper.updateCheckpoint(checkpoint["userkey"], checkpoint["id"], textEditingController.text);
}
else{
setState((){
editState = !editState;
});
}
},
),
),
],
),
)
],
),
)
);
},
)
This is my code. I want the Text (checkpoint ["memo"]) to be updated when I type something and click the save button.
I tried using ChangeNotifierProvider, but it didn't work. I may have used it incorrectly. So I don't know how to approach this problem. How can I solve this?
For text field there is one method onChanged(). Which gives you an updated text. You can save that text to some variable and use the same variable to display text where you want to display it. Don't forget to call setState() something like following in onChanged() to update the textfield text.
setState(() { _memo = newTextValue; });

Show loading indicator /spinner when the page data isn't fully loaded from Firebase - Flutter

In my Flutter app, I am using ModalProgressHUD to show a spinner when I click on save buttons in my form screens and it stops spinner once data successfully writes to Firebase.
I have this screen that uses Listview.builder to display a list of all my expenses and I want to automatically show spinner as soon as the page displays, and to stop spinner once all the data from Firebase fully loads.
I need assistance in doing this. I've pasted excerpt of my code as shown below. Thanks in advance.
//class wide declaration
bool showSpinner = true;
Widget build(BuildContext context) {
ExpenseNotifier expenseNotifier = Provider.of<ExpenseNotifier>(context);
Future<void> _resfreshList() async {
expenseNotifier.getExpenses(expenseNotifier);
var expenseList = ExpenseNotifier.getExpenses(expenseNotifier);
if (expenseList != null) {
setState(() {
showSpinner = false;
});
}
return Scaffold(
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: RefreshIndicator(
onRefresh: _resfreshList,
child: Consumer<ExpenseNotifier>(
builder: (context, expense, child) {
return expense == null
? Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
PaddingClass(bodyImage: 'images/empty.png'),
SizedBox(
height: 20.0,
),
Text(
'You don\'t have any expenses',
style: kLabelTextStyle,
),
],
)
: ListView.separated(
itemBuilder: (context, int index) {
var myExpense = expense.expenseList[index];
return Card(
elevation: 8.0,
color: Colors.white70,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
RegularExpenseTextPadding(
regText:
'${_formattedDate(myExpense.updatedAt)}',
),
Container(
margin: EdgeInsets.all(20.0),
padding: const EdgeInsets.all(15.0),
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(5.0)),
border: Border.all(
color: kThemeStyleBorderHighlightColour),
),
child: Row(
children: <Widget>[
Expanded(
flex: 5,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Text(
'${myExpense.amount}',
style: kRegularTextStyle,
),
SizedBox(
height: 20.0,
),
Text(
myExpense.description,
style: kRegularTextStyle,
),
],
),
),
Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
expenseNotifier.currentExpense =
expenseNotifier
.expenseList[index];
Navigator.of(context).push(
MaterialPageRoute(builder:
(BuildContext context) {
return ExpenseDetailsScreen();
}));
},
child: Icon(
FontAwesomeIcons.caretDown,
color: kThemeIconColour,
),
),
),
],
),
),
],
),
);
},
separatorBuilder: (BuildContext context, int index) {
return SizedBox(
height: 20.0,
);
},
itemCount: expenseNotifier.expenseList.length,
);
},
),
),
),
);
}
this is an example from my app:
bool _isLoading = false; <- default false
bool _isInit = true; <- to mae it only load once
#override
void initState() {
if (_isInit) {
// activating spinner
_isLoading = true;
// your function here <------
_isInit = false;
super.initState();
}
Initstate gets called before the user can see any kind of thin in your app, so this is the perfect place to make your firebase data load. with this logic from above the loading spinner shows as long you are receiving the data. And your body looks like the following then:
#override
Widget build(BuildContext context) {
return _isLoading <- is loading condition true? shows spinner
? Center(child: CircularProgressIndicator()) <- loading spinner
// else shows your content of the app
: SafeArea(
child: Container()
....

Resources