简体   繁体   English

如何使用JSON API在Flutter中在列表视图中创建gridview

[英]How to create a gridview within a listview by flutter with json API

I'd like to create a shopping cart app 我想创建一个购物车应用

I had a problem 我有问题

How to create a GridView within a ListView by flutter with JSON API 如何使用JSON API通过Flutter创建ListView中的GridView

I want it exactly like this Example : 我想要它完全像下面的示例:

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

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

--- Update ---- -更新-

About SliverGrid 关于SliverGrid

I tried to fetch the products but an error appeared (This is regarding the SliverGrid part) 我试图获取产品,但是出现了错误(这与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)".

With regard to ListView (It works fine).. 关于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());
                          }
                        }),
                  ),
                ),
              ],
            ),
          )
        ]));
  }
}

You can't embed a GridView directly in a ListView unless you play with the height reserved for the GridView . 除非您使用为GridView保留的高度,否则您不能直接将GridView嵌入到ListView If you want to maintain the scroll for both sections as you show in your images, the best thing is to use a CustomScrollView and Slivers . 如果您希望在图像中显示时都保持两个部分的滚动,最好的方法是使用CustomScrollViewSlivers

After the image is my proposal. 图片是我的建议。

在此处输入图片说明

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,
            ),
          ),
        ],
      ),
    );
  }
}

If you want to retrieve the json from the network you can add/replace the following code. 如果要从网络检索json,则可以添加/替换以下代码。 Add a method that returns a Future and then build the ListView using a FutureBuilder . 添加一个返回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());
                        }
                      }),
                ),
              ),
    ....

The simple answer to this would be Tabs . 一个简单的答案就是Tabs Render Tabs dynamically as per your category and under render GridView in TabView. 根据您的类别以及在TabView中的渲染GridView下动态地渲染选项卡。

Here is Output : 这是输出:

在此处输入图片说明

Here is the Code : 这是代码:

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)
        ],
      ),
    );
  }
}

Hope it Helps! 希望能帮助到你!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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