[英]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:我想按如下方式显示过滤后的数据:
TabBar
("continentName")第一个过滤器是TabBar
("continentName")ToggleButtons
("isDayTime") => requiring at least one selection第二个过滤器是ToggleButtons
("isDayTime") =>需要至少一个选择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
]),
)
]))));
}
}
I want to add 2 things as follows:我想添加以下两件事:
1. Add background image in TabBarView
for each _tabs
and each isDayTime
1.在TabBarView
中为每个_tabs
和每个isDayTime
添加背景图片
continentName
, there will be 2 images for Day or Night.对于每个continentName
,将有 2 张图片代表白天或夜晚。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
xAlign
& yAlign
to determine the position of cityName
on the image (JSON updated)我使用来自 JSON: xAlign
& yAlign
的数据来确定图像上 cityName 的cityName
(JSON 更新)IntrinsicHeight
, Stack
and Align
to do like this:据我所知,似乎最好的方法是使用IntrinsicHeight
, Stack
和Align
来做这样的事情: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.