简体   繁体   English

Flutter:如何根据 TabBar 和 ToggleButtons 从 JSON 过滤数据?

[英]Flutter: How to filter data from JSON based on TabBar and ToggleButtons?

I have a JSON like this:我有一个像这样的 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
  }
]

I want to display the filtered data as follows:我想按如下方式显示过滤后的数据:

在此处输入图像描述

  1. The 1st filter is TabBar ("continentName")第一个过滤器是TabBar ("continentName")
  2. The 2nd filter is ToggleButtons ("isDayTime") => requiring at least one selection第二个过滤器是ToggleButtons ("isDayTime") =>需要至少一个选择
  3. The 3rd filter is ToggleButtons ("listSeason") => mutually exclusive selection, but allows for none of the buttons to be selected.第三个过滤器是ToggleButtons ("listSeason") =>互斥选择,但不允许选择任何按钮。

When start the page, by default, Tabbar is selected as "NA", the first toggleButtons ("isDayTime") is selected as "Day" => I want that if click on "Spring" => it will display the satisfying data, specifically here will be "United States" and "Canada"启动页面时,默认情况下Tabbar选择为“NA”,第一个toggleButtons (“isDayTime”)选择为“Day”=>我希望如果点击“Spring”=>它将显示令人满意的数据,特别是这里将是“美国”和“加拿大”

So please help me, this is main file:所以请帮助我,这是主文件:

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


Edit 1:编辑1:

I want to add 2 things as follows:我想添加以下两件事:

1. Add background image in TabBarView for each _tabs and each isDayTime 1.在TabBarView中为每个_tabs和每个isDayTime添加背景图片

  • For each continentName , there will be 2 images for Day or Night.对于每个continentName ,将有 2 张图片代表白天或夜晚。

在此处输入图像描述

  • Because it is an image, I think I will put it in Assets for users to load faster.因为是图片,所以我想我会把它放在Assets中,以便用户加载更快。 Besides, to avoid creating more data on json => I will create the filename of image as: "na_day.png" or "na_true.png" and access it by: Image.asset('assets/${temp.continentName}_${isDayTime}.png') or something like that此外,为了避免在 json 上创建更多数据 => 我将创建图像的文件名为:“na_day.png”或“na_true.png”并通过以下方式访问它: Image.asset('assets/${temp.continentName}_${isDayTime}.png')或类似的东西

2. Display cityName on background image based on XY percent position of image 2.根据图片的XY百分比position在背景图片上显示cityName

在此处输入图像描述

  • I use data from JSON: xAlign & yAlign to determine the position of cityName on the image (JSON updated)我使用来自 JSON: xAlign & yAlign的数据来确定图像上 cityName 的cityName (JSON 更新)
  • As far as I know, it seems the best way is used IntrinsicHeight , Stack and Align to do like this:据我所知,似乎最好的方法是使用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("'", "_");
}

So please help me update class DisplayCountry to be able to combine the 2 things above所以请帮我更新class DisplayCountry以便能够结合上述两件事

something like this像这样的东西

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

I create a list called _tabs with all the continentName of _temperatures, then added toSet and toList.我创建了一个名为 _tabs 的列表,其中包含 _temperatures 的所有大陆名称,然后添加了 toSet 和 toList。 toSet converts it to a set, a set is an iterable that doesn't allow repeated values, and then I converted it back to list, that way I have a list of unique continentName (NA, EU, etc). toSet 将其转换为一个集合,一个集合是一个不允许重复值的可迭代对象,然后我将其转换回列表,这样我就有了一个唯一大陆名称(NA、EU 等)的列表。

In DefaultTabController I add _tabs.length and in tabView I create a list of _tab.map, which creates a list of widgets of DisplayCountry, I use retainwhere to keep only the ones that satisfies the conditions (same continentName that the one in the tab, same seasonName that the one selected and isDayTime if it's true is day else night)在 DefaultTabController 我添加 _tabs.length 并在 tabView 我创建一个 _tab.map 列表,它创建一个 DisplayCountry 小部件列表,我使用retainwhere 只保留满足条件的那些(与选项卡中的相同的continentName,与选择的季节名称相同的季节名称,如果为真,则为白天时间,否则为白天)

在此处输入图像描述 在此处输入图像描述

UPDATE更新

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

and when calling it in the TabView并在 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())

As far as I understand you can use the fit property of the stack (StackFit.passthrough) and it will work the same as intrinsicHeight.据我了解,您可以使用堆栈的 fit 属性(StackFit.passthrough),它的工作原理与intrinsicHeight 相同。 From the documentation从文档

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.

In this case you're using an Expanded in a column so it has horizontal loose and vertical tight.在这种情况下,您在列中使用 Expanded,因此它具有水平松散和垂直紧缩。 Then do some math and try the Align widget if the positioned doesnt work as you want然后做一些数学运算并尝试 Align 小部件,如果定位不按你想要的那样工作

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

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