简体   繁体   中英

Flutter: How to pass array data from json Future< to Widget

Being new to flutter, I'm learning and stumbling on the go.

I am trying to pass an array that I have received from json into an already waiting widget structure but can't quite seem to get the connection.

Here's the sample code:

class Products extends StatefulWidget {
  @override
  _ProductsState createState() => _ProductsState();
}

class _ProductsState extends State<Products> {
  @override
  void initState() {
    _getProducts();
  }

  Future<List<Single_prod>> _getProducts() async {
    var url = "";
    var data = await http.get(url);

    var jsonData = json.decode(data.body) as Map<String, dynamic>;
    //print(jsonData.toString());
    //jsonData.forEach((k, v) => print("Key : $k, Value : $v"));

    List<Single_prod> items = [];
    jsonData.forEach((k, v){
      Single_prod item = Single_prod(v["error"], v["id"], v["name"], v["price"], v["image"]);

      items.add(item);
    });
    //print(items.length);
    return items; <---Tring to pass this to Widget build but not recognized.....
  }

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
        itemCount: items.length,
        gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
        itemBuilder: (BuildContext context, int index){
          return Single_prod(
            prod_err: items[index]['error'], <--- This items array is not recognized
            prod_id: items[index]['id'],
            prod_name: items[index]['name'],
            prod_price: items[index]['price'],
            prod_image: items[index]['image'],
          );
        });
  }

}

The items array is not recognized in the widget

Here is the rest of the code:

class Single_prod extends StatelessWidget {
  final prod_err;
  final prod_id;
  final prod_name;
  final prod_price;
  final prod_image;

  Single_prod({
    this.prod_err,
    this.prod_id,
    this.prod_name,
    this.prod_price,
    this.prod_image,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Hero(
        tag: prod_name,
        child: Material(
          child: InkWell(
            onTap: () => Navigator.of(context).push(new MaterialPageRoute(
              // here we are passing the values of the products to the details page
                builder: (context) => new ProductDetails(
                  prod_detail_name: prod_name,
                  prod_detail_image: prod_image,
                  prod_detail_id: prod_id,
                  prod_detail_price: prod_price,
                ))),
            child: GridTile(
              footer: Container(
                height: 40.0,
                color: Colors.white70,
                child: ListTile(
                  leading: Text(prod_name, style: TextStyle(fontWeight: FontWeight.bold),),
                  title: Text(
                    prod_price,
                    style: TextStyle(color: Colors.blue, fontWeight: FontWeight.w800, fontSize: 12),
                  ),
                  /*subtitle: Text(
                    prod_oldprice,
                    style: TextStyle(color: Colors.black, fontWeight: FontWeight.w800, fontSize: 11, decoration: TextDecoration.lineThrough),
                  ),*/
                ),
              ),
              child: Image.asset(prod_image,
                fit: BoxFit.cover,),
            ),
          ),
        ),
      ),
    );
  }
}

How does the upper code connect with the lower code? Thanks in advance.

First, look at the scope of your 'items' variable: it is defined in getItems() function, and it is not visible outside the function. So, first thing: make it class level property.

Next - your initState will call your method. Method is async, and the way to handle it in initState to use '.then' on the Future returned by your method. What you want to do here is: once the future completes, you want to set your class level variable to hold the value returned by _getProduct() function.

And finally - this is very important to understand: you don't call build method yourself - flutter framework does it for you. Now, flutter does not have a magic way of knowing when you changed the data - it won't observe your code, so you need to tell it somehow that your state object changed, and it requires rebuild. You do it by calling setState() function.

I think you have another issue here actually: you already bulit your Single_prod widget in _getProduct(), no need to build it again. I tried to correct this also.

Try this (I didn't compile it so it might have few errors):

class Products extends StatefulWidget {
  @override
  _ProductsState createState() => _ProductsState();
}

class _ProductsState extends State<Products> {

  List<Single_prod> items = [];
  @override
  void initState() {
    super.initState();
    _getProducts().then( (result) {
        setState(() {
           items=result;
        }
    });
  }

  Future<List<Single_prod>> _getProducts() async {
    var url = "";
    var data = await http.get(url);

    var jsonData = json.decode(data.body) as Map<String, dynamic>;
    //print(jsonData.toString());
    //jsonData.forEach((k, v) => print("Key : $k, Value : $v"));

    List<Single_prod> items = [];
    jsonData.forEach((k, v){
      Single_prod item = Single_prod(v["error"], v["id"], v["name"], v["price"], v["image"]);

      items.add(item);
    });
    //print(items.length);
    return items; 
  }

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
        itemCount: items.length,
        gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
        itemBuilder: (BuildContext context, int index){
          return items[index];
        });
  }

}

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