getting duplicate data in stream builder flutter - flutter

I hope you guys are happy and healthy
I am facing a problem in flutter
i will be very thankful to you if any of you help me resolving this issue
I am trying to fetch data from api
and show it in alphabetic list with the help of stream builder
but i am getting the same data multiple of time
let me show you the code
here i am trying to fetch data from api and convert it into list
Future<List<DisplayClients>> getClientList() async {
String email = widget.email;
final response = await http.get(
Uri.parse(
api),
);
if (response.statusCode == 200) {
final parsed = json.decode(response.body).cast<Map<String, dynamic>>();
var list = parsed
.map<DisplayClients>(
(json) => DisplayClients.fromMap(json),
)
.toList();
print('clientList');
print(list.length);
for (var i = 0; i < list.length; i++) {
clientList.add(AlphabetSearchModel(
title: list[i].name,
subtitle: list[i].email,
phoneNumber: list[i].phoneNumber));
}
print(clientList);
return parsed
.map<DisplayClients>(
(json) => DisplayClients.fromMap(json),
)
.toList();
} else {
throw Exception('Failed to load album');
}
}
here i am showing i am using data in streamBuilder
StreamBuilder<List<DisplayClients>>(
stream: getClientsStream(),
builder: (context, snapshot) {
if (snapshot.hasData) {
// var list = s
print('got data');
print(snapshot.data!.length);
return SizedBox(
height: MediaQuery.of(context).size.height * 0.7,
child: AlphabetSearchView.modelList(
list: clientList,
buildItem: (context, index, item) {
return Padding(
padding:
const EdgeInsets.symmetric(vertical: 6),
child: Card(
// margin: EdgeInsets.all(12),
elevation: 3,
color: Constants.purpleLight,
shadowColor: Colors.white,
child: Row(children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [
Constants.red,
Constants.orange,
],
begin: Alignment.bottomLeft,
end: Alignment.topRight)),
child: CircleAvatar(
radius: 40,
child: Icon(
Icons.person,
color: Colors.white,
size: 40,
),
backgroundColor:
Colors.transparent),
),
),
SizedBox(
width: 10,
),
CustomText(
text: item.title,
color: Colors.white,
size: 18,
weight: FontWeight.w400,
),
SizedBox(
width: 10,
),
CircleAvatar(
backgroundColor: Constants.orange,
radius: 4,
),
SizedBox(
width: 10,
),
CustomText(
text: item.subtitle,
color: Colors.white70,
size: 14,
weight: FontWeight.w400,
),
SizedBox(
width: 10,
),
CircleAvatar(
backgroundColor: Constants.orange,
radius: 4,
),
SizedBox(
width: 10,
),
CustomText(
text: item.phoneNumber,
color: Colors.white70,
size: 14,
weight: FontWeight.w400,
),
Spacer(),
Padding(
padding: const EdgeInsets.all(12),
child: primaryButton(
verticalPadding: 0,
context: context,
text: 'Deal with ${item.title}',
onTap: () {
Navigator.push(context,
MaterialPageRoute(
builder: (context) {
return SeperateClient(
clientName: item.title,
email: widget.email,
clientEmail:
item.subtitle!);
}));
}),
)
])));
},
)
);
}
return Container();
},
)
i am calling getClientList() method in initSate
here is code for getClientStream()
Stream<List<DisplayClients>> getClientsStream() async* {
while (true) {
await Future.delayed(Duration(milliseconds: 1));
yield await getClientList();
}
}
this code is returning multiple data's
please let me know where i am making mistakes
i will be thankful to you
Thanks :)

Try the following code:
Stream<List<DisplayClients>> getClientsStream() async* {
await Future.delayed(const Duration(milliseconds: 1));
String email = widget.email;
final response = await http.get(
Uri.parse(api),
);
if (response.statusCode == 200) {
final parsed = json.decode(response.body).cast<Map<String, dynamic>>();
var list = parsed
.map<DisplayClients>(
(json) => DisplayClients.fromMap(json),
)
.toList();
print('clientList');
print(list.length);
for (var i = 0; i < list.length; i++) {
clientList.add(AlphabetSearchModel(title: list[i].name, subtitle: list[i].email, phoneNumber: list[i].phoneNumber));
}
print(clientList);
yield* parsed
.map<DisplayClients>(
(json) => DisplayClients.fromMap(json),
)
.toList();
} else {
throw Exception('Failed to load album');
}
}

Related

How to get certain user data when clicking to one specific item in List View Flutter from Firebase Realtime Database?

Currently, I am working on a Flutter project which is a tricycle booking system. I am developing two mobile apps in flutter, one for the passenger, and another for the driver. The functionality that I'm having a problem is that a passenger should ask or request a driver for his/her booking. On the driver's side, the driver will be able to see the list of active passengers (these are those who requested a ride). All these rides are stored in a list called pList. When a modal popup displays all the items (passengers that request a ride) in the pList, and then the driver chose one, the passenger data that is being fetched is only the passenger who FIRST requested a ride or the first item being listed in the list, and not the one who the driver has chosen. Do you have any idea why and how will I fix this problem?
What I want to happen is when the driver clicked on passenger C, passenger C data will be fetched and not passenger A (first to request). Any help will be much appreciated.
Here is my code:
readUserInformation(BuildContext context) async
{
Object? empty = "";
var passengerListKeys = Provider.of<AppInfo>(context, listen: false).activePassengerList;
for(String eachPassengerKey in passengerListKeys)
{
FirebaseDatabase.instance.ref()
.child("All Ride Requests")
.child(eachPassengerKey)
.once()
.then((snapData)
{
if(snapData.snapshot.value != null)
{
var passengerRequestInfo = snapData.snapshot.value;
if (passengerRequestInfo.toString() == empty.toString())
{
print("Same lang");
return null;
}
else
{
empty = passengerRequestInfo;
setState(() {
pList.add(passengerRequestInfo);
});
print("pList: " + pList.toString());
}
double originLat = double.parse((snapData.snapshot.value! as Map)["origin"]["latitude"]);
double originLng = double.parse((snapData.snapshot.value! as Map)["origin"]["longitude"]);
String originAddress = (snapData.snapshot.value! as Map)["originAddress"];
double destinationLat = double.parse((snapData.snapshot.value! as Map)["destination"]["latitude"]);
double destinationLng = double.parse((snapData.snapshot.value! as Map)["destination"]["longitude"]);
String destinationAddress = (snapData.snapshot.value! as Map)["destinationAddress"];
String userName = (snapData.snapshot.value! as Map)["username"];
String userId = (snapData.snapshot.value! as Map)["id"];
// String userPhone = (snapData.snapshot.value! as Map)["phone"];
String? rideRequestId = snapData.snapshot.key;
UserRideRequestInformation userRideRequestDetails = UserRideRequestInformation();
userRideRequestDetails.originLatLng = LatLng(originLat, originLng);
userRideRequestDetails.originAddress = originAddress;
userRideRequestDetails.destinationLatLng = LatLng(destinationLat, destinationLng);
userRideRequestDetails.destinationAddress = destinationAddress;
userRideRequestDetails.userName = userName;
userRideRequestDetails.userId = userId;
// userRideRequestDetails.userPhone = userPhone;
userRideRequestDetails.rideRequestId = rideRequestId;
showDialog(
context: context,
builder: (BuildContext context) => ConfirmDialogBox(
userRideRequestDetails: userRideRequestDetails,
),
);
}
else
{
Fluttertoast.showToast(msg: "This Ride Request Id do not exist.");
}
});
}
}
This is the UI for the modal popup:
import 'package:ehatid_driver_app/new_trip_screen.dart';
import 'package:ehatid_driver_app/user_ride_request_information.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:responsive_sizer/responsive_sizer.dart';
import 'global.dart';
class ConfirmDialogBox extends StatefulWidget
{
UserRideRequestInformation? userRideRequestDetails;
ConfirmDialogBox({this.userRideRequestDetails});
#override
State<ConfirmDialogBox> createState() => _ConfirmDialogBoxState();
}
class _ConfirmDialogBoxState extends State<ConfirmDialogBox>
{
final currentFirebaseUser = FirebaseAuth.instance.currentUser!;
#override
Widget build(BuildContext context)
{
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
backgroundColor: Colors.transparent,
elevation: 2,
child: Container(
margin: const EdgeInsets.all(8),
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
),
child: ListView.builder(
itemCount: pList.length,
itemBuilder: (BuildContext context, int index)
{
return GestureDetector(
onTap: ()
{
setState(() {
chosenPassengerId = pList[index]["id"].toString();
print("Passenger Id: " + chosenPassengerId.toString());
FirebaseDatabase.instance.ref()
.child("passengers")
.child(chosenPassengerId!)
.once()
.then((snap)
{
print("Snap: " + snap.toString());
if(snap.snapshot.value != null)
{
//send notification to that specific driver
// sendNotificationToDriverNow(chosenPassengerId!);
//Display Waiting Response from a Driver UI
// showWaitingResponseFromPassengerUI();
print("Waiting for Response");
//Response from the driver
FirebaseDatabase.instance.ref()
.child("passengers")
.child(chosenPassengerId!)
.child("newRideStatus")
.set("accepted");
FirebaseDatabase.instance.ref()
.child("passengers")
.child(chosenPassengerId!)
.child("newRideStatus")
.onValue.listen((eventSnapshot)
{
//accept the ride request push notification
//(newRideStatus = accepted)
if(eventSnapshot.snapshot.value == "accepted")
{
//design and display ui for displaying driver information
rideRequest(context);
print("accepted");
}
});
}
else
{
Fluttertoast.showToast(msg: "This passenger do not exist. Try again.");
}
});
});
// Navigator.pop(context, "passengerChoosed");
},
child: Card(
color: Colors.white54,
elevation: 3,
shadowColor: Colors.green,
margin: EdgeInsets.all(8.0),
child: ListTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 6.0),
child: Text(
pList[index]["username"],
style: const TextStyle(
fontFamily: 'Montserrat',
fontSize: 16,
color: Color(0xFF272727),
fontWeight: FontWeight.bold,
),
),
),
Icon(
Icons.verified_rounded,
color: Color(0xFF0CBC8B),
),
],
),
SizedBox(height: 1.h,),
//icon + pickup
Row(
children: [
Image.asset(
"assets/images/origin.png",
height: 26,
width: 26,
),
const SizedBox(width: 12,),
Expanded(
child: Container(
child: Text(
pList[index]["originAddress"],
style: const TextStyle(
fontFamily: 'Montserrat',
fontSize: 14,
color: Color(0xFF272727),
),
),
),
),
],
),
SizedBox(height: 1.h,),
Row(
children: [
Image.asset(
"assets/images/destination.png",
height: 26,
width: 26,
),
const SizedBox(width: 12,),
Expanded(
child: Container(
child: Text(
pList[index]["destinationAddress"],
style: const TextStyle(
fontFamily: 'Montserrat',
fontSize: 14,
color: Color(0xFF272727),
),
),
),
),
],
),
],
),
),
),
);
},
),
),
);
}
rideRequest(BuildContext context)
{
String getRideRequestId="";
FirebaseDatabase.instance.ref()
.child("drivers")
.child(currentFirebaseUser.uid)
.child("newRideStatus")
.set(widget.userRideRequestDetails!.rideRequestId);
FirebaseDatabase.instance.ref()
.child("drivers")
.child(currentFirebaseUser.uid)
.child("newRideStatus")
.once()
.then((snap)
{
if(snap.snapshot.value != null)
{
getRideRequestId = snap.snapshot.value.toString();
}
else
{
Fluttertoast.showToast(msg: "This ride request do not exists.");
}
if(getRideRequestId == widget.userRideRequestDetails!.rideRequestId)
{
FirebaseDatabase.instance.ref()
.child("drivers")
.child(currentFirebaseUser.uid)
.child("newRideStatus")
.set("accepted");
Fluttertoast.showToast(msg: "Accepted Successfully.");
//trip started now - send driver to new tripScreen
Navigator.push(context, MaterialPageRoute(builder: (c)=> NewTripScreen(
userRideRequestDetails: widget.userRideRequestDetails,
)));
}
else
{
Fluttertoast.showToast(msg: "This Ride Request do not exists.");
}
});
}
}

type 'bool' is not a subtype of type 'List<ternakModel> in a type cast

I have problem in the emulator android. type 'bool' is not a subtype of type 'List in a type cast. I can't solve of this. I use provider ListBuilder. so I'm retrieving data using the provider in the form of a list. Can you help?
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
TernakProvider ternakProvider = Provider.of<TernakProvider>(context);
return Consumer<TernakProvider>(
builder: (context, providerData, _) => FutureBuilder(
future: providerData.getTernak(),
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Text("Loading...");
}
List<ternakModel> ternak = snapshot.data as List<ternakModel>;
and this is file provider
class TernakProvider with ChangeNotifier {
List<ternakModel> _ternak = [];
List<ternakModel> get ternak => _ternak;
set ternak(List<ternakModel> ternak) {
_ternak = ternak;
notifyListeners();
}
Future<bool> getTernak() async {
try {
List<ternakModel> ternak = await TernakService().getTernak();
_ternak = ternak;
return true;
} catch (e) {
print(e);
return false;
}
}
}
and this is my service file
class TernakService {
String baseUrl = "BaseURL";
Future getTernak() async {
var url = Uri.parse("$baseUrl/api/ternak");
var response = await http.get(url);
print(response.body);
if (response.statusCode == 200) {
List data = jsonDecode(response.body)['data']['list'];
List<ternakModel> ternak = [];
for (var item in data) {
ternak.add(ternakModel.fromJson(item));
}
return ternak;
} else {
throw Exception('Gagal Get Ternak');
}
}
}
this is listview code
body: ListView.builder(
itemCount: ternak.length,
itemBuilder: (context, index) {
return Center(
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const DetailTernak(),
settings: RouteSettings(
arguments:
ternak[index].id_ternak.toString(),
),
),
);
},
// width: MediaQuery.of(context).size.width / 0.5,
// margin: const EdgeInsets.only(left: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: MediaQuery.of(context)
.size
.width /
1.2,
margin: EdgeInsets.only(
left: 16,
right: 16,
top: 10,
bottom: 10),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(10),
border: Border.all(
color: const Color(0xffE5E5E5),
width: 1,
),
boxShadow: [
BoxShadow(
blurStyle: BlurStyle.outer,
color: Colors.black
.withOpacity(0.1),
spreadRadius: 0,
blurRadius: 8,
offset: const Offset(0,
0), // changes position of shadow
),
],
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: <Widget>[
Container(
margin: EdgeInsets.only(
left: 15, top: 15),
child: Row(
children: [
Text(
"ID ",
style: regular
.copyWith(
color: Color(
0xFF646464),
),
),
Text(
"${ternak[index].id_ternak}",
style: semibold,
)
],
),
),
]),
Change your getTernak inside TernakProvider to this:
Future<List<ternakModel>> getTernak() async {
try {
List<ternakModel> ternak = await TernakService().getTernak();
_ternak = ternak;
return ternak;
} catch (e) {
print(e);
return [];
}
}

Update State without reloading a widget in Flutter

I have a widget on a screen that receives its data from API calls. The API call is made inside the init method of the Navigation Bar so that continuous API calls can be prevented when going back and forth between screens. Although this works fine, I'm facing a real challenge in trying to get the state of the widget updated when new data is added to that particular API that the widget relies on for displaying data. I would therefore need to know how to display the updated data that I added to the Database by making a post request on a different screen. The only way this happens now is by way of reloading the entire app or by killing it. Any help will be appreciated.
This is the NavBar where the API is getting called. I usually make all the API calls at once here and something I have done here too.
NavBar
class CustomBottomNavigationState extends State<CustomBottomNavigation> {
bool isLoading = true;
int index = 2;
final screens = [
MenuScreen(),
LeaveScreen(),
// TaskList(),
HomeScreen(),
// PaySlipScreen(),
TaskList(),
Claimz_category(),
// ClaimzScreen()
];
#override
void initState() {
// TODO: implement initState
Provider.of<LeaveRequestViewModel>(context, listen: false)
.getLeaveRequest()
.then((value) {
Provider.of<AnnouncementViewModel>(context, listen: false)
.getAllAnouncements()
.then((value) {
Provider.of<TodaysTaskList>(context, listen: false)
.getTodaysTasks() //This is the API call in question
.then((value) {
setState(() {
isLoading = false;
});
});
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
final items = ['The icons are stored here'];
// TODO: implement build
return SafeArea(
child: Scaffold(
body: isLoading
? const Center(
child: CircularProgressIndicator(),
)
: screens[index],
extendBody: true,
bottomNavigationBar: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(200),
topRight: Radius.circular(200)),
boxShadow: [
BoxShadow(
color: Colors.transparent,
blurRadius: 10,
offset: Offset(1, 2))
]),
child: CurvedNavigationBar(
items: items,
index: index,
height: 60,
color: const Color.fromARGB(255, 70, 70, 70),
backgroundColor: Colors.transparent,
onTap: (index) => setState(() {
this.index = index;
})),
),
),
);
}
}
ToDoList widget(This the widget where the updates never reflect without reloading)
class ToDoListState extends State<ToDoList> {
#override
Widget build(BuildContext context) {
final toDoList = Provider.of<TodaysTaskList>(context).getToDoList; //This is the getter method that stores the data after it has been fetched from API
// TODO: implement build
return ContainerStyle(
height: SizeVariables.getHeight(context) * 0.35,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(
top: SizeVariables.getHeight(context) * 0.015,
left: SizeVariables.getWidth(context) * 0.04),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
// color: Colors.red,
child: FittedBox(
fit: BoxFit.contain,
child: Text(
'To do list',
style: Theme.of(context).textTheme.caption,
),
),
),
],
),
),
SizedBox(height: SizeVariables.getHeight(context) * 0.01),
Padding(
padding: EdgeInsets.only(
left: SizeVariables.getWidth(context) * 0.04,
top: SizeVariables.getHeight(context) * 0.005,
right: SizeVariables.getWidth(context) * 0.04),
child: SizedBox(
height: SizeVariables.getHeight(context) * 0.25,
child: Container(
// color: Colors.red,
child: toDoList['today'].isEmpty
? Center(
child: Lottie.asset('assets/json/ToDo.json'),
)
: ListView.separated(
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => Row(
children: [
Icon(Icons.circle,
color: Colors.white,
size:
SizeVariables.getWidth(context) * 0.03),
SizedBox(
width:
SizeVariables.getWidth(context) * 0.02),
FittedBox(
fit: BoxFit.contain,
child: Text(
toDoList['today'][index]['task_name'], //This is where it is used
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.bodyText1),
)
],
),
separatorBuilder: (context, index) => Divider(
height: SizeVariables.getHeight(context) * 0.045,
color: Colors.white,
thickness: 0.5,
),
itemCount: toDoList['today'].length > 4
? 4
: toDoList['today'].length),
),
),
)
],
),
);
}
}
The other widget where the date gets added
class _TaskListState extends State<TaskList> {
#override
Widget build(BuildContext context) {
var floatingActionButton;
return Scaffold(
backgroundColor: Colors.black,
floatingActionButton: Container(
....
....,
child: FloatingActionButton(
backgroundColor: Color.fromARGB(255, 70, 69, 69),
onPressed: openDialog, //This is the method for posting data
child: Icon(Icons.add),
),
),
),
body: Container(
....
....
....
),
);
}
Future<dynamic> openDialog() => showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Color.fromARGB(255, 87, 83, 83),
content: Form(
key: _key,
child: TextFormField(
controller: taskController,
maxLines: 5,
style: Theme.of(context).textTheme.bodyText1,
decoration: InputDecoration(
border: InputBorder.none,
),
validator: (value) {
if (value!.isEmpty || value == '') {
return 'Please Enter Task';
} else {
input = value;
}
},
),
),
actions: [
InkWell(
onTap: () async {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2010),
lastDate:
DateTime.now().add(const Duration(days: 365)))
.then((date) {
setState(() {
_dateTime = date;
});
print('Date Time: ${dateFormat.format(_dateTime!)}');
});
},
child: const Icon(Icons.calendar_month, color: Colors.white)),
TextButton(
child: Text(
"Add",
style: Theme.of(context).textTheme.bodyText1,
),
onPressed: () async {
Map<String, dynamic> _data = {
'task': taskController.text,
'task_date': dateFormat.format(_dateTime!).toString()
};
print(_data);
if (_key.currentState!.validate()) {
await Provider.of<ToDoViewModel>(context, listen: false)
.addToDo(_data, context) //This is the post method
.then((_) {
Navigator.of(context).pop();
Provider.of<TodaysTaskList>(context, listen: false)
.getTodaysTasks(); //I did this here again to re-initialize the data. I was under the impression that the new data would get initialized for the widget to reflect it on the other screen.
});
}
},
),
],
),
);
void add() {
Navigator.of(context).pop();
}
}
The Get API Call
class TodaysTaskList with ChangeNotifier {
Map<String, dynamic> _getToDoList = {};
Map<String, dynamic> get getToDoList {
return {..._getToDoList};
}
Future<void> getTodaysTasks() async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
var response = await http.get(Uri.parse(AppUrl.toDoList), headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${localStorage.getString('token')}'
});
if (response.statusCode == 200) {
_getToDoList = json.decode(response.body);
} else {
_getToDoList = {};
}
print('TO DO LIST: $_getToDoList');
notifyListeners();
}
}
Please let me know for additional input.
i think it's because you didn't call the provider to update your state correctly
as i see that you declare new variable to store your provider like this
final toDoList = Provider.of<TodaysTaskList>(context).getToDoList;
then you use it like this
Text(
toDoList['today'][index]['task_name'], //This is where it is used
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.bodyText1),
)
it's not updating the state, you should wrap the widget that need to be updated with Consumer
Consumer<TodaysTaskList>(
builder: (context, data, child) {
return _Text(
data.[your_list]['today'][index]['task_name'],
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyText1),
);
},
);

Keyboard trigger the api call while closing and opening in flutter?

Keyboard triggers the api calling while closing and opening
I have search bar I didn't pass anything inside the search bar but when I click the search bar The api call will run.
I don't know why that thing was happen I tried several ways but It won't work
Even Inside the keyboard I choose the one hand keyboard that time also api will run
Also tried the Focusnode also.
class PopupNaviagator extends StatefulWidget {
var body;
String leadsName = "";
Map<String, dynamic> listValues;
PopupNaviagator(this.listValues);
var data;
String pageData = "";
double totalPageCount = 0;
int pageIncrementer = 1;
#override
_PopupNavigatorState createState() => _PopupNavigatorState();
}
class _PopupNavigatorState extends State<PopupNaviagator> {
Utils util = new Utils();
bool isSearching = false;
#override
void initState() {
super.initState();
}
String searchingText = "";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
titleSpacing: 0,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: util.getColors(1001),
stops: [0.1, 5.0],
),
),
),
title: !isSearching
? Text("Select Search Type")
: TextField(
autofocus: true,
autocorrect: true,
onChanged: (value) {
searchingText = value;
},
cursorColor: Colors.white,
onSubmitted: (value) {
searchingText = value;
campaignAPI(
widget.listValues["colname"].toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
},
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
icon: Icon(Icons.search),
hintText: "Search ",
hintStyle: TextStyle(color: Colors.white)),
),
actions: <Widget>[
isSearching
? IconButton(
icon: Icon(Icons.cancel),
onPressed: () {
setState(() {
searchingText = "";
this.isSearching = false;
campaignAPI(
widget.listValues["colname"].toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
/* filteredCountries = countries;*/
});
},
)
: IconButton(
icon: Icon(Icons.search),
onPressed: () {
setState(() {
this.isSearching = true;
});
},
)
],
),
body: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(5))),
child: Scaffold(
body: FutureBuilder(
future: campaignAPI(widget.listValues["colname"].toString(), "1",
searchingText, 0),
builder: (context, snapshot) {
if (snapshot.hasData) {
var body;
if (widget.data == null) {
body = snapshot.data;
} else {
body = widget.data;
}
final records = body["list_records"];
widget.pageData = body["PageValues"];
widget.totalPageCount =
(body["TotalRecordCount"] / 5).toDouble();
return Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
if (records.length != 0) ...[
Expanded(
child: ListView.builder(
padding: EdgeInsets.only(
top: 0,
bottom: 0,
),
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: records.length,
itemBuilder: (context, index) {
Map<String, dynamic> pickerValues =
records[index];
String titleName =
pickerValues["viewcol_value"]
.toString();
return Container(
margin: const EdgeInsets.all(5.00),
decoration: new BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.grey, width: 1.0),
borderRadius: new BorderRadius.all(
Radius.circular(3.0)),
),
child: FlatButton(
padding: EdgeInsets.all(8.0),
onPressed: () {
setState(() {
List<String> strArr = [
titleName,
pickerValues["rec_id"].toString(),
widget.listValues["colname"]
];
Navigator.pop(context, strArr);
});
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(titleName.toString()),
Icon(
Icons.check_circle_outlined,
color: Colors.grey,
),
]),
),
);
}),
),
] else ...[
Container(
height: MediaQuery.of(context).size.height / 1.4,
child: Center(
child: Container(
padding: EdgeInsets.all(65),
child: Text(
"No Record Found",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.black),
),
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey,
width: 2,
),
)),
),
),
],
if (records.length != 0) ...[
Container(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
if (widget.pageIncrementer > 1) {
widget.pageIncrementer--;
campaignAPI(
widget.listValues["colname"]
.toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
setState(() {});
} else {
util.showToast("No records to show");
}
},
child: Icon(Icons.arrow_left_rounded,
size: 50, color: Colors.white),
),
Text(
widget.pageData,
style: TextStyle(
fontSize: 15,
color: Colors.white,
fontWeight: FontWeight.bold),
),
InkWell(
onTap: () {
if (widget.totalPageCount > 1 &&
widget.totalPageCount >=
widget.pageIncrementer) {
widget.pageIncrementer++;
campaignAPI(
widget.listValues["colname"]
.toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
} else {
util.showToast("No reocrds to show");
}
},
child: Icon(Icons.arrow_right_rounded,
size: 50, color: Colors.white),
),
],
),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: util.getColors(1001),
stops: [0.1, 5.0],
),
),
)
]
],
),
),
);
} else if (snapshot.hasError) {
return Text('${snapshot.error.toString()}');
}
return Center(
child: CircularProgressIndicator(),
);
},
),
),
));
}
campaignAPI(
String field_col, String page_no, String searchValue, int flag) async {
Utils util = new Utils();
SharedPreferences prefs = await SharedPreferences.getInstance();
final accessKey = prefs.getString('access_key');
final companyName = prefs.getString('company_name');
Object requestParam = {
"companyname": companyName,
"access_key": accessKey.toString(),
"field_col": field_col,
"page_no": page_no,
"searchValue": searchValue,
};
print(requestParam.toString());
APIS apis = new APIS();
Uri url = Uri.parse(apis.searchquickcreate);
util.prints(url);
util.prints(requestParam);
final response = await http.post(url, body: requestParam);
if (response.statusCode == 200) {
final body = json.decode(response.body);
print(body);
String status = body["status"];
if (status != "failed") {
if (flag == 1) {
setState(() {
widget.data = body;
});
}
return body;
} else {
util.logOut(context);
return body;
}
} else {
Navigator.pop(context);
util.showToast("Server Error !");
throw Exception("Server Error !");
}
}
}

Flutter FutureBuilder calling function continuously

I have simple function which is calling data from firestore and filtering data. But issue is my futurebuilder keeps on loader situation (Data is called successfully i can see in console but now showing in future) I think its because my fucntion is calling in loop or something i have try to print something in my function which indicates me that my function is not stopping and thats why i think my futureBuilder keeps on loading.
My code
Future<List> getCustomerList() async {
print('calling');
String uUid1 = await storage.read(key: "uUid");
String uName1 = await storage.read(key: "uName");
String uNumber1 = await storage.read(key: "uNumber");
setState(() {
uUid = uUid1;
uName = uName1;
uNumber = uNumber1;
});
CollectionReference _collectionRef =
FirebaseFirestore.instance.collection('Customers');
QuerySnapshot querySnapshot = await _collectionRef.get();
// Get data from docs and convert map to List
List allData = querySnapshot.docs
.where((element) => element['sellerUID'] == uUid)
.map((doc) => doc.data())
.toList();
double gGive = 0;
double gTake = 0;
double gCal = 0;
for (int i = 0; i < allData.length; i++) {
// print(allData[i]);
// print('give ${double.parse(allData[i]['give'].toString()) }');
// print('take ${double.parse(allData[i]['take'].toString()) }');
double.parse(allData[i]['give'].toString()) -
double.parse(allData[i]['take'].toString()) >
0
? gGive += double.parse(allData[i]['give'].toString()) -
double.parse(allData[i]['take'].toString())
: gTake += double.parse(allData[i]['give'].toString()) -
double.parse(allData[i]['take'].toString());
}
// print(gGive);
// print(gTake);
setState(() {
Gtake = gGive.toString().replaceAll("-", "");
Ggive = gTake.toString().replaceAll("-", "");
});
if (greenBox) {
var check = allData.where((i) => i['take'] > i['give']).toList();
return check;
} else if (redBox) {
var check = allData.where((i) => i['give'] > 1).toList();
return check;
} else {
return allData;
}
}
And my futureBuilder look like this
Expanded(
child: Container(
height: Height * 0.5,
child: FutureBuilder(
future: getCustomerList(),
builder: (context, snapshot) {
if (snapshot.hasData) {
list = snapshot.data;
return SingleChildScrollView(
child: Column(
children: [
Container(
height: Height * 0.5,
child: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
itemBuilder:
(BuildContext context,
int index) {
var showThis = list[index]
['give'] -
list[index]['take'];
return list[index]
['customerName']
.toString()
.contains(searchString)
? GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
CustomerData(
data: list[
index])),
);
},
child: Padding(
padding:
const EdgeInsets
.only(
left: 13,
right: 13),
child: Container(
decoration:
BoxDecoration(
border: Border(
top: BorderSide(
color: Colors
.grey,
width:
.5)),
),
child: Padding(
padding:
const EdgeInsets
.all(
13.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Row(
children: [
CircleAvatar(
child:
Text(
list[index]['customerName'][0]
.toString(),
style:
TextStyle(fontFamily: 'PoppinsBold'),
),
backgroundColor:
Color(0xffF7F9F9),
),
SizedBox(
width:
20,
),
Text(
list[index]['customerName']
.toString(),
style: TextStyle(
fontFamily:
'PoppinsMedium'),
),
],
),
Text(
'RS ${showThis.toString().replaceAll("-", "")}',
style: TextStyle(
fontFamily:
'PoppinsMedium',
color: list[index]['give'] - list[index]['take'] <
0
? Colors.green
: Colors.red),
),
],
),
),
),
),
)
: Container();
},
),
)
],
),
);
} else
return Center(
heightFactor: 1,
widthFactor: 1,
child: SizedBox(
height: 70,
width: 70,
child: CircularProgressIndicator(
strokeWidth: 2.5,
),
),
);
}),
),
),
I am damn sure its because futurebuilder keeps calling function which is returning data but because of keeps calling functions my Futurebuilder keeps showing loading.
You should not call setState inside the future that you are giving to the FutureBuilder.
The state actualization will cause the FutureBuilder to re-build. Meaning triggering the future again, and ... infinite loop !