【问题标题】:Flutter: How to filter data from JSON based on TabBar and ToggleButtons?Flutter:如何根据 TabBar 和 ToggleButtons 从 JSON 中过滤数据?
【发布时间】:2020-06-23 17:29:07
【问题描述】:

我有一个这样的 JSON:

[
  {
    "continentName": "NA",
    "isDayTime": true,
    "seasonName": "Spring",
    "cityName": "United States",
    "xAlign": 45.4,
    "yAlign": 69,
    "cityTemperature": 27
  },
  {
    "continentName": "NA",
    "isDayTime": true,
    "seasonName": "Spring",
    "cityName": "Canada",
    "xAlign": 35.7,
    "yAlign": 53,
    "cityTemperature": 16
  },
  {
    "continentName": "NA",
    "isDayTime": true,
    "seasonName": "Summer",
    "cityName": "Mexico",
    "xAlign": 87.8,
    "yAlign": 41.8,
    "cityTemperature": 28
  },
  {
    "continentName": "NA",
    "isDayTime": false,
    "seasonName": "Summer",
    "cityName": "Cuba",
    "xAlign": 55.3,
    "yAlign": 88.8,
    "cityTemperature": 27
  },
  {
    "continentName": "EU",
    "isDayTime": true,
    "seasonName": "Winter",
    "cityName": "Germany",
    "xAlign": 33.8,
    "yAlign": 38.8,
    "cityTemperature": 3
  }
]

我想将过滤后的数据显示如下:

  1. 第一个过滤器是TabBar ("continentName")
  2. 第二个过滤器是ToggleButtons ("isDayTime") => 需要至少一个选择
  3. 第三个过滤器是ToggleButtons ("listSeason") => 互斥选择,但不允许选择任何按钮。

当启动页面时,默认情况下Tabbar被选为“NA”,第一个toggleButtons(“isDayTime”)被选为“Day” => 如果点击“Spring”,我想要那个" => 会显示满意的数据,这里具体是“United States”和“Canada”

所以请帮帮我,这是主文件:

import 'package:ask/model/temperature_model.dart';
import 'package:ask/services/temperature_service.dart';
import 'package:flutter/material.dart';

class CityTemperature extends StatefulWidget {
  CityTemperature() : super();
  @override
  _CityTemperatureState createState() => _CityTemperatureState();
}

class _CityTemperatureState extends State<CityTemperature> {
  List<Temperature> _temperature = [];
  List<bool> isDayTime = [true, false];
  List<bool> listSeason = [false, false, false, false];

  @override
  void initState() {
    super.initState();
    TemperatureServices.getTemperature().then((temperature) {
      setState(() {
        _temperature = temperature;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        child: DefaultTabController(
            length: 4,
            child: Scaffold(
                appBar: AppBar(
                  title: Text('Temperature'),
                  bottom: TabBar(tabs: [
                    Tab(child: Text('NA')),
                    Tab(child: Text('EU')),
                    Tab(child: Text('Africa')),
                    Tab(child: Text('Asia')),
                  ]),
                ),
                body: Column(children: [
                  Center(
                      child: ToggleButtons(
                          children: [Text('Day'), Text('Night')],
                          onPressed: (int index) {
                            setState(() {
                              for (int buttonIndex = 0; buttonIndex < isDayTime.length; buttonIndex++) {
                                if (buttonIndex == index) {
                                  isDayTime[buttonIndex] = true;
                                } else {
                                  isDayTime[buttonIndex] = false;
                                }
                              }
                            });
                          },
                          isSelected: isDayTime)),
                  SizedBox(height: 5),
                  Center(
                      child: ToggleButtons(
                          children: [Text('Spring'), Text('Summer'), Text('Autumn'), Text('Winter')],
                          onPressed: (int index) {
                            setState(() {
                              for (int buttonIndex = 0; buttonIndex < listSeason.length; buttonIndex++) {
                                if (buttonIndex == index) {
                                  listSeason[buttonIndex] = !listSeason[buttonIndex];
                                } else {
                                  listSeason[buttonIndex] = false;
                                }
                              }
                            });
                          },
                          isSelected: listSeason)),
                  SizedBox(height: 5),
                  Expanded(
                    child: TabBarView(children: [
                      Column(children: [ // How to display the satisfying data
                        for (Temperature temp in _temperature)
                          Row(
                            mainAxisAlignment: MainAxisAlignment.spaceAround,
                            children: [
                              Text(temp.cityName),
                              Text('${temp.cityTemperature.toString()}° C'),
                            ],
                          )
                      ]),
                      Column(), // How to display the satisfying data
                      Column(), // How to display the satisfying data
                      Column(), // How to display the satisfying data
                    ]),
                  )
                ]))));
  }
}


编辑 1:

我想补充两点如下:

1.为每个_tabs 和每个TabBarView 添加背景图像 isDayTime

  • 对于每个 continentName,将有 2 张图片用于白天或夜晚。

  • 因为是图片,所以我想我会把它放在Assets 中,以便用户加载更快。此外,为了避免在 json => 上创建更多数据,我将创建图像的文件名:“na_day.png”或“na_true.png”,并通过:Image.asset('assets/${temp.continentName}_${isDayTime}.png') 或类似的方式访问它

2。根据图片的 X Y 百分比位置在背景图片上显示cityName

  • 我使用来自 JSON 的数据:xAlign & yAlign 来确定图像上cityName 的位置(JSON 已更新)
  • 据我所知,似乎最好的方法是使用IntrinsicHeightStackAlign 这样做:
class DisplayCountry extends StatelessWidget {
  final List<Temperature> countries;

  DisplayCountry({this.countries});

  @override
  Widget build(BuildContext context) {
    return Column(children: [
      for (Temperature temp in countries)  // I don't know where to put this
        IntrinsicHeight(
          child: Stack(children: [
            Image.asset('assets/${temp.continentName}_${isDayTime}.png'.asset), // Or something like this
            Align(
              alignment: Alignment(temp.xAlign / 100 * 2 - 1, temp.yAlign / 100 * 2 - 1),
              child: Text(temp.cityName),
            ),
          ]),
        )
    ]);
  }
}

extension AssetsExtension on String {
  String get asset => this.toLowerCase().replaceAll(" ", "_").replaceAll("'", "_");
}

所以请帮我更新class DisplayCountry 以便能够结合以上两件事

【问题讨论】:

    标签: flutter dart


    【解决方案1】:

    类似的东西

    class CityTemperature extends StatefulWidget {
      CityTemperature() : super();
      
      @override
      _CityTemperatureState createState() => _CityTemperatureState();
    }
    
    class _CityTemperatureState extends State<CityTemperature> {
      List<Temperature> _temperature = [];
      List<String> _tabs = [];
      Map<String, bool> isDayTime = {'Day': true, 'Night': false};
      Map<String, bool> listSeason = {'Spring': false, 'Summer': false, 'Autumn': false, 'Winter': true};
    
      @override
      void initState() {
        super.initState();
        var response = json.decode(jsonFile);
        _temperature = List<Temperature>.from(response.map((x) => Temperature.fromJson(x)));
        _tabs = _temperature.map<String>((x) => x.continentName).toSet().toList();
        /*
        TemperatureServices.getTemperature().then((temperature) {
          setState(() {
            _temperature = temperature;
          });
        });*/
      }
    
      @override
      Widget build(BuildContext context) {
        return DefaultTabController(
                length: _tabs.length,
                child: Scaffold(
                    appBar: AppBar(
                      title: Text('Temperature'),
                      bottom: TabBar(
                        tabs: _tabs.map((String name) => Tab(text: name)).toList()
                      ),
                    ),
                    body: Column(children: [
                      Center(
                          child: ToggleButtons(
                              children: isDayTime.keys.map((key) => Text(key)).toList(),
                              onPressed: (int index) {
                                String indexKey = isDayTime.keys.toList()[index];
                                setState(() {
                                  isDayTime.updateAll(
                                    (key, value) => key == indexKey ? true : false);
                                  }
                                );
                              },
                              isSelected: isDayTime.values.toList())),
                      SizedBox(height: 5),
                      Center(
                          child: ToggleButtons(
                              children: listSeason.keys.map((key) => Text(key)).toList(),
                              onPressed: (int index) {
                                String indexKey = listSeason.keys.toList()[index];
                                setState(() {
                                  listSeason.updateAll(
                                    (key, value) => key == indexKey ? 
                                    !listSeason[indexKey] : false);
                                });
                              },
                              isSelected: listSeason.values.toList())),
                      SizedBox(height: 5),
                      Expanded(
                        child: TabBarView(
                          children: _tabs.map((String name) {
                            return DisplayCountry(
                              countries: List<Temperature>.from(_temperature)
                              ..retainWhere((temperature) => 
                                temperature.continentName == name 
                                && temperature.isDayTime == isDayTime['Day']
                                && temperature.seasonName == listSeason.keys.firstWhere(
                                (k) => listSeason[k] == true, orElse: () => 'Nothing'))
                            );
                          }).toList()
                        ),
                      )
                    ]
                 )
             )
        );
      }
    }
    
    class DisplayCountry extends StatelessWidget{
      final List<Temperature> countries;
      
      DisplayCountry({this.countries});
      
      @override
      Widget build(BuildContext context){
       return Column(
         children: [
           for(Temperature temp in countries)
               Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  Text(temp.cityName),
                  Text('${temp.cityTemperature.toString()}° C'),
                 ],
               )
         ]
       ); 
      }
    }
    

    我创建了一个名为 _tabs 的列表,其中包含 _temperatures 的所有大洲名称,然后添加了 toSet 和 toList。 toSet 将其转换为一个集合,一个集合是一个不允许重复值的可迭代对象,然后我将其转换回列表,这样我就有了一个唯一大陆名称(NA、EU 等)的列表。

    在 DefaultTabController 中添加 _tabs.length,在 tabView 中创建 _tab.map 列表,该列表创建 DisplayCountry 的小部件列表,我使用 retainwhere 仅保留满足条件的小部件(与选项卡,与选择的相同的 seasonName 和 isDayTime 如果它是真的是白天其他晚上)

    更新

    class DisplayImage extends StatelessWidget {
      final List<Temperature> countries;
      final String continentName;
      final bool isDayTime;
      
      DisplayImage({this.countries , this.continentName, this.isDayTime});
    
      @override
      Widget build(BuildContext context) {
        final size = MediaQuery.of(context).size;
        return Stack(
          fit: StackFit.passthrough,
          children: [
          Image.asset('assets/$continentName_$isDayTime.png'.asset), 
            fit: BoxFit.cover,),
          for (Temperature temp in countries)
            Positioned(
              left: temp.xAlign * size.width / 100.0,
              top: temp.yAlign  / 2 * size.height / 100.0,
              child: Text('${temp.cityName} ${temp.cityTemperature.toString()}° C'),
            )
        ]);
      }
    }
    

    当在 TabView 中调用它时

    TabBarView(
      children: _tabs.map((String name) {
        return DisplayImage(
           continentName: name,
           isDayTime: isDayTime['Day'],
           countries: List<Temperature>.from(_temperature)
             ..retainWhere((temperature) =>
               temperature.continentName == name &&
               temperature.isDayTime == isDayTime['Day'] &&
               temperature.seasonName ==
               listSeason.keys.firstWhere(
                (k) => listSeason[k] == true,
                orElse: () => 'Nothing')));
      }).toList())
    

    据我了解,您可以使用堆栈的 fit 属性 (StackFit.passthrough),它的工作原理与intrinsicHeight 相同。来自文档

    StackFit.passthrough 
    For example, if a Stack is an Expanded child of a Row, the horizontal constraints will be tight and the vertical constraints will be loose.
    

    在这种情况下,您在列中使用 Expanded,因此它具有水平松散和垂直紧缩。然后做一些数学运算,如果定位不按您的意愿工作,请尝试使用 Align 小部件

    【讨论】:

    • 是的,我知道你会回复我的 xD。没有言语可以表达我的感激之情,这是我所期望的。只有that 如何将jsonFile 链接到特定的link 或资产中的json 文件
    • 啊,nvm,我想通了:D
    • 抱歉忘记更改了,我没有 TemperatureServices 来获取列表,所以我只是从 Json 字符串中提取出来
    • 请不要介意,你帮了我很多:P BTW,我已经编辑了帖子以添加更多问题,请帮助我更多xD
    • 那些坐标是基于什么的?图片的大小?
    猜你喜欢
    • 2015-10-02
    • 2020-08-29
    • 1970-01-01
    • 2020-11-07
    • 2021-04-27
    • 1970-01-01
    • 1970-01-01
    • 2019-05-04
    • 1970-01-01
    相关资源
    最近更新 更多