简体   繁体   English

如何将卡片轮播的硬编码数据更改为来自 api 响应的动态数据 -Flutter

[英]How can change my hardcoded data of a card carousel to dynamic data from api response -Flutter

Good day.再会。 Please I am have a challenge on how to change my hardcoded data to dynamic from the api.请我在如何将我的硬编码数据从 api 更改为动态方面遇到挑战。 The response to the API returns success but populating the card carousel is the challenge I am having.对 API 的响应返回成功,但填充卡片轮播是我面临的挑战。 I keep on getting error "type 'List' is not a subtype of type 'List<Map<String, String>>'"'我不断收到错误“类型 'List' 不是类型 'List<Map<String, String>>'”' 的子类型

This my Api request:这是我的 Api 请求:

  SharedPreferences _prefs;

  void getCategories() async {

    try {
      _prefs = await SharedPreferences.getInstance();
      var _categoryService = CategoryService();
      var result =
          await _categoryService.getCategories(_prefs.getString('token'));
      var categories = json.decode(result.body);

      print('categories');
      print(categories);

      List<Map<String, String>> foodCategories = categories;

    } catch (e) {
      throw Exception();
    }
  }

This is my list variable with hard coded data:这是我的带有硬编码数据的列表变量:

 final List<Map<String, String>> foodCategories = [
    {
      'name': 'Rice Planting',
      'image': 'images/Icon-001.png',
    },
    {
      'name': 'Harvesting',
      'image': 'images/Icon-002.png',
    },
    {
      'name': 'Machineries',
      'image': 'images/Icon-003.png',
    },
    {
      'name': 'Rice Products',
      'image': 'images/Icon-004.png',
    }
  ];

And this is my screen:这是我的屏幕:

  Container(
              height: 105,
              margin: const EdgeInsets.only(
                top: 20.0,
                bottom: 25.0,
              ),
              child: ListView.builder(
                  scrollDirection: Axis.horizontal,
                  padding: const EdgeInsets.only(
                    left: 20.0,
                  ),
                  itemCount: this.foodOptions.length,
                  itemBuilder: (context, index) {
                    Map<String, String> option = this.foodOptions[index];
                    return Container(
                      margin: const EdgeInsets.only(right: 35.0),
                      child: Column(
                        children: <Widget>[
                          Container(
                            width: 70,
                            height: 70,
                            margin: const EdgeInsets.only(bottom: 10.0),
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.all(
                                Radius.circular(5.0),
                              ),
                              image: DecorationImage(
                                image: AssetImage(
                                  option['image'],
                                ),
                              ),
                              boxShadow: [
                                BoxShadow(
                                  blurRadius: 10.0,
                                  color: Colors.grey[300],
                                  offset: Offset(6.0, 6.0),
                                )
                              ],
                            ),
                          ),
                          Text(
                            option['name'],
                            style: TextStyle(fontSize: 17.0),
                          ),
                        ],
                      ),
                    );
                  }),
            ),
          

My basic challenge is how to switch this hardcoded data of **foodCategories ** to我的基本挑战是如何将 **foodCategories ** 的硬编码数据切换到

 var categories = json.decode(result.body); which is from the API. any suggestion I will highly appreciate. The Api returns a json with image and category name

this is the UI picture这是用户界面图片

在此处输入图片说明

Declare this List in your screen widget:在您的屏幕小部件中声明此列表:

final List<Map<String, String>> foodCategories = [
    {
      'name': 'Rice Planting',
      'image': 'images/Icon-001.png',
    },
    {
      'name': 'Harvesting',
      'image': 'images/Icon-002.png',
    },
    {
      'name': 'Machineries',
      'image': 'images/Icon-003.png',
    },
    {
      'name': 'Rice Products',
      'image': 'images/Icon-004.png',
    }
  ];

Add this method to your screen widget:将此方法添加到您的屏幕小部件:

void getCategories() async {

    try {
      _prefs = await SharedPreferences.getInstance();
      var _categoryService = CategoryService();
      var result =
          await _categoryService.getCategories(_prefs.getString('token'));
      var categories = json.decode(result.body);

      print('categories');
      print(categories);

      setState(() {
          foodCategories = categories;
      });
      

    } catch (e) {
      throw Exception();
    }
  }

and call the method in initState:并在 initState 中调用该方法:

@override
  void initState() {
    super.initState();
getCategories();
  }

You can use a state management tool for this use case, like Provider, on flutter.dev there is a nice explanation on how to use it Simple app state management .您可以为此用例使用状态管理工具,例如 Provider,在 flutter.dev 上有一个关于如何使用它的很好的解释Simple app state management

Basically you wrap the api call inside a provider class and every time data comes from the api it notify the consumers that are using the provider.基本上,您将 api 调用包装在提供者类中,每次数据来自 api 时,它都会通知正在使用提供者的消费者

Steps:脚步:

1 - Add the provider dependency to your pubspec.yaml. 1 - 将提供程序依赖项添加到您的 pubspec.yaml。

name: my_name
description: Blah blah blah.

# ...

dependencies:
  flutter:
    sdk: flutter

  provider: ^6.0.0

dev_dependencies:
  # ...

2 - Create a provider for your desire state, in this case the list of categories 2 - 为您的愿望状态创建一个提供程序,在本例中为类别列表

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../category_service.dart';

class CategoriesProvider with ChangeNotifier {
  List<Map<String, String>> foodCategories = [];
  bool loading = false;

  var _prefs;

  void getCategories() async {
    try {
      loading = true;

      _prefs = await SharedPreferences.getInstance();
      var _categoryService = CategoryService();
      var result =
      await _categoryService.getCategories(_prefs.getString('token'));
      var categories = json.decode(result.body);

      print('categories');
      print(categories);

      List<Map<String, String>> foodCategories = categories;

      loading = false;
      notifyListeners();
    } catch (e) {
      throw Exception();
    }
  }
}

3 - Create an instance of CategoriesProvider and add it to the widget tree near to the desired carrossel widget, you can read more about where to insert the provider on this link https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple 3 - 创建 CategoriesProvider 的实例并将其添加到所需 carrossel 小部件附近的小部件树中,您可以在此链接上阅读有关在何处插入提供程序的更多信息https://flutter.dev/docs/development/data-and-后端/状态管理/简单

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CategoriesProvider (),
      child: const MyApp(),
    ),
  );
}

4 - In the stateful widget you need to create a instance of the CategoriesProvider to retrieve the data from the api. 4 - 在有状态小部件中,您需要创建 CategoriesProvider 的实例以从 api 检索数据。

class _MyApp extends State<MyApp> {
  late CategoriesProvider categoriesProvider;
  @override
  void initState() {
    categoriesProvider = Provider.of(context, listen: false);
    categoriesProvider.getCategories();
    
    super.initState();
  }

5 - On the screen widget you can wrap your widget with a Consumer widget to retrieve the data from the provider, replacing on your example the this.foodOptions with the categories defined early on the provider class. 5 - 在屏幕小部件上,您可以使用消费者小部件包装您的小部件以从提供者检索数据,在您的示例中将 this.foodOptions 替换为提供者类早期定义的类别。

Consumer<CategoriesProvider>(
        builder: (context, provider, _) => Container(
          height: 105,
          margin: const EdgeInsets.only(
            top: 20.0,
            bottom: 25.0,
          ),
          child: ListView.builder(
              scrollDirection: Axis.horizontal,
              padding: const EdgeInsets.only(
                left: 20.0,
              ),
              itemCount: provider.foodCategories.length,
              itemBuilder: (context, index) {
                Map<String, String> option = provider.foodCategories[index];
                return Container(
                  margin: const EdgeInsets.only(right: 35.0),
                  child: Column(
                    children: <Widget>[
                      Container(
                        width: 70,
                        height: 70,
                        margin: const EdgeInsets.only(bottom: 10.0),
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.all(
                            Radius.circular(5.0),
                          ),
                          image: DecorationImage(
                            image: AssetImage(
                              option['image'],
                            ),
                          ),
                          boxShadow: [
                            BoxShadow(
                              blurRadius: 10.0,
                              color: Colors.grey[300],
                              offset: Offset(6.0, 6.0),
                            )
                          ],
                        ),
                      ),
                      Text(
                        option['name'],
                        style: TextStyle(fontSize: 17.0),
                      ),
                    ],
                  ),
                );
              }),
        ),
      ),

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

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