Flutter:如何基于TabBar和ToggleButtons从JSON过滤数据? [英] Flutter: How to filter data from JSON based on TabBar and ToggleButtons?
问题描述
我有这样的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
(续inentName) - 第二个过滤器是
ToggleButtons
( isDayTime)=> 需要至少一个选择 - 第三个过滤器是
ToggleButtons
( listSeason)=> 互斥选择,但不允许选择任何按钮。
- The 1st filter is
TabBar
("continentName") - The 2nd filter is
ToggleButtons
("isDayTime") => requiring at least one selection - 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 $添加背景图像c $ c>
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 ofcityName
on the image (JSON updated) - As far as I know, it seems the best way is used
IntrinsicHeight
,Stack
andAlign
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屋!