Flutter FutureBuilder show Progress indicator - asynchronous

I have a button which onpressed inserts something in DB and redirects user to another page. I am trying to implement FutureBuilder which should show CircularProgressIndicator until everything is done.
This is my function:
Future<bool> insertPhoneNumber(String phoneNumber) async {
String token = await getToken();
if (token.isNotEmpty) {
var body = jsonEncode({'token': token, 'userID': user.getUserID(), 'phoneNumber': phoneNumber});
print(body.toString());
var res = await http.post((baseUrl + "/insertPhoneNumber/" + user.getUserID()),
body: body,
headers: {
"Accept": "application/json",
"content-type": "application/json"
});
if (res.statusCode == 200) {
print("Insert Phone Number is OK");
notifyListeners();
return true;
} else {
print("Insert Phone Number not OK");
notifyListeners();
return false;
}
} else {
print("Insert Phone Number failed due to unexisting token");
}
}
and this is a button which triggers DB interaction:
RaisedButton(
color: Color.fromRGBO(105, 79, 150, 1),
onPressed: () {
var user = Provider.of<UserRepository>(context);
String completePhoneNumber = _selectedDialogCountry.phoneCode + phoneNumberController.text;
FutureBuilder(
future: user.insertPhoneNumber(completePhoneNumber),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return CircularProgressIndicator(
backgroundColor: Colors.blue);
} else {
return Dashboard();
}
},
);
},
textColor: Colors.white,
child: Text("SAVE"),
)
It updates DB but nothing else happens. There is no progress indicator nor redirection to Dashboard.
EDIT
This is my full build method:
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
AutoSizeText(
'What is your phone number?',
style: TextStyle(fontSize: 30),
maxLines: 1,
),
Row(
children: <Widget>[
SizedBox(
width: 9.0,
child: Icon(Icons.arrow_downward),
),
SizedBox(width: 8.0),
SizedBox(
width: 120.0,
height: 65.0,
child: Card(
child: ListTile(
onTap: _openCountryPickerDialog,
title: _buildDialogItem(_selectedDialogCountry),
),
)),
Expanded(
child: TextField(
autofocus: true,
keyboardType: TextInputType.number,
decoration:
InputDecoration(border: OutlineInputBorder())),
),
],
),
SizedBox(
width: 100,
child: RaisedButton(
color: Color.fromRGBO(105, 79, 150, 1),
onPressed: () {
print("Test");
},
textColor: Colors.white,
child: Text("SAVE"),
),
)
],
)),
);
}

It is not working because the FutureBuilder isn't attached to the Widget tree.
The once the future is not in done state, it is just creating an instance Dashboard.You shouldn't be using FutureBuilder here instead you can set the child widget based on some variable and when future complete, you call setState on the that variable to update the state which will in-turn rebuild the widget with the new state value
Something like this
var isLoading = false;
void insertNumber(){
var user = Provider.of<UserRepository>(context);
String completePhoneNumber = _selectedDialogCountry.phoneCode + phoneNumberController.text;
setState(() => isLoading=true);
user.insertPhoneNumber(completePhoneNumber).then((result){
setState(() => isLoading=false);
})
}
Widget build(BuildContext context){
return RaisedButton(
color: Color.fromRGBO(105, 79, 150, 1),
onPressed: () {
var user = Provider.of<UserRepository>(context);
String completePhoneNumber = _selectedDialogCountry.phoneCode + phoneNumberController.text;
FutureBuilder(
future: user.insertPhoneNumber(completePhoneNumber),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return CircularProgressIndicator(
backgroundColor: Colors.blue);
} else {
return Dashboard();
}
},
);
},
textColor: Colors.white,
child: isLoading? CircularProgressIndicator(): Text("Save"),
);
}
For your use case, you will need more that two state to achieve the UI you are looking for .Example DEFAULT,LOADING,ERROR,SUCCESS

Related

Error message - Alert Message Not Appearing

I placing on the interface a message to provide information to users on errors in logging or signing up.
The error does not provide a bug and the application continues to run.
However, the message of error or the status is not passed on to the widget _showAlert which does not appear.
_signup() async {
AuthNotifier authNotifier = Provider.of<AuthNotifier>(context, listen: false);
{
setState(() {
});
final status =
await signup(_user, authNotifier);
if (status == AuthResultStatus.successful) {
// Navigate to success screen
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => SecondPage()),
(r) => false);
} else {
final errorMsg = AuthExceptionHandler.generateExceptionMessage(status);
_showAlert(errorMsg);
}
}
}
_showAlert(errorMsg) {
if (errorMsg != null) {
return Container(
color: Colors.amberAccent,
width: double.infinity,
padding: EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Icon(Icons.error_outline),
),
Expanded(
child: AutoSizeText(
errorMsg,
maxLines: 3,
),
),
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: IconButton(
icon: Icon(Icons.close),
onPressed: () {
setState(() {
errorMsg = null;
});
},
),
)
],
),
);
}
return SizedBox(
height: 0,
);
}
You are returning a Widget not showing a Dialog. To show a Dialog try this
_showAlert(errorMsg, BuildContext context) {
if (errorMsg != null) {
showDialog(
context: context,
builder: (BuildContext context) =>
Dialog(
child: Container(
// ...
),
),
);
}
}

Show loading indicator /spinner when the page data isn't fully loaded from Firebase - Flutter

In my Flutter app, I am using ModalProgressHUD to show a spinner when I click on save buttons in my form screens and it stops spinner once data successfully writes to Firebase.
I have this screen that uses Listview.builder to display a list of all my expenses and I want to automatically show spinner as soon as the page displays, and to stop spinner once all the data from Firebase fully loads.
I need assistance in doing this. I've pasted excerpt of my code as shown below. Thanks in advance.
//class wide declaration
bool showSpinner = true;
Widget build(BuildContext context) {
ExpenseNotifier expenseNotifier = Provider.of<ExpenseNotifier>(context);
Future<void> _resfreshList() async {
expenseNotifier.getExpenses(expenseNotifier);
var expenseList = ExpenseNotifier.getExpenses(expenseNotifier);
if (expenseList != null) {
setState(() {
showSpinner = false;
});
}
return Scaffold(
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: RefreshIndicator(
onRefresh: _resfreshList,
child: Consumer<ExpenseNotifier>(
builder: (context, expense, child) {
return expense == null
? Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
PaddingClass(bodyImage: 'images/empty.png'),
SizedBox(
height: 20.0,
),
Text(
'You don\'t have any expenses',
style: kLabelTextStyle,
),
],
)
: ListView.separated(
itemBuilder: (context, int index) {
var myExpense = expense.expenseList[index];
return Card(
elevation: 8.0,
color: Colors.white70,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
RegularExpenseTextPadding(
regText:
'${_formattedDate(myExpense.updatedAt)}',
),
Container(
margin: EdgeInsets.all(20.0),
padding: const EdgeInsets.all(15.0),
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(5.0)),
border: Border.all(
color: kThemeStyleBorderHighlightColour),
),
child: Row(
children: <Widget>[
Expanded(
flex: 5,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Text(
'${myExpense.amount}',
style: kRegularTextStyle,
),
SizedBox(
height: 20.0,
),
Text(
myExpense.description,
style: kRegularTextStyle,
),
],
),
),
Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
expenseNotifier.currentExpense =
expenseNotifier
.expenseList[index];
Navigator.of(context).push(
MaterialPageRoute(builder:
(BuildContext context) {
return ExpenseDetailsScreen();
}));
},
child: Icon(
FontAwesomeIcons.caretDown,
color: kThemeIconColour,
),
),
),
],
),
),
],
),
);
},
separatorBuilder: (BuildContext context, int index) {
return SizedBox(
height: 20.0,
);
},
itemCount: expenseNotifier.expenseList.length,
);
},
),
),
),
);
}
this is an example from my app:
bool _isLoading = false; <- default false
bool _isInit = true; <- to mae it only load once
#override
void initState() {
if (_isInit) {
// activating spinner
_isLoading = true;
// your function here <------
_isInit = false;
super.initState();
}
Initstate gets called before the user can see any kind of thin in your app, so this is the perfect place to make your firebase data load. with this logic from above the loading spinner shows as long you are receiving the data. And your body looks like the following then:
#override
Widget build(BuildContext context) {
return _isLoading <- is loading condition true? shows spinner
? Center(child: CircularProgressIndicator()) <- loading spinner
// else shows your content of the app
: SafeArea(
child: Container()
....

how to retrieve phone number from firebase using flutter

How to retrieve phone number which is used during phone authentication in Firebase and display it using flutter, I tried this below method but it is not working for me:
FirebaseAuth.instance.currentUser().then((user) {
_userId = user.uid;
_phone = user.phoneNumber;
});
This is my full code
class HomePageState extends State<HomeScreen>{
String _userId,_phone;
#override
Widget build(BuildContext context) {
FirebaseAuth.instance.currentUser().then((user) {
_userId = user.uid;
_phone = user.phoneNumber;
});
var myGridView = new GridView.builder(
itemCount: services.length,
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
// childAspectRatio: (itemWidth / itemHeight),
),
itemBuilder: (BuildContext context, int index) {
return FlatButton(
child: Padding(
padding: const EdgeInsets.all(0.0),
// child:new Card(
//elevation: 5.0,
child: new Container(
alignment: Alignment.centerLeft,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
new Text(_userId),
],
),
),
),
),
onPressed: () {
String catgry = services[index];
if (catgry == "coming soon") {
showDialog(
barrierDismissible: false,
context: context,
child: new CupertinoAlertDialog(
title: new Column(
children: <Widget>[
new Text("This feature is coming soon"),
new Icon(
Icons.favorite,
color: Colors.red,
),
],
),
// content: new Text( services[index]),
actions: <Widget>[
new FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: new Text("OK"))
],
));
} else {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => Details(catgry,phone1)));
}
},
);
},
);
return new Scaffold(
appBar: new AppBar(
backgroundColor: Colors.black,
title: Text("Appbar", style: TextStyle(color: Colors.white),),
),),
body: myGridView,
bottomNavigationBar: CustomBottomBar(),
);
}
}
SInce you're using a StatefulWidget, you can create a getUser function that will update the userId and phone then call the Function in your initState. Also add a loading screen with Center(child: CircularProgressIndicator()) when fetching the values
class _HomeScreenState extends State<HomeScreen> {
String _userId,_phone;
bool loading = true;
getUser(){
FirebaseAuth.instance.currentUser().then((user) {
_userId = user.uid;
_phone = user.phoneNumber;
loading = false;
setState(() {});
});
}
#override
void initState() {
super.initState();
getUser();
}
#override
Widget build(BuildContext context) {
var myGridView = new GridView.builder(
itemCount: services.length,
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
// childAspectRatio: (itemWidth / itemHeight),
),
itemBuilder: (BuildContext context, int index) {
return FlatButton(
child: Padding(
padding: const EdgeInsets.all(0.0),
// child:new Card(
//elevation: 5.0,
child: new Container(
alignment: Alignment.centerLeft,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
new Text(_userId),
],
),
),
),
),
onPressed: () {
String catgry = services[index];
if (catgry == "coming soon") {
showDialog(
barrierDismissible: false,
context: context,
child: new CupertinoAlertDialog(
title: new Column(
children: <Widget>[
new Text("This feature is coming soon"),
new Icon(
Icons.favorite,
color: Colors.red,
),
],
),
// content: new Text( services[index]),
actions: <Widget>[
new FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: new Text("OK"))
],
));
} else {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => Details(catgry,phone1)));
}
},
);
},
);
return new Scaffold(
appBar: new AppBar(
backgroundColor: Colors.black,
title: Text("Appbar", style: TextStyle(color: Colors.white),),
),
body: loading?Center(child: CircularProgressIndicator()):myGridView,
bottomNavigationBar: CustomBottomBar(),
);
}
}
You can do this using async/await and once you get the data you can call setState()
String phone;
#override
void initState(){
super.initState();
getPhone();
}
getPhone() async{
FirebaseUser currentUser = await FirebaseAuth.instance.currentUser();
setState(() {
phone=currentUser.phoneNumber;
});
}

Flutter & Firebase: Error with FutureBilder

Currently i develop a Meal and Shopping App. In this App you can Add what you want to Eat next and have the secound Tab, Shopping where you can Add your Items you want to buy next. Created is that a User can invite another User to edit together the List.
I get the Error shown below. I can't figure out how to return the Container. At the void saveInviteToFirestore the user is not used do I need that it used?
Code
import 'package:flutter/material.dart';
import 'package:mealapp/models/Widgets/whenAndWhatToEat.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:intl/intl.dart';
import 'package:mealapp/models/global.dart';
import 'package:status_alert/status_alert.dart';
import 'package:firebase_auth/firebase_auth.dart';
class MealTile extends StatefulWidget {
final MealsAndWhen mealsAndWhen;
MealTile({this.mealsAndWhen});
#override
MealTileState createState() {
return MealTileState();
}
}
class MealTileState extends State<MealTile> {
String id;
final db = Firestore.instance;
String mail;
List<String> authors = [];
DateTime selectedDate = DateTime.now();
Future pickDate() async {
DateTime datepick = await showDatePicker(
context: context,
initialDate: new DateTime.now(),
firstDate: new DateTime.now().add(Duration(days: -0)),
lastDate: new DateTime.now().add(Duration(days: 365)));
if (datepick != null)
setState(() {
selectedDate = datepick;
});
}
Future<String> inputData() async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
return user != null ? user.uid : null;
}
Future<String> inputDataMail() async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
return user != null ? user.email : null;
}
String userId;
void _getUserId() {
inputData().then((value) => setState(() {
userId = value;
}));
}
String currentMail;
void _getMail(doc) {
inputDataMail().then((value) => setState(() {
currentMail = value;
}));
}
/*void _getAuthors(DocumentSnapshot doc) async {
authors = [];
//if (await FirebaseAuth.instance.currentUser() != null) {
authors = List.from(doc.data['Authors']);
print(doc.data['authors']);
//authors.insert(0, currentMail);
//}
}*/
Widget buildItem(DocumentSnapshot doc) {
DateTime now = doc.data['Date'].toDate();
DateFormat formatter = DateFormat('dd-MM-yyyy');
String formatted = formatter.format(now);
_getUserId();
_getMail(doc);
if (doc.data['Authors'] != null) {
//_getAuthors(doc);
//print('Current mail: ' + currentMail + authors.toString() + doc.data['Author'] + doc.data['Meal']);
}
if (now.day == DateTime.now().day) { // If the Date of the meal is today
deleteData(doc, false); // Delete it!
}
// You could also change ".day" to ".hour".
// Example: if (now.day == DateTime.now().day && now.hour == DateTime.hour())
// So, if a meal is set for 2PM, it will delete at 2PM
return FutureBuilder<FirebaseUser>(
future: FirebaseAuth.instance.currentUser(),
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.hasData && snapshot != null) {
return Container(
margin: const EdgeInsets.all(8.0),
child: currentMail == doc.data['Author'] || // If the current mail is the author
List.from(doc.data['Authors']).contains(currentMail) // Or if the current mail is part of the authors
? Column( // then if true, show a Column
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'Meal:',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white),
textAlign: TextAlign.center,
),
Text(
'${doc.data['Meal']}',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white),
textAlign: TextAlign.center,
),
SizedBox(height: 20),
Text(
'When:',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white),
textAlign: TextAlign.center,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
onPressed: () => updateData(doc),
color: lightBlueColor,
icon: Icon(Icons.calendar_today,
color: Colors.white),
tooltip: 'Update Date',
),
Text(
formatted,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white),
textAlign: TextAlign.center,
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
SizedBox(width: 8),
FlatButton(
color: Colors.red,
onPressed: () => deleteData(doc, true),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadiusDirectional.circular(12)),
child: Row(children: <Widget>[
Text('Delete',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white)),
Icon(Icons.delete_forever, color: Colors.white),
]),
),
SizedBox(width: 8),
FlatButton(
color: Colors.blue,
onPressed: () => [
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
child: invite(doc),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(12)),
),
);
})
],
shape: RoundedRectangleBorder(
borderRadius:
BorderRadiusDirectional.circular(12)),
child: Row(children: <Widget>[
Text('Invite',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white)),
Icon(Icons.share, color: Colors.white),
]),
),
],
),
],
)
: Text(''), // if false, show an empty text widget
decoration: BoxDecoration(
color: lightBlueColor,
borderRadius: BorderRadius.all(Radius.circular(12)),
),
);
}
/*Navigator.pop(context);
return HomePage();*/
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: darkGreyColor,
body: ListView(
padding: EdgeInsets.only(top: 220),
children: <Widget>[
StreamBuilder<QuerySnapshot>(
stream: db
.collection('mealList')
.orderBy('Date', descending: false) // Order by Date, not descending
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: snapshot.data.documents
.map((doc) => buildItem(doc))
.toList());
} else {
return Container();
}
},
),
],
),
);
}
/*share(BuildContext context, DocumentSnapshot doc) {
final RenderBox box = context.findRenderObject();
final dynamic date = timeago.format(doc['Date'].toDate());
Share.share(
"${doc['Meal']} - $date",
subject: doc['Meal'],
sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size,
);
}*/
Widget invite(DocumentSnapshot doc) {
final _formKey = GlobalKey<FormState>();
return Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Center(
child: Text(
"Invite someone by mail",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
)),
SizedBox(
height: 24,
),
TextFormField(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12))),
labelText: 'Enter the email address'),
validator: (value) {
if (value.isEmpty) {
return 'Please enter an email address';
}
return null;
},
onSaved: (value) => mail = value,
),
FlatButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
saveInviteToFirestore(doc, mail);
}
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12))),
child: Text("Save"),
color: redColor,
textColor: Colors.white,
),
]),
),
);
}
Future<String> getCurrentUser() async {
return await FirebaseAuth.instance.currentUser().then((value) => value.uid);
}
void saveInviteToFirestore(DocumentSnapshot doc, String email) async {
final String user = await getCurrentUser();
var list = List<String>();
list.add(email);
Firestore.instance
.collection('mealList')
.document(doc.documentID)
.updateData({"Authors": FieldValue.arrayUnion(list)});
//setState(() => id = doc.documentID);
StatusAlert.show(
context,
duration: Duration(seconds: 2),
title: 'Added',
subtitle: 'You have Added your and the Date to your List',
configuration: IconConfiguration(icon: Icons.done),
);
//Navigator.pop(context);
}
void deleteData(DocumentSnapshot doc, bool showMessage) async {
await db.collection('mealList').document(doc.documentID).delete();
setState(() => id = null);
if (showMessage) {
StatusAlert.show(
context,
duration: Duration(seconds: 2),
title: 'Deleted',
subtitle: 'You have Deleted your Meal',
configuration: IconConfiguration(icon: Icons.delete),
);
}
}
void updateData(DocumentSnapshot doc) async {
await pickDate();
await db
.collection('mealList')
.document(doc.documentID)
.updateData({'Date': selectedDate});
StatusAlert.show(
context,
duration: Duration(seconds: 2),
title: 'Updated',
subtitle: 'You have updated your Meal Date',
configuration: IconConfiguration(icon: Icons.done),
);
}
}
Error
The following assertion was thrown building FutureBuilder<FirebaseUser>(dirty, state: _FutureBuilderState<FirebaseUser>#a4504):
A build function returned null.
The offending widget is: FutureBuilder<FirebaseUser>
Build functions must never return null.
To return an empty space that causes the building widget to fill available room, return "Container()". To return an empty space that takes as little room as possible, return "Container(width: 0.0, height: 0.0)".
The relevant error-causing widget was
FutureBuilder<FirebaseUser>
lib/…/MealPlan/mealTile.dart:92
When the exception was thrown, this was the stack
#0 debugWidgetBuilderValue.<anonymous closure>
package:flutter/…/widgets/debug.dart:276
In your FutureBuilder you are not returning anything when the Future hasn't completed yet. A widget always needs to be returned whether there is data or not.
Example fix for your code:
return FutureBuilder<FirebaseUser>(
future: FirebaseAuth.instance.currentUser(),
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.hasData && snapshot != null) {
return Container(
...
);
}
//ADDED ELSE BLOCK
else {
return Container();
}
}
);
Or as #stacker suggested, you can return a CircularProgressIndicator().

Initialize App using FutureBuilder with a ListView.builder and have an onClick in each ListItem?

Im building an App with Flutter and have a problem regarding the use of FutureBuilder. The situation is that my HomePage in the App should make a request to my server and get some Json. The call to the getData-Method happens in the build-method of the Homescreen (not sure if this is right).
The next call in the build-Method has the snapshot and builds a ListView.
Here is the problem:
Each time I click a button or go to a different screen, the Future Builder is triggered! That means that I have a bunch of useless API calls.
Here is the question:
What do I have to change, to let the Future Builder only run when I come to the Homescreen?
class HomeState extends State<Home> {
int count = 0;
final homeScaffoldKey = GlobalKey<ScaffoldState>();
List compList = new List();
Future<List> getData() async {
final response = await http.get(
Uri.encodeFull("http://10.0.2.2:5000/foruser"),
headers: {
"Authorization":
"JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NTk2NDM4ODcsImlhdCI6MTU1NzA1MTg4NywibmJmIjoxNTU3MDUxODg3LCJpZGVudGl0eSI6MX0.OhuUgX9IIYFX7u0o_6MXlrMYwk7oMCywlmHLw-vbNSY",
"charset": "utf-8"
},
);
if (response.statusCode == 200) {
compList = jsonDecode(response.body);
List<Comp> result = [];
count++;
for (var c in compList) {
Comp comp = Comp.fromJson(c);
result.add(comp);
}
return result;
} else {
throw Exception('Failed to load');
}
}
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.white10,
body: Stack(
children: <Widget>[
new Container(
child: FutureBuilder(
future: getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data == null) {
return new Container(
child: Text("whoops"),
);
}
if (snapshot.hasData) {
if (snapshot.data != null) {
if (snapshot.data.toString() == "[]") {
print("no comps - called API: $count");
return new ListView(
key: Key("1"),
children: <Widget>[
new Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
SizedBox(
height: 30.0,
),
Card(
color: Colors.blue,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: Text(
"Welcome, you have no comps",
style:
TextStyle(color: Colors.white),
),
),
],
),
),
],
),
],
);
}
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
print(index);
if (index == 0) {
return new Column(
children: <Widget>[
Card(
color: Colors.blue,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: Text(
"Welcome back, these are your comps",
style:
TextStyle(color: Colors.white),
),
),
],
),
),
SizedBox(
height: 10.0,
),
new CompListItem(
new Comp(
snapshot.data[index].left_name,
snapshot.data[index].right_name,
snapshot.data[index].left,
snapshot.data[index].right,
snapshot.data[index].left_city,
snapshot.data[index].right_city,
snapshot.data[index].latitude_left,
snapshot.data[index].longitude_left,
snapshot.data[index].latitude_right,
snapshot.data[index].longitude_right,
snapshot.data[index].points,
),
"left")
],
);
}
Comp tmp = new Comp(
snapshot.data[index].left_name,
snapshot.data[index].right_name,
snapshot.data[index].left,
snapshot.data[index].right,
snapshot.data[index].left_city,
snapshot.data[index].right_city,
snapshot.data[index].latitude_left,
snapshot.data[index].longitude_left,
snapshot.data[index].latitude_right,
snapshot.data[index].longitude_right,
snapshot.data[index].points,
);
return new CompListItem(tmp, "left");
},
);
} else if (snapshot.data == null) {
return new Container(
child: Text("Sorry, there seems to be a problem :("),
);
}
}
} else {
return CircularProgressIndicator();
}
},
),
),
],
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FloatingActionButton(
heroTag: null,
child: Icon(
Icons.add_location,
color: Colors.white,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MakeComp(),
),
);
},
backgroundColor: Colors.blue,
),
SizedBox(
height: 10.0,
),
FloatingActionButton(
heroTag: null,
child: Icon(Icons.compare_arrows),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => GetComps(),
),
);
},
backgroundColor: Colors.blue,
),
],
));
}
}
Actual results:
Open the App -> Future Builder runs -> List of data is shown -> Navigate to another Widget -> Future Builder runs -> click some button -> Future Builder runs
Expected results:
Open the App -> Future Builder runs -> List of data is shown -> Navigate to another Widget -> click some button -> do something on another screen -> return to homescreen -> Future Builder runs
And in the moment I posted this question, I found my answer :D
Thanks to Rémi Rousselet, who answerd this question here:
How to deal with unwanted widget build?
The answer was simply to put the call for the Future into the initState Method, which is exactly called, when I need the data to be loaded.
Happy Fluttering everyone!

Resources