【问题标题】:How to create a gridview within a listview by flutter with json API如何使用 json API 在列表视图中创建网格视图
【发布时间】:2019-02-28 04:57:55
【问题描述】:

我想创建一个购物车应用

我有问题

如何使用 JSON API 在 ListView 中创建 GridView

我希望它完全像这个例子:

https://i.stack.imgur.com/2KQFG.png

https://i.stack.imgur.com/I0gY8.gif

---更新----

关于 SliverGrid

我尝试获取产品但出现错误(这是关于 SliverGrid 部分)

I/flutter ( 5992): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 5992): The following assertion was thrown building FutureBuilder<List<dynamic>>(dirty, state:
I/flutter ( 5992): _FutureBuilderState<List<dynamic>>#78747):
I/flutter ( 5992): A build function returned null.
I/flutter ( 5992): The offending widget is: FutureBuilder<List<dynamic>>
I/flutter ( 5992): Build functions must never return null. To return an empty space that causes the building widget to
I/flutter ( 5992): fill available room, return "new Container()". To return an empty space that takes as little room as
I/flutter ( 5992): possible, return "new Container(width: 0.0, height: 0.0)".

关于 ListView(它工作正常)..

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';

Future<List<dynamic>> getCategoriesApi() async {
  http.Response response1 =
      await http.get("http://159.89.228.206/");
  Map<String, dynamic> decodedCategories = json.decode(response1.body);
  //print(response1);
  return decodedCategories['categories'];
}

Future<List<dynamic>> getProductsApi() async {
  http.Response response =
      await http.get("http://159.89.228.206/");
  Map<String, dynamic> decodedCategories2 = json.decode(response.body);
  // print(response);
  return decodedCategories2['last'];
}

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(title: 'Sliver Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final ScrollController _scrollController = new ScrollController();

  List<dynamic> products;
  List<dynamic> categories;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text(widget.title),
        ),
        body: Column(children: <Widget>[
          Expanded(
            child: CustomScrollView(
              controller: _scrollController,
              slivers: <Widget>[
                SliverToBoxAdapter(
                  child: SizedBox(
                    height: 120.0,
                    child: FutureBuilder(
                        future: getCategoriesApi(),
                        builder: (BuildContext context,
                            AsyncSnapshot<List<dynamic>> snapshot) {
                          if (snapshot.connectionState ==
                              ConnectionState.done) {
                            return ListView.builder(
                              scrollDirection: Axis.horizontal,
                              itemBuilder: (context, index) {
                                Map<String, String> category =
                                    snapshot.data[index].cast<String, String>();
                                return Card(
                                  child: Container(
                                    height: double.infinity,
                                    color: Colors.grey[200],
                                    child: Center(
                                      child: Padding(
                                        padding: EdgeInsets.all(30.0),
                                        child: Text(category["name"]),
                                      ),
                                    ),
                                  ),
                                );
                              },
                              itemCount: snapshot.data.length,
                            );
                          } else {
                            return Center(child: CircularProgressIndicator());
                          }
                        }),
                  ),
                ),
                SliverToBoxAdapter(
                  child: Container(
                    child: FutureBuilder(
                        future: getProductsApi(),
                        builder: (BuildContext context,
                            AsyncSnapshot<List<dynamic>> snapshot) {
                          if (snapshot.connectionState ==
                              ConnectionState.done) {
                            SliverGrid(
                              gridDelegate:
                                  SliverGridDelegateWithFixedCrossAxisCount(
                                crossAxisCount: 2,
                                childAspectRatio: 0.8,
                              ),
                              delegate: SliverChildBuilderDelegate(
                                (context, index) {
                                  Map<String, String> product = snapshot
                                      .data[index]
                                      .cast<String, String>();

                                  return Card(
                                    child: Container(
                                      height: double.infinity,
                                      color: Colors.grey[200],
                                      child: Center(
                                        child: Padding(
                                          padding: EdgeInsets.all(30.0),
                                          child: Text(product["name"]),
                                        ),
                                      ),
                                    ),
                                  );
                                },
                                childCount: snapshot.data.length,
                              ),
                            );
                          } else {
                            return Center(child: CircularProgressIndicator());
                          }
                        }),
                  ),
                ),
              ],
            ),
          )
        ]));
  }
}

【问题讨论】:

    标签: json api dart flutter


    【解决方案1】:

    您不能将GridView 直接嵌入到ListView 中,除非您使用为GridView 保留的高度。如果您想保持图像中显示的两个部分的滚动,最好使用CustomScrollViewSlivers

    图片之后是我的提议。

    import 'dart:convert';
    
    import 'package:flutter/material.dart';
    
    String productsJson =
        '{"last": [{"product_id":"62","thumb":"sandwich.png","name":"Test Tilte","price":"\$55.00"}, '
        '{"product_id":"61","thumb":"sandwich.png","name":"Test Tilte","price":"\$55.00"}, '
        '{"product_id":"57","thumb":"sandwich.png","name":"Test Tilte","price":"\$55.00"}, '
        '{"product_id":"63","thumb":"sandwich.png","name":"Test Tilte","price":"\$55.00"}, '
        '{"product_id":"64","thumb":"sandwich.png","name":"Test Tilte","price":"\$55.00"}, '
        '{"product_id":"58","thumb":"sandwich.png","name":"Test Tilte","price":"\$55.00"}, '
        '{"product_id":"59","thumb":"sandwich.png","name":"Test Tilte","price":"\$55.00"}]}';
    
    String categoriesJson = '{"categories":['
        '{"name":"Category 1","image":"icon.png","id":2}, '
        '{"name":"Category 2","image":"icon.png","id":4}, '
        '{"name":"Category 3","image":"icon.png","id":4}, '
        '{"name":"Category 4","image":"icon.png","id":4}, '
        '{"name":"Category 5","image":"icon.png","id":6}]}';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: MyHomePage(title: 'Sliver Demo'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      final ScrollController _scrollController = ScrollController();
    
      List<dynamic> products;
      List<dynamic> categories;
    
      @override
      initState() {
        super.initState();
    
        Map<String, dynamic> decoded = json.decode(productsJson);
        products = decoded['last'];
    
        Map<String, dynamic> decodedCategories = json.decode(categoriesJson);
        categories = decodedCategories['categories'];
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: CustomScrollView(
            controller: _scrollController,
            slivers: <Widget>[
              SliverToBoxAdapter(
                child: SizedBox(
                  height: 120.0,
                  child: ListView.builder(
                    scrollDirection: Axis.horizontal,
                    itemBuilder: (context, index) {
                      Map<String, String> category =
                          categories[index].cast<String, String>();
                      return Card(
                        child: Container(
                          height: double.infinity,
                          color: Colors.grey[200],
                          child: Center(
                            child: Padding(
                              padding: EdgeInsets.all(30.0),
                              child: Text(category["name"]),
                            ),
                          ),
                        ),
                      );
                    },
                    itemCount: categories.length,
                  ),
                ),
              ),
              SliverGrid(
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  childAspectRatio: 0.8,
                ),
                delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) {
                    Map<String, String> product =
                        products[index].cast<String, String>();
                    return Card(
                      child: Container(
                        color: Colors.grey[400],
                        child: Padding(
                          padding: EdgeInsets.symmetric(vertical: 30.0),
                          child: Center(
                              child: Text("Product ${product["product_id"]}")),
                        ),
                      ),
                    );
                  },
                  childCount: products.length,
                ),
              ),
            ],
          ),
        );
      }
    }
    

    如果您想从网络中检索 json,您可以添加/替换以下代码。添加一个返回Future 的方法,然后使用FutureBuilder 构建ListView

    ....
    import 'package:http/http.dart' as http;
    import 'dart:async';
    ....
          Future<List<dynamic>> getCategories() async {
            http.Response response = await http.get("http://159.89.228.206");
            Map<String, dynamic> decodedCategories = json.decode(response.body);
            return decodedCategories['categories'];
          }
        ...
        ...
                  SliverToBoxAdapter(
                    child: SizedBox(
                      height: 120.0,
                      child: FutureBuilder(
                          future: getCategories(),
                          builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) {
                            if (snapshot.connectionState == ConnectionState.done) {
                              return ListView.builder(
                                scrollDirection: Axis.horizontal,
                                itemBuilder: (context, index) {
                                  Map<String, String> category =
                                      snapshot.data[index].cast<String, String>();
                                  return Card(
                                    child: Container(
                                      height: double.infinity,
                                      color: Colors.grey[200],
                                      child: Center(
                                        child: Padding(
                                          padding: EdgeInsets.all(30.0),
                                          child: Text(category["name"]),
                                        ),
                                      ),
                                    ),
                                  );
                                },
                                itemCount: snapshot.data.length,
                              );
                            } else {
                              return Center(child: CircularProgressIndicator());
                            }
                          }),
                    ),
                  ),
        ....
    

    【讨论】:

    • 你能解释一下它们是如何从 http url 带来的 使用这个 api :159.89.228.206 非常感谢 :)
    • 我添加了一些代码来从网络检索到的 json 构建类别列表。
    • 谢谢 :) .. 问问你为什么第一次使用 initState .. 在这段代码中使用 initState 有什么好处...
    • initState 用于在创建 State 对象后立即执行操作。在某种程度上就像在构造函数中进行初始化,但在这里它更安全,因为您现在对象已完全创建。对于网络部分,它没有在 initState 中初始化,因为未来是在构建方法中调用的。但是,如果您在 FutureBuilder 小部件中挖掘一点,您会发现 Future 在 FutureBuilder 自己的 initState 方法中被监听。
    • 首先再次感谢您,关于SliverGrid 我尝试获取产品但出现错误(注意:问题已在顶部修改)我添加了完整的代码去请上一页..
    【解决方案2】:

    这个问题的简单答案是 Tabs 。 根据您的类别动态渲染选项卡,并在 TabView 中渲染 GridView。

    这是输出:

    这是代码:

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Shopping App',
          theme: ThemeData(
            primarySwatch: Colors.orange,
          ),
          home: ShowProductScreen(),
        );
      }
    }
    
    class Product {
      String productId;
      String image;
      String name;
      String price;
    
      Product({this.productId, this.image, this.name, this.price});
    }
    
    class Category {
      String id;
      String name;
      String image;
    
      List<Product> productList;
    
      Category({this.id, this.name, this.image, this.productList});
    }
    
    class ShowProductScreen extends StatefulWidget {
      @override
      _ShowProductScreenState createState() => _ShowProductScreenState();
    }
    
    class _ShowProductScreenState extends State<ShowProductScreen> with TickerProviderStateMixin {
      List<Category> categoryList = List();
    
      TabController _tabController;
    
      @override
      void initState() {
        super.initState();
    
        //Add data
    
        for (int i = 0; i < 10; i++) {
          List<Product> productList = List();
          for (int j = 0; j < 50; j++) {
            Product product = Product(
              productId: "$i-$j",
              price: "${(j + 1) * 10}",
              name: "Product $i-$j",
              image: "assets/image.jpg",
            );
    
            productList.add(product);
          }
    
          Category category = Category(
            id: "$i",
            name: "Category $i",
            image: "assets/image.jpg",
            productList: productList,
          );
    
          categoryList.add(category);
        }
    
        _tabController = TabController(vsync: this, length: categoryList.length);
      }
    
      @override
      void dispose() {
        super.dispose();
        _tabController.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            titleSpacing: 0.0,
            title: IconButton(
              icon: Icon(
                Icons.shopping_cart,
              ),
              onPressed: () {},
            ),
            actions: <Widget>[
              IconButton(
                icon: Icon(
                  Icons.menu,
                ),
                onPressed: () {},
              )
            ],
          ),
          body: Column(
            children: <Widget>[
              Container(
                height: 100.0,
                child: TabBar(
                  controller: _tabController,
                  isScrollable: true,
                  tabs: categoryList.map((Category category) {
                    return CategoryWidget(
                      category: category,
                    );
                  }).toList(),
                ),
              ),
              Expanded(
                child: Container(
                  padding: EdgeInsets.all(5.0),
                  child: TabBarView(
                    controller: _tabController,
                    children: categoryList.map((Category category) {
                      return Container(
                        child: GridView.count(
                          crossAxisCount: 2,
                          childAspectRatio: 4 / 3,
                          controller: ScrollController(keepScrollOffset: false),
                          scrollDirection: Axis.vertical,
                          children: category.productList.map(
                            (Product product) {
                              return ProductWidget(
                                product: product,
                              );
                            },
                          ).toList(),
                        ),
                      );
                    }).toList(),
                  ),
                ),
              )
            ],
          ),
        );
      }
    }
    
    class CategoryWidget extends StatelessWidget {
      final Category category;
    
      const CategoryWidget({Key key, this.category}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.all(4.0),
                child: Image(
                  image: AssetImage(category.image),
                  height: 60.0,
                ),
              ),
              Text(category.name)
            ],
          ),
        );
      }
    }
    
    class ProductWidget extends StatelessWidget {
      final Product product;
    
      const ProductWidget({Key key, this.product}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Container(
          margin: EdgeInsets.all(4.0),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.all(
              Radius.circular(8.0),
            ),
            border: Border.all(
              color: Colors.orange,
            ),
          ),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              Image(
                image: AssetImage(product.image),
                fit: BoxFit.contain,
                height: 80.0,
              ),
              Text(product.name)
            ],
          ),
        );
      }
    }
    

    希望对你有帮助!

    【讨论】:

    • 我和你的想法一样im5.ezgif.com/tmp/ezgif-5-3dd5b743dd.webp我想要一些不同的东西,请看我需要的图片:i.stack.imgur.com/I0gY8.gif
    • 注意:你的想法很好..我会用在别的东西上..但是怎么用json调用呢? {"categories":[ {"name":"Category 1","image":"icon.png","id":2}, {"name":"Category 3","image":"icon. png","id":4}, {"name":"Category 5","image":"icon.png","id":6} ] }
    猜你喜欢
    • 2021-11-02
    • 1970-01-01
    • 1970-01-01
    • 2017-10-02
    • 1970-01-01
    • 1970-01-01
    • 2019-03-20
    • 1970-01-01
    • 2013-11-16
    相关资源
    最近更新 更多