How to display in dialog variable from API request in Flutter - flutter

I want to access the variable totalPresences that I have in my API request where I sum up the values from a map. Then I want to display the variable in my widget inside a dialog. How can I do that? Thanks in advance!
Here is my code
Future<List<Presence>> getPresencesByAthleteId() async {
try {
final response = await http.get(
Uri.parse();
if (response.statusCode == 200) {
Map map = json.decode(response.body);
List<Presence>? presencesList = [];
map.forEach((key, value) {
presencesList.add(Presence(
date: map.entries.first.key, count: map.entries.first.value));
var values = map.values;
var totalPresences = values.reduce((sum, element) => sum + element); //this I want to display it in a text
});
return presencesList.toList();
}
} catch (e) {
logger.e(e.toString());
}
return getPresencesByAthleteId(depId, teamId, id, context);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<Athlete>>(
...
secondary: IconButton(
icon: const Icon(Icons.history_outlined,
color: Colors.black, size: 25),
onPressed: () {
if (_athlete[i].currentMonthPresences! > 0) {
showDialog(
context: context,
builder: (BuildContext context) {
return SimpleDialog(
children: [
Column(
FutureBuilder<List<Presence>>(
future: getPresencesByAthleteId(_athlete[i].department!.id, widget._team.teamKey!.teamId, _athlete[i].id, context),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
...
}),
);
} else if (snapshot.hasError) {
logger.e('${snapshot.error}');
}
}),
Container(
child:
Row(
children: [
const Text(''), // HERE I WANT TO DISPLAY totalPresences
)
],
),
),

It was easier than I thought I just needed a setState inside my api request like this:
int total=0;
Future<List<Presence>> getPresencesByAthleteId() async {
try {
final response = await http.get(
Uri.parse();
if (response.statusCode == 200) {
Map map = json.decode(response.body);
List<Presence>? presencesList = [];
map.forEach((key, value) {
presencesList.add(Presence(
date: map.entries.first.key, count: map.entries.first.value));
var values = map.values;
var totalPresences = values.reduce((sum, element) => sum + element);
setState(() {
totalPresences = total;
});
});
return presencesList.toList();
}
} catch (e) {
logger.e(e.toString());
}
return getPresencesByAthleteId(depId, teamId, id, context);
}
and then just display in dialog
.
.
const Text($total),

Related

how to remove duplicate from list of objects in flutter?

I'm new to flutter. I'm working on small projects which is like a scanning QR app. Here, I used hive to store scanned data in box but the problem is, it is storing duplicate values.
I need to remove that duplicate data how to do it?
code:
class PageState extends State<PassthroughQrScanPage> {
final ApiRepository repository = ApiRepository(
apiClient: ApiClient(
httpClient: http.Client(),
),
);
#override
void initState() {
super.initState();
openBox();
Prefs().reload();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => PassthroughqrscanBloc(repository),
child: BlocConsumer<PassthroughqrscanBloc, PassthroughqrscanState>(
listener: (context, state) {
if (state is PassthroughqrscanEmpty) {
return scan(context);
}
if (state is PassthroughqrscanError) {
Navigator.pop(context);
ShowErrorMessage(context, state.error.message.toString());
}
if (state is PassthroughqrscanLoaded) {
List<Batch> cleared = [];
state.entity.batches.forEach((element) {
cleared.add(element);
});
// final clearedData =
// cleared.map((item) => jsonEncode(item)).toList();
// final uniqueJsonList = clearedData.toSet().toList();
// List result =
// uniqueJsonList.map((item) => jsonDecode(item)).toList();
var seen = Set<int>();
List<Batch> clearedData = cleared
.where((cleared) => seen.add(cleared.batchNumber!))
.toList();
// clearedData = [
// ...{...clearedData}
// ];
clearedData.forEach((element) {
debugPrint(
"check the values for all the sdasd ${element.batchNumber}");
box.add(Batch(
batchNumber: element.batchNumber,
isUsed: element.isUsed,
transactionId: element.transactionId));
});
print("adding ssssss ${box.values.toList()}");
// String json = jsonEncode(state.entity);
// print("------>>>>>>>>>>>D>S>D>>$json");
// Prefs().setPassthroughData(json);
Navigator.pop(context);
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
context: context,
builder: (ctxDialog) => PassDialog(
compoundCode: widget.compoundCode.toString(),
lotNo: widget.lotNo.toString(),
schedule_id: widget.schedule_id.toString(),
screenClosed: _screenWasClosed,
scheduleRange: widget.scheduleRange,
batchQty: widget.batchQty,
));
});
}
// return Container();
Center(
child: CircularProgressIndicator(),
);
},
builder: (context, state) {
if (state is PassthroughqrscanEmpty) {
return scan(context);
} else
return scan(context);
},
),
),
);
}
scan(BuildContext mcontext) =>
MobileScanner(
controller: MobileScannerController(facing: CameraFacing.back),
allowDuplicates: false,
onDetect: (barcode, args) {
if (barcode.rawValue == null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("Scan correct QR"),
duration: Duration(milliseconds: 800)));
});
} else {
String code = barcode.rawValue ?? "";
debugPrint('Barcode found! $code');
if (code.isNotEmpty) {
// if (!_screenOpened) {
// _screenOpened = true;
passthroughData = jsonDecode(code);
passthroughQrScan =
PassthroughQrScanData.fromJson(passthroughData);
BlocProvider.of<PassthroughqrscanBloc>(mcontext)
..add(VerifyPassthroughBatch(
passthroughQrScan?.operationName ?? "",
widget.schedule_id.toString(),
passthroughQrScan?.transactionId ?? "",
passthroughQrScan?.transactionRange ?? ""));
buildShowDialog(context);
}
}
});
Widget ShowErrorMessage(BuildContext context, String error) {
print("------------------------------/./././$error");
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("Scan correct QR"),
duration: Duration(milliseconds: 800)));
});
return scan(context);
}
Future<void> openBox() async {
box = await Hive.openBox<Batch>("GetBatches");
// box = Boxes.getAllBatches();
await box.clear();
debugPrint("wwwwwwwwwwwwwwwwwkekekkkkkkkkk${box.values}");
// await box.deleteAll(box.keys);
}
}
List<Batch> batched = [];
var data;
class PassDialog extends StatefulWidget {
// const PassDialog({Key? key}) : super(key: key);
String? schedule_id;
String? compoundCode;
String? lotNo;
final Function() screenClosed;
final String? scheduleRange;
final int? batchQty;
PassDialog(
{required this.schedule_id,
required this.compoundCode,
required this.lotNo,
required this.screenClosed,
required this.scheduleRange,
required this.batchQty});
#override
State<PassDialog> createState() => _PassDialogState();
}
class _PassDialogState extends State<PassDialog> {
#override
void initState() {
batched = box.values.toSet().toList();
print("got values check for $batched");
super.initState();
}
#override
Widget build(BuildContext context) {
// List<Batch> batch = box.get("GetBatches");
//Batch batch = box?.get("GetBatches");
return SizedBox(
width: 150,
height: 100,
child: AlertDialog(
content: SingleChildScrollView(
child: Column(
children: [
// batched.forEach((element) {
for (final q in batched)
Text(
'${q.batchNumber.toString()}--${q.isUsed.toString()}--${q.transactionId.toString()}'),
// }),
Row(
children: [
ElevatedButton(
onPressed: () {
// print(values);
widget.screenClosed();
Navigator.of(
context,
rootNavigator: true,
).pop(
context,
);
},
child: Text("Continue")),
SizedBox(
width: 10,
),
ElevatedButton(
onPressed: () {
print(widget.scheduleRange);
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.push(
context,
new MaterialPageRoute(
builder: (_) => GluePassthroughUploadPage(
id: widget.schedule_id.toString(),
compoundCode:
widget.compoundCode.toString(),
lotNo: widget.lotNo.toString(),
scheduleRange: widget.scheduleRange,
batchQty: widget.batchQty,
// getAllBatch: getBatch,
)));
});
},
child: Text("Show Add page")),
],
),
],
),
),
// Text("hohohoooooo${batched[0].batchNumber}${batched[0].isUsed}"),
),
);
}
}
Future buildShowDialog(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return Center(
child: CircularProgressIndicator(),
);
});
}
How to remove duplicate list of objects? I'm using Batch as model class. I tried many methods to solve this issue. toSet(), like that.......
For your Batch Model, you can filter with a function like this:
List<Batch> removeDuplicates(List<Batch> items) {
List<Batch> uniqueItems = []; // uniqueList
var uniqueIDs = items
.map((e) => e.uniqueID)
.toSet(); //list if UniqueID to remove duplicates
uniqueIDs.forEach((e) {
uniqueItems.add(items.firstWhere((i) => i.uniqueID == e));
}); // populate uniqueItems with equivalent original Batch items
return uniqueItems;//send back the unique items list
}
Replace uniqueID with the parameter you want to use for filtering duplicates
If you want dealing with more complex objects, store seen ids to the Set and filter away those ones that are already in the set.
final list = ['a', 'a', 'b', 'c', 'c'];
final seen = <String>{};
final uniqueList = list.where((str) => seen.add(str)).toList();
print(uniqueList); // => ['a', 'b', 'c']
With the help of this, You can easily get Unique data from list and it will remove duplicate value
uniqueList = uniqueList.toSet().toList();

How to use setState in FutureBuilder?

I have a function that accepts data via API:
//fetch data from API
Future<List<CurrencyModel>?> _fetchCurrency() async {
currencyList = [];
final response = await http.get(
Uri.parse(
'https:...'),
);
if (response.statusCode == 200) {
List<dynamic> values = [];
values = json.decode(response.body);
if (values.isNotEmpty) {
for (int i = 0; i < values.length; i++) {
if (values[i] != null) {
Map<String, dynamic> map = values[i];
currencyList.add(
CurrencyModel.fromJson(map),
);
}
}
setState(() {
currencyList;
});
}
return currencyList;
} else {
throw Exception('Failed to load currencies');
}
}
I moved the logic of working with the API into a separate file, created a regular class with the Future function. How now to be with setState which was in Future?
Because setState cannot be added to a regular class.
How to add it to FutureBuilder?
My code:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Currencies'),
centerTitle: true,
),
body: FutureBuilder(
future: client.fetchCurrency(),
builder: (BuildContext context,
AsyncSnapshot<List<CurrencyModel>?> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: currencyList.length,
itemBuilder: (context, index) => CurrencyCard(
currencyList[index],
),
);
} else if (snapshot.hasError) {
return Text(
'${snapshot.error}',
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: startTimer,
child: const Icon(Icons.update_sharp),
),
);
}
It is easy:
1 Declare a function to process your list
List<CurrencyModel>? _processOnEnd(List<CurrencyModel>? value){
//write what you need inside this function
return value;
}
2 Call it inside then method of your Future
FutureBuilder(
future: client.fetchCurrency().then(_processOnEnd),

Flutter cannot increment inside listtile, always returning 0

I want to increment number on trailing ListTile Flutter when ontap ListTile,
but always return to 0?
i'm using future builder fyi,
thanks in advance
this my model
class ItemModel {
String name;
String price;
String image;
bool isSelected = false;
int countSelected = 0;
ItemModel(
this.name,
this.price,
this.image,
);
}
This method to get data from api json from local server
Future<List<ItemModel>> _getItems() async {
List<ItemModel> listItems = [];
var url = Uri.parse(BASEURLLOCAL.apiGetItems);
var data = await http.get(url);
var jsonData = jsonDecode(data.body);
for (var p in jsonData) {
ItemModel item = ItemModel(
p["name"],
p["price"],
p["image"],
// p["PRODUCT_NAME"],
// p["PRICE_SELL"],
// p["FILENAME"],
);
listItems.add(item);
}
return listItems;
}
and this the listview builder
#override
Widget build(BuildContext context) {
return FutureBuilder<List<ItemModel>>(
future: _getItems(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext contex, int idx) {
if (_itemCount.length < snapshot.data.length) {
_itemCount.add(0);
}
return Card(
child: ListTile(
leading: Image.network(
BASEURLLOCAL.imgItem + snapshot.data[idx].image),
title: Text(snapshot.data[idx].name),
subtitle: Text(snapshot.data[idx].price),
// trailing: _buildTrailing(),
trailing: Text(snapshot.data[idx].countSelected.toString()),
onTap: () {
setState(() {
snapshot.data[idx].countSelected++;
});
},
),
);
},
);
}
},
);
}
this is what the problem is
Try something like this, create a list
List<ItemModel> listItems = [];
then, in initState() call the _getItems()
_getItems() async {
var url = Uri.parse(BASEURLLOCAL.apiGetItems);
var data = await http.get(url);
var jsonData = jsonDecode(data.body);
for (var p in jsonData) {
ItemModel item = ItemModel(
p["name"],
p["price"],
p["image"],
// p["PRODUCT_NAME"],
// p["PRICE_SELL"],
// p["FILENAME"],
);
listItems.add(item);
}
}
And remove FutureBuilder
#override
Widget build(BuildContext context) {
return ListView.builder(
shrinkWrap: true,
itemCount: listItems.length,
itemBuilder: (BuildContext context, int idx) {
if (_itemCount.length < listItems.length) {
_itemCount.add(0);
}
return Card(
child: ListTile(
leading: Image.network(BASEURLLOCAL.imgItem + listItems[idx].image),
title: Text(listItems[idx].name),
subtitle: Text(listItems[idx].price),
// trailing: _buildTrailing(),
trailing: Text(listItems[idx].countSelected.toString()),
onTap: () {
setState(() {
listItems[idx].countSelected++;
});
},
),
);
},
);
}
By this way it won't fetch everytime when it rebuilds.

flutter pull up to refetch data from api

I want to use Refresh indicator so that when you pull up the page you are in right now rebuilds i will share with you my code i have tried many times but really i can't find a straight way around it here is my code
class Companies {
final int id;
final String name;
final String companyLogo;
Companies({this.id, this.name, this.companyLogo});
factory Companies.fromJson(Map<String, dynamic> json) {
return Companies(
id: json['id'],
name: json['name'],
companyLogo: json['company_logo'],
);
}
}
Future<List<Companies>> fetchCompanies() async {
final response = await http.get('$webSiteUrl/company/api/fetch');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return parseCompanies(response.body);
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load the companies');
}
}
List<Companies> parseCompanies(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Companies>((json) => Companies.fromJson(json)).toList();
}
class CompaniesPage extends StatefulWidget{
#override
_CompaniesState createState() => _CompaniesState();
}
class _CompaniesState extends State<CompaniesPage> {
var refreshKey = GlobalKey<RefreshIndicatorState>();
Future<List<Companies>> companies;
#override
void initState() {
super.initState();
companies = fetchCompanies();
}
Future<Null> refreshCompanies() async {
refreshKey.currentState?.show(atTop: false);
setState(() {
companies = fetchCompanies();
});
return await companies;
}
Widget build(BuildContext context) {
checkVersion(context);
return Scaffold(
body: Center(
child: FutureBuilder<List<Companies>>(
future: companies,
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Companies> companies = snapshot.data;
if(companies.length >= 1){
return MainLayout(
RefreshIndicator(
key: refreshKey,
onRefresh: refreshCompanies,
child: GridView.count(
crossAxisCount: 2 ,
children: List.generate(companies.length, (index) {
return GestureDetector(
onTap: () => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Categories(companies[index].id, companies[index].name)),
)},
child: CompaniesInterface(companies[index].id , companies[index].name , companies[index].companyLogo),
);
}),
),
),
);
}else{
return EmptyDataBase();
}
} else if (snapshot.hasError) {
return ConnectionError();
}
// By default, show a loading spinner.
return DefaultTabController(
length: 1,
child: TabBar(
indicatorColor: Colors.transparent,
tabs: <Widget>[
Tab(
child: LoadingBouncingGrid.square(
backgroundColor: Colors.cyan,
size: 40,
),
),
],
),
);
},
),
),
);
}
}
as you can see i have tested it but it isn't refreshing the page correctly what i want is how should i rebuild this page on pull up so the missing part from my code i think is refreshCompanies() function
Update :
class _CompaniesState extends State<CompaniesPage> {
StreamController<List<Companies>> companiesStreamController;
var refreshKey = GlobalKey<RefreshIndicatorState>();
Future<List<Companies>> fetchCompanies() async {
final response = await http.get('$webSiteUrl/company/api/fetch');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return parseCompanies(response.body);
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load the companies');
}
}
loadCompanies() async {
fetchCompanies().then((result) async {
companiesStreamController.add(result);
return result;
});
}
Future<Null> refreshCompanies() async {
refreshKey.currentState.show(atTop: true);
setState(() {
loadCompanies();
});
}
#override
void initState() {
checkVersion(context);
companiesStreamController = new StreamController();
Timer.periodic(Duration(seconds: 1), (_) => loadCompanies());
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: StreamBuilder<List<Companies>>(
stream: companiesStreamController.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Companies> companies = snapshot.data;
if(companies.length >= 1){
return MainLayout(
RefreshIndicator(
onRefresh: refreshCompanies,
key: refreshKey,
child: GridView.count(
crossAxisCount: 2 ,
children: List.generate(companies.length, (index) {
return GestureDetector(
onTap: () => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Categories(companies[index].id, companies[index].name)),
)},
child: CompaniesInterface(companies[index].id , companies[index].name , companies[index].companyLogo),
);
}),
),
),
);
}else{......rest of code
Add a StreamController:
StreamController<List<Companies>> dataController;
Initialize it in your initState:
dataController = StreamController();
Move fetchCompanies inside your widget and before returning the result add it to your stream:
var result = parseCompanies(response.body);
dataController.add(result);
Use a StreamBuilder instead of FutureBuilder:
StreamBuilder<List<Companies>>(
stream: dataController.stream,
builder: (context, snapshot) {
...
}
)

How to get async value in Flutter

I have this function that retrieves data from firestore and returns a list. I and call this function from another function.
the function that retrieves data and returns the list:
Future<List> getNewsOnSearchBar() async{
final String _collection = 'news';
final Firestore _fireStore = Firestore.instance;
var newsList = [];
print("1");
Future<QuerySnapshot> getData() async {
print("2");
return await _fireStore.collection(_collection).getDocuments();
}
QuerySnapshot val = await getData();
if (val.documents.length > 0) {
print("3");
for (int i = 0; i < val.documents.length; i++) {
newsList.add(val.documents[i].data["headline"]);
}
} else {
print("Not Found");
}
print("4");
return newsList.toList();
}
And I am calling this getNewsOnSearchBar() function from another function that returns a Widget as below:
Widget _showSearchBar(BuildContext context) {
var list = [];
getNewsOnSearchBar().then((value){
print(value); //this will print successfully
list = value; //but this assign doesn't work
});
print(list); //prints an empty list
return GFSearchBar(
// overlaySearchListHeight: 160.0,
searchList: list, //empty
searchQueryBuilder: (query, list) {
return list
.where((item) => item.toLowerCase().contains(query.toLowerCase()))
.toList();
},
overlaySearchListItemBuilder: (item) {
return Container(
padding: const EdgeInsets.all(3),
child: Text(
item,
style: const TextStyle(fontSize: 18),
),
);
},
onItemSelected: (item) {},
);
}
Can someone help me, please?
You are using Future in a wrong way, when you are building a widget that depends on a value from a Future, you need to use FutureBuilder,
So for example your _showSearchBar method -which,I assume, you are calling inside build to show something on the screen- will become:
Widget _showSearchBar(BuildContext context) {
return FutureBuilder(
future: getNewsOnSearchBar(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
if (snapshot.data != null) {
return GFSearchBar(
// overlaySearchListHeight: 160.0,
searchList: list, //empty
searchQueryBuilder: (query, list) {
return list
.where((item) => item.toLowerCase().contains(query.toLowerCase()))
.toList();
},
overlaySearchListItemBuilder: (item) {
return Container(
padding: const EdgeInsets.all(3),
child: Text(
item,
style: const TextStyle(fontSize: 18),
),
);
},
onItemSelected: (item) {},
);
} else {
return CircularProgressIndicator();
}
}
}
);
}
Learn more about Futures and async/await here:
https://www.youtube.com/watch?v=OTS-ap9_aXc
https://www.youtube.com/watch?v=SmTCmDMi4BY
https://www.youtube.com/watch?v=ek8ZPdWj4Qo