I am getting a list of users from firebase using StreamBuilder
. I am trying to make an ‘Add as a friend’ button for the user to make friends with each other. But there is an issue I am facing. The issue here is when I click on any one of the user, all the other add buttons start with the loader. I want the loader to be only for the particular user selected.
This is before I click on anyone:
This is After I click on any user:
Here is the code I am currently using:
Column(
children: [
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.where("uid", isNotEqualTo: FirebaseAuth.instance.currentUser!.uid)
.snapshots(),
builder: (context, snapshot) {
List<Column> clientWidgets = [];
if (snapshot.connectionState == ConnectionState.waiting) {
return const CustomProgressBar();
} else if (snapshot.hasError) {
return Center(
child: ErrorMessage(message: snapshot.error.toString()),
);
} else if (!snapshot.hasData) {
return const Center(child: Center(child: CustomProgressBar()));
} else {
final clients = snapshot.data?.docs.reversed.toList();
for (var client in clients!) {
final clientWidget = Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ListTile(
leading: FadedScaleAnimation(
child: CircleAvatar(
radius: 30.0,
backgroundImage: Image.network(client["avatar"] ??
"https://i.pravatar.cc/300")
.image),
),
trailing: GestureDetector(
onTap: () {
print(client['name']);
print(isLoading);
setState(() {
isLoading = true;
});
PalRequestModel palRequest = PalRequestModel(
name: ap.userModel.name,
avatar: ap.userModel.avatar,
phoneNumber: ap.userModel.phoneNumber,
uid: ap.userModel.uid,
);
ap.sendRequestToPals(
context: context,
palRequests: palRequest,
palId: client["uid"],
onSuccess: () {
setState(() {
isLoading = false;
});
});
},
child: Container(
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
border: Border.all(
width: 0.8, color: Colors.grey)),
child: isLoading
? Padding(
padding: const EdgeInsets.symmetric(
vertical: 5.0, horizontal: 10.0),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
CustomProgressBar(),
],
),
)
: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Icon(
Icons.person_add,
size: 20,
),
SizedBox(
width: 5,
),
Text(
"add",
style: TextStyle(fontSize: 18),
),
],
)),
),
title: Text(
"${client["name"]}".toLowerCase(),
overflow: TextOverflow.ellipsis,
style: TextStyles.h2,
),
subtitle: Text(
"${client["bio"]}".toLowerCase(),
overflow: TextOverflow.ellipsis,
style: TextStyles.bodyText,
),
),
Divider(
height: 0.1,
thickness: 0.5,
endIndent: 10,
indent: 10,
color: ApplicationColors.offWhite,
),
],
);
clientWidgets.add(clientWidget);
}
}
return clientWidgets.isEmpty
? Center(
child: Padding(
padding: const EdgeInsets.only(
top: 10.0, bottom: 5.0, left: 10.0, right: 10.0),
child: Container(
height: 50,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
border: Border.all(
color: ApplicationColors.primaryColor
.withOpacity(0.5),
width: 3),
borderRadius: BorderRadius.circular(10)),
child: Center(
child: Text(
"no pals yet",
))),
))
: Expanded(child: ListView(children: clientWidgets));
}),
],
);
Please let me know what I can do about it and where I am going wrong? Thank you!
2
Answers
You’re using only one global
isLoading
variable to control the loading animation. So when it’strue
, all of your buttons will be loading at the same time.The best way to create the loading animation in this situation is creating a
null
variable for theClient
like:The condition of loading animation:
When you want to show a loading animation of a Button:
When you want to show the original Widget:
You are using is loading for all button, so when you change it, all button react with it, i guess it is better that u make your button widget separately in other class and put your variable in that class and send your bool through constructor.