I have FutureBuilder which takes future coming from HTTP GET request. Future object is initialized on tap of submit button, that too is based on isInEditMode bool.
- First time I tap on Submit, it's connectionState goes from none to waiting to done as expected.
- Lets say future catches error from Get request and I can thow it inside catch block gracefully, I get connectionState.done which is good.
- Now the problem comes on second tap of submit button, which doesnt update future's connectionState to waiting and in turn, builder doesnt show accordingly on screen.
- I have checked, everytime I tap on Submit button, brand new future object is retrieved from Get request, but it doesn't reflect in FutureBuilder. How to let FutureBuilder know that Future object is renewed/changed, adapt to the new one??
class AddEditProductSoScreen extends StatefulWidget {
static const routeName = '/add-edit-product';
@override
_AddEditProductSoScreenState createState() => _AddEditProductSoScreenState();
}
class _AddEditProductSoScreenState extends State<AddEditProductSoScreen> {
var isEditMode = false;
final _formKey = GlobalKey<FormState>();
ProductFormModel _formProduct;
Future<Response> futureOfSubmit;
bool isLoadingVisible = false;
@override
Widget build(BuildContext context) {
final productsProvider =
Provider.of<ProductsProvider>(context, listen: false);
Product product = ModalRoute.of(context).settings.arguments;
if (product != null) {
isEditMode = true;
_formProduct = ProductFormModel.copyFromProduct(product);
} else {
_formProduct = ProductFormModel.init();
}
return Scaffold(
bottomNavigationBar: Container(
height: 50,
child: Material(
color: Theme.of(context).accentColor,
child: InkWell(
onTap: isLoadingVisible
? null
: () {
if (isEditMode) {
futureOfSubmit =
productsProvider.editProduct(_formProduct);
} else {
futureOfSubmit =
productsProvider.addProduct(_formProduct);
}
isLoadingVisible = true;
futureOfSubmit.then((value) {
//Navigator.of(context).pop();
}).catchError((error) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('An error occurred!'),
content: Text('Something went wrong.'),
actions: [
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Okay'))
],
));
}).whenComplete(() {
setState(() {
isLoadingVisible = false;
});
});
},
child: FutureBuilder(
future: futureOfSubmit,
builder: (context, AsyncSnapshot<Response> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Align(
alignment: Alignment.center,
child: Container(
height: 30,
width: 30,
child: CircularProgressIndicator(
backgroundColor: Colors.white,
)),
);
} else {
return Container(
padding: EdgeInsets.all(10),
width: double.infinity,
child: Text(
'SUBMIT',
style: TextStyle(color: Colors.white, fontSize: 20),
textAlign: TextAlign.center,
),
);
}
},
),
),
),
),
);
}
}
Any help would be appreciated, thanks!
Edit: here is addProduct method of productProvider class which gives back future object and do throw the exception as well..
Future<Response> addProduct(ProductFormModel product) async {
const url = '................';
try {
Response response = await http.post(url,
body: json.encode({
'title': product.title,
'description': product.description,
'imageUrl': product.imageUrl,
'price': product.price,
'isFavorite': product.isFav
}));
Map respMap = json.decode(response.body);
product.id = respMap['name'];
_items.add(product.toProduct());
notifyListeners();
return response;
} on Exception catch (error) {
print("error is :: " + error.toString());
throw Exception('Something went wrong!!');
}
}
To notify the framework, that the state of your widget has changed, you can use the
setState
method ofStatefulWidget
.So this:
should be:
And the others accordingly.