Flutter:如何基于TabBar和ToggleButtons从JSON过滤数据? [英] Flutter: How to filter data from JSON based on TabBar and ToggleButtons?

查看:68
本文介绍了Flutter:如何基于TabBar和ToggleButtons从JSON过滤数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这样的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. 第一个过滤器是 TabBar (续inentName)

  2. 第二个过滤器是 ToggleButtons ( isDayTime)=> 需要至少一个选择

  3. 第三个过滤器是 ToggleButtons ( listSeason)=> 互斥选择,但不允许选择任何按钮。

  1. The 1st filter is TabBar ("continentName")
  2. The 2nd filter is ToggleButtons ("isDayTime") => requiring at least one selection
  3. The 3rd filter is ToggleButtons ("listSeason") => mutually exclusive selection, but allows for none of the buttons to be selected.

启动页面时,默认情况下,<将code> Tabbar 选择为 NA,将第一个 toggleButtons ( isDayTime)选择为 Day。 => 我想要的是,如果点击春季 =>它将显示令人满意的数据,特别是此处的美国。和加拿大

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"

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

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




编辑1:


我要添加两件事,如下所示:


Edit 1:

I want to add 2 things as follows:

1。在 TabBarView 中为每个 _tabs 和每个
isDayTime

1. Add background image in TabBarView for each _tabs and each isDayTime


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

  • For each continentName, there will be 2 images for Day or Night.


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

  • Because it is an image, I think I will put it in Assets for users to load faster. 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

2。根据图像的XY百分比位置在背景图像上显示 cityName

2. Display cityName on background image based on X Y percent position of image


  • 我使用JSON中的数据: xAlign & yAlign 确定 cityName 在图像上的位置(已更新JSON)

  • 据我所知,似乎最好的方法是使用 IntrinsicHeight Stack 对齐来执行以下操作:

  • I use data from JSON: xAlign & yAlign to determine the position of cityName on the image (JSON updated)
  • As far as I know, it seems the best way is used IntrinsicHeight, Stack and Align to do like this:
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("'", "_");
}

所以请帮助我更新 DisplayCountry类能够将上面的两件事结合起来

So please help me update class DisplayCountry to be able to combine the 2 things above

推荐答案

类似这样的东西

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将其转换为一个集合,一个集合是一个可迭代的,不允许重复的值,然后我将其转换回列表,这样我就有了一个唯一的continentName(NA,EU等)列表。

I create a list called _tabs with all the continentName of _temperatures, then added toSet and 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).

在DefaultTabController中,我添加_tabs.length,并在tabView中创建一个_tab.map列表,该列表创建了DisplayCountry的小部件列表,我使用keepwhere来仅保留那些满足条件的小部件(相同的continentName标签中的那个,与选择的那个相同的seasonName,如果为true,则为isDayTime,否则为白天)

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)


更新

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中调用时

and when calling it in the 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),它的工作方式与InternalHeight相同。来自文档

As far as I understand you can use the fit property of the stack (StackFit.passthrough) and it will work the same as 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. Then do some math and try the Align widget if the positioned doesnt work as you want

这篇关于Flutter:如何基于TabBar和ToggleButtons从JSON过滤数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆