Sermed mayi
Sermed mayi

Reputation: 757

Flutter : why future builder run more than one time

I have page contain future builder to get username from shared preference :

 Widget build(BuildContext context) {
    return Consumer<ProductProvider>(builder: (context, prod, child) {
      return FutureBuilder(
          future: Services().get_user(),
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              var name = snapshot.data['name'];
              return OrderDetails(name);
            } else {
              return Container();
            }
          });
    });
  }

that page have widget contain another future builder depend on name value passed through page :

class OrderDetails extends StatelessWidget {
  final name;
  const OrderDetails(this.name);
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: Services().get_orders(name),
      builder: (context, snapshot) {
        if (snapshot.data != null) {
          print('snapshot.data.length  ${snapshot.data.length}'); // ------ it is print 6
          return ListView.builder(
            itemCount: snapshot.data.length,
            itemBuilder: (context, index) {
              return Text('user_id');
            },
          );
        } else {
          return Container();
        }
      },
    );
  }

Services().get_orders(name) method :

get_orders(name) async {
    var url = 'http://10.0.2.2:8000/api/user/myorder/$name';
    var response = await http.get(url);
    var data = jsonDecode(response.body);
    return (data);
  }

I put data on database which is just this:

{
  "id": 3,
  "user_id": 24,
  "status": "ordered",
  "order_item": [
    {
      "id": 11,
      "order_id": 3,
      "product_id": 1,
      "quantity": 2,
    },
    {
      "id": 12,
      "order_id": 3,
      "product_id": 2,
      "quantity": 1,
    }
  ]
}

as a result ,it run 6 times ,and print 'user_id'

Upvotes: 3

Views: 1137

Answers (1)

Mike
Mike

Reputation: 489

because it is used inside the build() method. That's not a problem...if you do not evaluate the Future method inside that. I mean, you should not do this:

Widget build(BuildContext context) {
    return FutureBuilder(
      future: Services().get_orders(name),
      builder: (context, snapshot) {
 .....

but this:

Widget build(BuildContext context) {
    return FutureBuilder(
      future: _myFuture,
      builder: (context, snapshot) {
 .....

Where _myFuture is evaluated at initState():

class OrderDetails extends StatefullWidget {
@override
_OrderDetailsState createState() => _OrderDetailsState();

}
class _OrderDetailsState extends State<OrderDetails> {

  final name;
  
  Future _myFuture;

  @override
  void initState() {
     name = widget.name;
     _myFuture = Services().get_orders(name); //this method is async, thus do not slow down the init state: the result is still not ready.
     super.initState();
   }


  const OrderDetails(this.name);
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _myFuture,  //the FutureBuilder will wait until the _myFuture will get the results from Services().get_orders(...)
      builder: (context, snapshot) {
        if (snapshot.hasFinished ) { //better test: data is always not null. at least with zero data.
          print('snapshot.data.length  ${snapshot.data.length}'); // ------ it is print 6
          return ListView.builder(
            itemCount: snapshot.data.length,
            itemBuilder: (context, index) {
              return Text('user_id');
            },
          );
        } else {
          return CircularProgressIndicator(); // a circular progress indicator is the quickest way to tell the user we are waiting for data.
        }
      },
    );
  }

Upvotes: 3

Related Questions