FutureBuilder doesn't update after failure recieved via try/catch

81 Views Asked by At

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!!');
    }
  }
2

There are 2 best solutions below

0
On BEST ANSWER

To notify the framework, that the state of your widget has changed, you can use the setState method of StatefulWidget.

So this:

futureOfSubmit = productsProvider.editProduct(_formProduct);

should be:

setState(() { futureOfSubmit = productsProvider.editProduct(_formProduct); });

And the others accordingly.

0
On

use

setState((){});

to notify the framework ,that the state has changed.

use setState in your If else block

setState(() {
futureOfSubmit = productsProvider.editProduct(_formProduct);
 });