简体   繁体   中英

Unhandled exception in FutureBuilder Flutter

i try to use FutureBuilder widget in flutter to get some data from network. for this i use below code :

Future<List<Product>> getWishList() async {
http.Response response = await http.post(
  MY_URL,
  headers: {
    HttpHeaders.acceptHeader: 'application/json',
    HttpHeaders.contentTypeHeader: 'application/json; charset=utf-8'
  },
);
if (response.statusCode == 200) {
  List<Product> ret = List();
  Map<String, dynamic> result;
  try {
    result = json.decode(response.body);
    for (var i = 0; i < result['data'].length; i++) {
        ret.add(Product.fromJson(result['data'][i]));
    }
    return ret;
  } catch (e) {
    return throw Exception("Json parse error");
  }
} else {
  return throw Exception("network connection failed");
}
}

AND:

FutureBuilder(
            future: getWishList(),
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              if(snapshot.connectionState == ConnectionState.done){
                  if(snapshot.hasError){
                    controller.forward(from: 0.0);
                    return Material(
                      child: Container(
                        margin: const EdgeInsets.only(top: 36.0),
                        width: double.infinity,
                        child: FadeTransition(
                          opacity: animation,
                          child: PButton(
                            onPressed: (){
                              setState(() {
                                getWishList();
                              });
                            },
                            child: Column(
                              children: <Widget>[
                                Icon(Icons.perm_scan_wifi,color: Colors.black,size: 76.0,),
                                SizedBox(height:24.0),
                                Text("Try again",style: TextStyle(fontSize: 16.0,color: const Color(0XFF222222)),),

                              ],
                            ),
                          ),
                        ),
                      ),
                    );
                  }else{
                    return new ListView(
                        children: <Widget>[
                          GestureDetector(
                              onTap:(){
                                setState(() {
                                  getWishList();
                                });
                              },
                              child: new Text("Every thing ok"))
                        ]);
                  }
              }else{
                return Center(
                  child: Container(
                      margin: const EdgeInsets.only(top: 36.0),
                      child: CircularProgressIndicator()),
                );
              }
            })

now if http response return error in first time every thing good but with click on Try again and if error again this message display on console:

[VERBOSE-2:shell.cc(184)] Dart Error: Unhandled exception: Exception: network connection failed _WishListState.getWishList (package:parchino/screen/screen_wish_list.dart:127:14) _WishListState.build... (package:parchino/screen/screen_wish_list.dart:65:33) State.setState (package:flutter/src/widgets/framework.dart:1130:30) _WishListState.build.. (package:parchino/screen/screen_wish_list.dart:64:31) GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:102:24) TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:242:9) TapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:175:7) PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:315:9) PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart<…>

When you call setState , you mark the widget as Dirty and basically tells the framework to rebuild it but only after getWishList has been called (the one inside setState ). As it is an async method, it launches quickly and rebuild the widget.

By rebuilding the widget, you rebuild the FutureBuilder which tries to evaluate its future. As the future is a function, it calls it and makes a new call to getWishList .

That makes two calls to the same method and thus two calls to an http server very quickly. Those calls are probably in conflict, and throws an error.

You should not invoke a Future directly in the FutureBuilder but use a previously-obtained Future instead.

Future<List<Product>> myFuture;

@override
void initState() {
  myFuture = getWishList();
  super.initState();
}

Future<List<Product>> getWishList() async {
 //Do your stuff
}

Then in your build, set myFuture as the future of the FutureBuilder and in your setState , set myFuture again:

FutureBuilder(
  future: myFuture,
  builder: (BuildContext context, AsyncSnapshot snapshot) {
      //...
      setState(() {
        myFuture = getWishList();
      });
      //...
  }
);

That will make the setState set a new future in myFuture and ask the widget to rebuilt itself. As the FutureBuilder rebuild, it evaluates myFuture instead of calling http once again.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM