I have created a demo project for showing orders using FutureBuilder but Its not showing order, instead its giving infinite loop , so where should I correct my code
Why it is infinite?
herewith I am sending code for my provider method to fetch orders and the code where I am using it
or is there any other better option to replace future builder..
Future<void> fetchandsetorders() async {
print('I am fetchandsetorders method of provider');
final url = Uri.parse(
mylink);
final response = await http.get(url);
final List<OrderItem> loadedorders = [];
final Map<String, dynamic> extradeddata = json.decode(response.body) as Map<String, dynamic>;
extradeddata.forEach((orderid, orderdata) {
loadedorders.add(
OrderItem(
id: orderid,
products: (orderdata['products'] as List<dynamic>).map((item) {
return CartItem(
id: item['id'],
title: item['title'],
quantity: item['qty'],
price: item['price']);
}).toList(),
amount: orderdata['amount'],
date: DateTime.parse(orderdata['date']),
),
);
});
_orders=loadedorders.reversed.toList();
notifyListeners();
}
class _OrderScreenState extends State<OrderScreen> {
#override
Widget build(BuildContext context) {
final orderdata = Provider.of<Orders>(context);
print('I am buildmethod');
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromRGBO(Random().nextInt(255),
Random().nextInt(255), Random().nextInt(255), 1),
title: Text('List of orders: ' + orderdata.ordercount.toString()),
),
drawer: AppDrawer(),
body: FutureBuilder(
future: Provider.of<Orders>(context, listen: false).fetchandsetorders(),
builder: (context,snapshop){
if(snapshop.connectionState==ConnectionState.waiting)
{
return Center(child: CircularProgressIndicator());
}
else
{
if(snapshop.error!=null)
{
return Text(snapshop.error.toString());
}
else
{
return ListView.builder(
itemCount: orderdata.ordercount,
itemBuilder: (context, index) {
return OrderItemWidget(
order: orderdata.orders[index],
);
});
}
}
},
),
);
}
}
create a state variable for Future like
late myFuture = Provider.of<Orders>(context, listen: false).fetchandsetorders(),
And use on
body: FutureBuilder(
future: myFuture,
Related
Currently struggling to make a ListView data retrieved from Firestore.
I am trying to get "kids name" saved under in the firestore as linked photo.
Firestore
No error message is shown up but the data is not retrieved correctly and shown blank screen...hope anyone can correct my code!
and here is my code:
class kidsNamePick extends StatefulWidget {
#override
_kidsNamePickState createState() => _kidsNamePickState();
}
class _kidsNamePickState extends State<kidsNamePick> {
List<Memo> kidsnamelist = [];
Future<void>fetchMemo()async{
final kidsnames = await FirebaseFirestore.instance.collection('useraccount').doc(FirebaseAuth.instance.currentUser!.uid)
.collection('kidsname').get();
final docs = kidsnames.docs;for (var doc in docs){
Memo fetchMemo = Memo(kidsname: doc.data()['kids name'],
);
kidsnamelist.add(fetchMemo);}
setState(() {
});}
#override
void initState(){
super.initState();
fetchMemo();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add/Select Kids'),
),
body: ListView.builder(
itemCount: kidsnamelist.length,
itemBuilder: (context, index){
return ListTile(
title: Text(kidsnamelist[index].kidsname),
);
},
)
);
}
}
The best way to call future method is using FutureBuilder, first change your fetchMemo to this:
Future<List<Memo>> fetchMemo() async {
try {
final kidsnames = await FirebaseFirestore.instance
.collection('useraccount')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection('kidsname')
.get();
final docs = kidsnames.docs;
return docs
.map((doc) => Memo(
kidsname: doc.data()['kids name'],
))
.toList();
} catch (e) {
return [];
}
}
then change your build method to this:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add/Select Kids'),
),
body: FutureBuilder<List<Memo>>(
future: fetchMemo(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<Memo> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(data[index].kidsname),
);
},
);
}
}
},
),
);
}
I have just created a demo for better understanding future builder
scaffold body showing all users from api and appear should be shown with number of users
appear's title showing 0 when loaded but does not change...what to do to rebuild it
here is my code
class _withmodelState extends State<withmodel> {
List<UserModel> userlist=[];
Future<List<UserModel>> getdata() async {
final resp =
await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));
if (resp.statusCode == 200) {
print('i ma called');
List<dynamic> dlist = json.decode(resp.body);
await Future.delayed(Duration(seconds: 2));
userlist= dlist.map((e) => UserModel.fromJson(e)).toList();
return userlist;
}
return userlist;
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(title: Text("Total users="+userlist.length.toString()),),
body: MyBody(
//MyBody returning FutureBuilder for showing userlist array;
),
));
}
You can use ChangeNotifier like this, first create a class like this:
class WithmodelDecl with ChangeNotifier {
ValueNotifier<int> totalUsers = ValueNotifier<int>(0);
}
WithmodelDecl withmodeldecl = new WithmodelDecl();
then use it like this:
return SafeArea(
child: Scaffold(
appBar: PreferredSize(
child: ValueListenableBuilder<int>(
valueListenable: withmodeldecl.totalUsers,
builder: (context, value, _) {
return AppBar(
title: Text("Total users=" + value.toString()),
);
}),
preferredSize: AppBar().preferredSize),
body: MyBody(
//MyBody returning FutureBuilder for showing userlist array;
),
));
and finally change your getdata to this:
Future<List<UserModel>> getdata() async {
final resp =
await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));
if (resp.statusCode == 200) {
print('i ma called');
List<dynamic> dlist = json.decode(resp.body);
await Future.delayed(Duration(seconds: 2));
userlist= dlist.map((e) => UserModel.fromJson(e)).toList();
withmodeldecl.totalUsers.value = userlist.length;
return userlist;
}
return userlist;
}
You also need to rebuild the Text widget, that you are using to show the count, when the count is available, i.e., the Future completes.
You need to wrap that Text widget with FutureBuilder like this:
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: FutureBuilder<List<UserModel>>(
future: getdata(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
final List<UserModel> userlist = snapshot.data!;
return Text("Total users= ${userlist.length}");
// it's better to use String interpolation than "Total users=" + snapshot.data!.length.toString()
} else {
// return loading widget
}
},
),
),
body: MyBody(
//MyBody returning FutureBuilder for showing userlist array;
),
),
);
It is better to have the Future in a variable, and then use it like this, to avoid unwanted and repeated calling of it whenever the build() method is called:
late final Future<List<UserModel>> _userListFuture;
And initialize it in your initState(), like this:
#override
void initState() {
super.initState();
_userListFuture = Future<List<UserModel>>(getdata);
}
And use it with your FutureBuilder like this:
FutureBuilder<List<UserModel>>(
future: _userListFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// return your widget showing data
} else {
// return loading widget
}
},
)
I tried to fetch data as List from database but data not display in UI. How I fix this? I tried fetch data using model class and my collection name is '12words'.
UI code:
class _WordsScreenState extends State<WordsScreenState> {
List<Words12> wordList = [];
#override
void iniState() {
fetchRecords();
iniState();
}
fetchRecords() async {
var records = await FirebaseFirestore.instance.collection('12words').get();
mapRecords(records);
}
mapRecords(QuerySnapshot<Map<String, dynamic>> records) {
var _list = records.docs
.map(
(words12) => Words12(
id: words12.id,
wordName: words12['wordName'],
categoryName: words12['categoryName'],
),
)
.toList();
setState(() {
wordList = _list;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: ListView.builder(
itemCount: wordList.length,
itemBuilder: (context, index) {
return (ListTile(
title: Text(wordList[index].wordName),
subtitle: Text(wordList[index].categoryName),
));
},
));
}
Model:
First do not call async function in initState, instead of that, use FutureBuilder and also change your fetchRecords() to return a list. This is a full example of using FutureBuilder with your code:
class TestFuture extends StatefulWidget {
const TestFuture({super.key});
#override
State<TestFuture> createState() => _TestFutureState();
}
class _TestFutureState extends State<TestFuture> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: FutureBuilder<List<Words12>>(
future: fetchRecords(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<Words12> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return (ListTile(
title: Text(data[index].wordName),
subtitle: Text(data[index].categoryName),
));
},
);
}
}
},
),
);
}
Future<List<Words12>> fetchRecords() async {
var records = await FirebaseFirestore.instance.collection('12words').get();
return mapRecords(records);
}
List<Words12> mapRecords(QuerySnapshot<Map<String, dynamic>> records) {
var _list = records.docs
.map(
(words12) => Words12(
id: words12.id,
wordName: words12['wordName'],
categoryName: words12['categoryName'],
),
)
.toList();
return _list;
}
}
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),
I have try many sample in stack but still can`t get the idea which part i miss, the result in UI always display null, ..
here is the code i try :
class PointBallance {
String id, date, datetime, companyid, storecode, customercode, topup, amount, remark, cashier, invoice ;
PointBallance({this.id, this.date, this.datetime, this.companyid, this.storecode, this.customercode, this.topup, this.amount, this.remark, this.cashier, this.invoice});
factory PointBallance.fromJson(Map<String, dynamic> json) {
return PointBallance(
id: json['id'],
date: json['date'],
datetime: json['datetime'],
companyid: json['company_id'],
storecode: json['store_code'],
customercode: json['customer_code'],
topup: json['topup'],
amount: json['amount'],
remark: json['remark'],
cashier: json['cashier'],
invoice: json['invoice'],
);
}
}
the part for call http is here :
Future<List<PointBallance>> pointBal() async {
var url = 'http://someUrl';
var res = await http.get(url);
if(res.statusCode == 200) {
var dtpoint = json.decode(res.body);
print(dtpoint);
var bel = List<PointBallance>.from(dtpoint.map((i) => PointBallance.fromJson(i)));
return bel;
} else {
throw Exception(
"Request to $url failed with status ${res.statusCode}: ${res.body}"
);
}
}
and for screen to display data ..
class _PointScreenState extends State<PointScreen> {
Future<List<PointBallance>> _point;
AuthService _authService = new AuthService();
#override
void initState() {
_point = _authService.pointBal();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Point'),
),
body: FutureBuilder<List<PointBallance>>(
future: _point,
builder: (context, snapshot) {
if (snapshot.hasData) {
var dt = snapshot.data[0].id;
return Column(
children: <Widget>[
**Text('in the top $dt'),**
Expanded(
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder:(BuildContext context, int index){
var hei = snapshot.data[index];
return **Text(hei.id != null ? hei.id : 'Cant get data')**;
}),
),
],
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return CircularProgressIndicator();
}),
);
}
}
in console i got result
print(dtpoint);
any guide to correctly display data result? because in console there is result.