I am trying to print a value returned from a function to the screen.
Function:
calculation.dart:
Future<dynamic> getTotalCost(BuildContext context) async {
final user = Provider.of<Userr?>(context, listen: false);
double totalCost = 0.0;
QuerySnapshot snapshot = await FirebaseFirestore.instance
.collection('myOrders')
.doc(user?.uid)
.collection('items')
.get();
for (var doc in snapshot.docs) {
totalCost += doc["price"];
}
print(totalCost.toString());
return totalCost.toString();
}
But instead printing the value , it prints Instance of Future.
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Item total', style: textStyle),
Text('${ Provider.of<Calculations>(context, listen: false).getTotalCost(context).toString()}', style: textStyle),
],
),
I also know the reason that this is due to the function is asynchronous, it returns future object instead of actual value. But I need to know how can I change the above code in order to print the actual value.
Try the following code:
FutureBuilder(
future: Provider.of<Calculations>(context, listen: false).getTotalCost(context),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Item total', style: textStyle),
Text('${snapshot.data}', style: textStyle),
],
);
} else if (snapshot.hasError) {
return Text("Error: ${snapshot.error.toString()}");
} else {
return const CircularProgressIndicator();
}
},
),
In order to reduce the number of Widget rebuild, and for performance optimization, I suggest to only wrap the Widget to update with the Future value.
By doing so, you limit the Widget tree in the builder of the FutureBuilder to the strict necessary.
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Item total', style: textStyle),
FutureBuilder(
future: Provider.of<Calculations>(context, listen: false)
.getTotalCost(context),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('${snapshot.data}', style: textStyle);
} else if (snapshot.hasError) {
return Text("Error: ${snapshot.error.toString()}");
} else {
return const CircularProgressIndicator();
}
},
),
],
),
Related
I have this code as am trying to code something to update data in firestore.
#override
Widget build(BuildContext context) {
// Use the Todo to create the UI.
return Scaffold(
appBar: AppBar(
title: Text(mid.toString()),
),
body: FutureBuilder<Member?>(
future: readMember(mid),
builder: (context, snapshot) {
if (snapshot.hasData) {
final member = snapshot.data;
/// return a form
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
);
}
if snapshot hasData I want to return a form like this
Card(
child: Row(
children: <Widget>[
TextField(
controller: controllerName,
decoration: decoration('name'),
),
const SizedBox(height: 24),
TextField(
controller: controllerAge,
decoration: decoration('Age'),
keyboardType: TextInputType.number,
),
const SizedBox(height: 24),
ElevatedButton(
child: const Text('Create'),
onPressed: () {},
),
],
));
All my attempt yield no success please I need help.
Check others state like error or if the data is null or not
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text("Got Error");
}
if (snapshot.data == null) {
return Text("No data");
}
if (snapshot.hasData) {
final member = snapshot.data;
return Card( ///here form
child: Row(
children: <Widget>[],
));
} else {
return const Center(child: CircularProgressIndicator());
}
},
And provide width on TextFiled to fix overflow, TextFiled and row are trying to get infinite with.
just wrap with Expanded
Expanded(child: TextField(...)),
You can find more about unbound height& width
I have FutureBuilder widget as Scaffold's body and I want to display a list of widgets when database returns required info but while database is properly returning data the FutureBuilder does not show any widgets(like CircularProgressIndicator or required List as it finishes its job). Code shown below:
Future<List<Widget>> getList() async{
List<Widget> list = [];
var db = dbMethods();
db.getConnection().then((conn) async {
await Future.delayed(const Duration(seconds: 1));
var result = await conn.query("select * from info", []);
for(var row in result) {
var item = BilbordItem(
firm: row[2],
constructionType: row[3],
demonstrationType: row[4],
lat: double.parse(row[5]),
long: double.parse(row[6]),
sizeX: double.parse(row[7]),
sizeY: double.parse(row[8]),
address: row[9],
positionRate: int.parse(row[10]),
colorsRate: int.parse(row[11]),
passabilityRate: int.parse(row[12]),
typeRate: int.parse(row[13])
);
print(item.address); //here i was checking if database fetch is working and it does print required info
list.add(item);
}
conn.close();
});
return list;
}
class _ListPage extends State<ListPage>{
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Список билбордов"),
centerTitle: true,
),
body: FutureBuilder(
future: getList(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if(snapshot.connectionState == ConnectionState.done){
if(snapshot.hasData && !snapshot.hasError){
return Center(
child: Column(
children: snapshot.data,
),
);
}
else if(snapshot.hasError){
return SingleChildScrollView(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(Icons.error_outline, color: Colors.red,),
Text(snapshot.error.toString()),
],
),
),
);
}
}else {
return Center(child: CircularProgressIndicator());
}
return Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Text("Empty"),
],
),
);
},
),
);
}
}
i have a function for asking that u have token apikey or not. but when im trying to get some textfield there, i cant texting in it(always refresh causing that futurebuilder i think).
below is the function.
Future<String> getApiKey() async {
WidgetsFlutterBinding.ensureInitialized();
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString("apikey");
}
FutureBuilder<String>(
future: getApiKey(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(
child: CupertinoActivityIndicator(),
);
default:
if (snapshot.hasError) {
print("has error");
return Center(child: Text('Error: ${snapshot.error}'));
} else if (snapshot.data == null) {
print("not login");
// return Container();
return Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset("assets/image/images/nodata.svg",
height: 150),
SizedBox(height: 15),
Text(
"no data",
style: TextStyle(
color: baseColor,
fontSize: 18,
fontWeight: FontWeight.w500,
fontFamily: "Sofia"),
),
],
),
);
} else {
return FutureBuilder<List<LineUpListModel>>(
future: prov.setLineUpList(prov),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(
child: CupertinoActivityIndicator(),
);
default:
if (snapshot.hasError)
return Center(
child: Text('Error: ${snapshot.error}'));
else if (snapshot.data == null)
// return Container();
return Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
"assets/image/images/nodata.svg",
height: 150),
SizedBox(height: 15),
Text(
"no data",
style: TextStyle(
color: baseColor,
fontSize: 18,
fontWeight: FontWeight.w500,
fontFamily: "Sofia"),
),
],
),
);
else {
print(prov.getMyProfileModel.apiKey.toString());
return TextField();
}
}
},
);
}
}
},
),
How can I type in the TextField without any refreshing interruptions from the Futurebuilder? what should I fix?
You can take this approach for all of your future builder.
Future _future;
#override
#initState() {
_future = getApiKey();
}
Now in your FutureBuilder use this _future variable.
FutureBuilder<String>(
future: _future,
The issue might come from your future: getApiKey() because you are not saving an instance of your future value so it will be reloaded each time the build() method is called (which will happen a lot). A solution might be to keep your future instance inside a variable and initializing it only one time (you will need a StatefulWidget):
Future<String> _futureString;
#override
void initState() {
super.initState();
_futureString = getApiKey();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: _futureString,
);
}
By doing so the _futureString variable will keep the value of your asynchronous operation and won't reload every time.
There is a list that displays data from the database that comes to it at the time the data is fetched and until the data appears, the CircularProgressIndicator () appears so that the user knows that there is a process happening in the background.
Excellent but there is a problem with this CircularProgressIndicator () continues to work non-stop if there is no data in the database. Herein lies the problem.
It is supposed to work for a specified time and if there is no data in the database it will stop working and disappear.
Is there a way to do this? So that if there is no data that can be fetched it stops working?
My code:
class MainListView extends StatefulWidget {
MainListViewState createState() => MainListViewState();
}
class MainListViewState extends State {
final String apiURL = 'http://====================/getStudentInfo.php';
Future<List<Studentdata>> fetchStudents() async {
var response = await http.get(apiURL);
if (response.statusCode == 200) {
final items = json.decode(response.body).cast<Map<String, dynamic>>();
List<Studentdata> studentList = items.map<Studentdata>((json) {
return Studentdata.fromJson(json);
}).toList();
return studentList;
}
else {
throw Exception('Failed to load data from Server.');
}
}
#override
Widget build(BuildContext context) {
return FutureBuilder<List<Studentdata>>(
future: fetchStudents(),
builder: (context, snapshot) {
if (!snapshot.hasData)
return Center(
child: CircularProgressIndicator()
);
return ListView(
children: snapshot.data
.map((data) => Column(children: <Widget>[
GestureDetector(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.fromLTRB(20, 5, 0, 5),
child: Text(data.studentName,
style: TextStyle(fontSize: 21),
textAlign: TextAlign.left))
]),),
Divider(color: Colors.black),
],))
.toList(),
);
},
);
}
}
You can try with the below lines
return FutureBuilder<List<Studentdata>>(
future: fetchStudents(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView(
children: snapshot.data
.map((data) =>
Column(children: <Widget>[
GestureDetector(
child: Row(
crossAxisAlignment: CrossAxisAlignment
.start,
children: [
Padding(
padding: EdgeInsets.fromLTRB(
20, 5, 0, 5),
child: Text(data.studentName,
style: TextStyle(fontSize: 21),
textAlign: TextAlign.left))
]),),
Divider(color: Colors.black),
],))
.toList(),
);
}
else if (!snapshot.hasData) {
return Text("No data Available");
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return Center(
child: CircularProgressIndicator()
);
},
);
You can also set a value to CircularProgressIndicator() to stop at full circle indicator.
Just do this:
if (!snapshot.hasData && !snapshot.hasError) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(
child: Text("No data Found"),
);
} else {
// do something here
}
It means that if status code is 200 and there is no data then you are going to get an error.
This is my code in which I want to display an email which is a Future and I will get it from my firestore.However, I am not sure how I will need to retrieve my value using FutureBuilder which I want to use as a string.
This is my method to get my email:
Future<String> getEmail() async {
String _email = (await FirebaseAuth.instance.currentUser()).email;
DocumentSnapshot snapshot = await _firestore.collection('users')
.document(_email)
.collection('met_with')
.document('email')
.get();
// print("data: ${snapshot.data}"); // might be useful to check
return snapshot.data['email']; // or another key, depending on how it's saved
}
this is my updated code:
#override
Widget build(BuildContext context) {
return Card(
elevation: 3.0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
child: ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage(imagePath),
),
trailing: Icon(Icons.more_horiz),
title: Text(
email,
style: TextStyle(
c #override
Widget build(BuildContext context) {
return Card(
elevation: 3.0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
child: ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage(imagePath),
),
trailing: Icon(Icons.more_horiz),
title: Text(
email,
style: TextStyle(
color: Colors.deepPurple[700],
fontWeight: FontWeight.bold,
),
),
subtitle: Text(infection),
onTap: () => showModalBottomSheet(
context: context,
builder: (builder) {
return FutureBuilder(
future: getEmail(),
builder: (BuildContext context, snapshot) {
if(snapshot.hasData){
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: CircularProgressIndicator(),
);
}else{
return Padding(padding: EdgeInsets.symmetric(vertical: 50.0, horizontal: 10.0),
child: Column(
children: <Widget>[
BottomSheetText(
question: 'Email', result: snapshot.data['email']),
SizedBox(height: 5.0),
BottomSheetText(
question: 'Contact Time',result:"lol"),// getTimeStamp().toString()),
SizedBox(height: 5.0),
BottomSheetText(
question: 'Contact Location',
result: "help"),
SizedBox(height: 5.0),
BottomSheetText(question: 'Times Contacted', result: "lool",),
],
),
);
}
}else{
return CircularProgressIndicator();}
}
);
}
),
),
);
}
}
Here is my firebase database:
enter image description here
Your query is wrong, try following one.
Future<String> getEmail() async {
String _email = (await FirebaseAuth.instance.currentUser()).email;
var a = await Firestore.instance
.collection("met_with")
.where('email', isEqualTo:. _email )
.getDocuments();
return a.documents[0]['email'];
}
And to call this method you need futureBuilder.
FutureBuilder(
future: getEmail(),
builder: (BuildContext context, snapshot) {
if(snapshot.hasData){
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: CircularProgressIndicator(),
);
}else{
return Center( // here only return is missing
child: Text(snapshot.data['email'])
);
}
}else if (snapshot.hasError){
return Text('no data');
}
return CircularProgressIndicator();
},
),
You need to use a FutureBuilder:
FutureBuilder(
future: getEmail(),
builder: (BuildContext context, snapshot) {
if(snapshot.hasData){
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: CircularProgressIndicator(),
);
}else{
return Center( // here only return is missing
child: Text(snapshot.data['email'])
);
}
}else if (snapshot.hasError){
Text('no data');
}
return CircularProgressIndicator();
},
),
This way you can use the returned value of the method inside the build method. Also change the Future method to the following:
Future<String> getEmail() async {
String _email = (await FirebaseAuth.instance.currentUser()).email;
return await _firestore.collection('users')
.document(_email)
.collection('met_with')
.document('email')
.get();
}