flutter how to create a TabBarView base on null condition - flutter

I have a JSON file like this:
[
{
"name": "A",
"tabX": ["AX1","AX2"],
"tabY": null
},
{
"name": "B",
"tabX": null,
"tabY": ["BY1","BY2"]
},
{
"name": "C",
"tabX": ["CX1","CX2"],
"tabY": ["CY1","CY2"]
}
]
I want to create 3 TabView:
The "All" tab shows all
Tab "TabX" will show the elements that tabX has a value, if tabY == null will not include
Tab "TabY" will show the elements that tabY has a value, if tabX == null will not include
So please help me, this is full code:
import 'package:flutter/material.dart';
import 'dart:convert';
List<Json> jsonFromJson(String str) => List<Json>.from(json.decode(str).map((x) => Json.fromJson(x)));
String jsonToJson(List<Json> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Json {
Json({
this.name,
this.tabX,
this.tabY,
});
String name;
List<String> tabX;
List<String> tabY;
factory Json.fromJson(Map<String, dynamic> json) => Json(
name: json["name"] == null ? null : json["name"],
tabX: json["tabX"] == null ? null : List<String>.from(json["tabX"].map((x) => x)),
tabY: json["tabY"] == null ? null : List<String>.from(json["tabY"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"name": name == null ? null : name,
"tabX": tabX == null ? null : List<dynamic>.from(tabX.map((x) => x)),
"tabY": tabY == null ? null : List<dynamic>.from(tabY.map((x) => x)),
};
}
class JsonServices {
static Future<List<Json>> getData() {
String jsonString = '''
[
{
"name": "A",
"tabX": ["AX1","AX2"],
"tabY": null
},
{
"name": "B",
"tabX": null,
"tabY": ["BY1","BY2"]
},
{
"name": "C",
"tabX": ["CX1","CX2"],
"tabY": ["CY1","CY2"]
}
]
''';
return Future.value(jsonFromJson(jsonString));
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Json> _json = [];
#override
void initState() {
super.initState();
setState(() {
JsonServices.getData().then((data) {
setState(() {
_json = data;
});
});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: [
Tab(text: 'ALL'),
Tab(text: 'TabX'),
Tab(text: 'TabY'),
],
),
),
body: TabBarView(children: [
Tab(child: TabItemWidget(j: _json,),),
Tab(text: 'TabX'), // pls help
Tab(text: 'TabY'), // pls help
],),
),
),
);
}
}
class TabItemWidget extends StatelessWidget {
final List<Json> j;
const TabItemWidget({Key key, this.j}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(children: [
for(Json j in j)
Row(
children: [
Expanded(child: Text('${j.name}: ')),
if(j.tabX != null) Expanded(child: Text(j.tabX.reduce((value, element) => '$value - $element'))),
if(j.tabY != null) Expanded(child: Text(j.tabY.reduce((value, element) => '$value - $element')))
],
)
],);
}
}

You can filter the list before passing in:
// Only when tabX != null
_json.where((element) => element.tabX != null).toList()
// Only when tabY != null
_json.where((element) => element.tabY != null).toList()
To display only the appropriate value for that tab, we can use an enum and pass it in. It's always a good idea to use enum when we have more than 2 modes of display
enum DisplayMode { all, onlyX, onlyY }
The TabItemWidget would now be:
class TabItemWidget extends StatelessWidget {
final List<Json> j;
final DisplayMode mode;
const TabItemWidget({Key key, this.j, this.mode = DisplayMode.all})
: super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: [
for (Json j in j)
Row(
children: [
Expanded(child: Text('${j.name}: ')),
if (j.tabX != null && !(mode == DisplayMode.onlyY))
Expanded(
child: Text(j.tabX
.reduce((value, element) => '$value - $element'))),
if (j.tabY != null && !(mode == DisplayMode.onlyX))
Expanded(
child: Text(
j.tabY.reduce((value, element) => '$value - $element')))
],
)
],
);
}
}
The TabBarView would now be:
body: TabBarView(
children: [
Tab(
child: TabItemWidget(
j: _json,
),
),
Tab(
child: TabItemWidget(
j: _json.where((element) => element.tabX != null).toList(),
mode: DisplayMode.onlyX,
),
),
// pls help
Tab(
child: TabItemWidget(
j: _json.where((element) => element.tabY != null).toList(),
mode: DisplayMode.onlyY,
),
),
// pls help
],
)
Result:

This code is not beautiful but it works (null safety):
import 'package:flutter/material.dart';
import 'dart:convert';
List<Json> jsonFromJson(String str) => List<Json>.from(json.decode(str).map((x) =>
Json.fromJson(x)));
String jsonToJson(List<Json> data) => json.encode(List<dynamic>.from(data.map((x) =>
x.toJson())));
class Json {
Json({
this.name,
this.tabX,
this.tabY,
});
String? name;
List<String>? tabX;
List<String>? tabY;
factory Json.fromJson(Map<String, dynamic> json) => Json(
name: json["name"] == null ? null : json["name"],
tabX: json["tabX"] == null ? null : List<String>.from(json["tabX"].map((x) =>
x)),
tabY: json["tabY"] == null ? null : List<String>.from(json["tabY"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"name": name == null ? null : name,
"tabX": (tabX == null)? null : List<dynamic>.from(tabX!.map((x) => x)),
"tabY": (tabY == null) ? null : List<dynamic>.from(tabY!.map((x) => x)),
};
}
class JsonServices {
static Future<List<Json>> getData() {
String jsonString = '''
[
{
"name": "A",
"tabX": ["AX1","AX2"],
"tabY": null
},
{
"name": "B",
"tabX": null,
"tabY": ["BY1","BY2"]
},
{
"name": "C",
"tabX": ["CX1","CX2"],
"tabY": ["CY1","CY2"]
}
]
''';
return Future.value(jsonFromJson(jsonString));
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Json> _json = [];
#override
void initState() {
super.initState();
setState(() {
JsonServices.getData().then((data) {
setState(() {
_json = data;
});
});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: [
Tab(text: 'ALL'),
Tab(text: 'TabX'),
Tab(text: 'TabY'),
],
),
),
body: TabBarView(children: [
Tab(child: TabItemWidget(j: _json,),),
Tab(child: TabItemWidget2(j: _json,),), // pls help
Tab(child: TabItemWidget3(j: _json,),), // pls help
],),
),
),
);
}
}
class TabItemWidget extends StatelessWidget {
final List<Json> j;
TabItemWidget({Key? key, required this.j}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(children: [
for(Json j in j)
Row(
children: [
Expanded(child: Text('${j.name}: ')),
if(j.tabX != null) Expanded(child: Text(j.tabX!.reduce((value, element) =>
'$value - $element'))),
if(j.tabY != null) Expanded(child: Text(j.tabY!.reduce((value, element) =>
'$value - $element')))
],
)
],);
}
}
class TabItemWidget2 extends StatelessWidget {
final List<Json> j;
const TabItemWidget2({Key? key, required this.j}) : super(key: key);
#override
Widget build(BuildContext context) {
print(j.where((element) => element.tabX != null).toString());
var jForTabX = j.where((element) => element.tabX != null);
return Column(children: [
for(Json j in jForTabX)
Row(
children: [
Expanded(child: Text('${j.name}: ')),
if(j.tabX != null && j.tabY == null) Expanded(child:
Text(j.tabX!.reduce((value, element) => '$value - $element'))),
if(j.tabY != null) Expanded(child: Text(j.tabX!.reduce((value, element) =>
'$value - $element')))
],
)
],);
}
}
class TabItemWidget3 extends StatelessWidget {
final List<Json> j;
const TabItemWidget3({Key? key, required this.j}) : super(key: key);
#override
Widget build(BuildContext context) {
var jForTabY = j.where((element) => element.tabY != null);
return Column(children: [
for(Json j in jForTabY)
Row(
children: [
Expanded(child: Text('${j.name}: ')),
if(j.tabX != null && j.tabX == null) Expanded(child:
Text(j.tabX!.reduce((value, element) => '$value - $element'))),
if(j.tabY != null) Expanded(child: Text(j.tabY!.reduce((value, element) =>
'$value - $element')))
],
)
],);
}
}
You can improve que quality of this code.

Related

JSON data coming from API not showing in the dropdownlist

I am using JSON API to get the data into my dropdownlist in Flutter. But whenever I am clicking on the dropdownlist the data does not appear in the list.
Following is my code:
class ItemSelection extends StatefulWidget{
#override
_ItemSelectionState createState() => _ItemSelectionState();
}
class _ItemSelectionState extends State<ItemSelection> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text("Dropdown",
style: TextStyle(
fontSize: 20.0,
),
textAlign: TextAlign.center,
),
SizedBox(
height: 20.0,
),
DropDownField(
controller: cityselected,
hintText: "Select an item",
enabled: true,
itemsVisibleInDropdown: 5,
items: items,
onValueChanged: (value){
setState(() {
selectedcity = value;
});
}
)
],
),
),
);
}
}
final List<String> items = [];
Future getitem() async {
var response = await http.get(
"basic json url here",
headers: {
"Accept": "application/json"
}
);
if(response.statusCode == 200){
List<GetItem> getitem = getItemFromJson(response.body);
for(int i =0; i< getitem.length; i++){
items.add(getitem[i].itemName);
}
print(items);
}
}
GetItem.dart:
import 'dart:convert';
List<GetItem> getItemFromJson(String str) => List<GetItem>.from(json.decode(str).map((x) => GetItem.fromJson(x)));
String getItemToJson(List<GetItem> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class GetItem {
GetItem({
this.sLNo,
this.itemName,
this.category,
});
String sLNo;
String itemName;
String category;
factory GetItem.fromJson(Map<String, dynamic> json) => GetItem(
sLNo: json["S/L NO"],
itemName: json["Item Name"] == null ? null : json["Item Name"],
category: json["Category"] == null ? null : json["Category"],
);
Map<String, dynamic> toJson() => {
"S/L NO": sLNo,
"Item Name": itemName == null ? null : itemName,
"Category": category == null ? null : category,
};
}
As soon as I click on the dropdownlist I want the data coming from the JSON API to show in there. Can someone help me with this please?
You can copy paste run full code below
You can use FutureBuilder
code snippet
Future<List<String>> _future;
#override
void initState() {
_future = getitem();
...
FutureBuilder(
future: _future,
builder: (context, AsyncSnapshot<List<String>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'${snapshot.error}',
style: TextStyle(color: Colors.red),
);
} else {
return DropDownField(
controller: cityselected,
hintText: "Select an item",
enabled: true,
itemsVisibleInDropdown: 5,
items: snapshot.data,
onValueChanged: (dynamic value) {
setState(() {
selectedcity = value;
});
});
working demo
full code
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:dropdownfield/dropdownfield.dart';
import 'package:http/http.dart' as http;
List<GetItem> getItemFromJson(String str) =>
List<GetItem>.from(json.decode(str).map((x) => GetItem.fromJson(x)));
String getItemToJson(List<GetItem> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class GetItem {
GetItem({
this.sLNo,
this.itemName,
this.category,
});
String sLNo;
String itemName;
String category;
factory GetItem.fromJson(Map<String, dynamic> json) => GetItem(
sLNo: json["S/L NO"],
itemName: json["Item Name"] == null ? null : json["Item Name"],
category: json["Category"] == null ? null : json["Category"],
);
Map<String, dynamic> toJson() => {
"S/L NO": sLNo,
"Item Name": itemName == null ? null : itemName,
"Category": category == null ? null : category,
};
}
class ItemSelection extends StatefulWidget {
#override
_ItemSelectionState createState() => _ItemSelectionState();
}
class _ItemSelectionState extends State<ItemSelection> {
TextEditingController cityselected = TextEditingController();
String selectedcity;
Future<List<String>> _future;
#override
void initState() {
_future = getitem();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"Dropdown",
style: TextStyle(
fontSize: 20.0,
),
textAlign: TextAlign.center,
),
SizedBox(
height: 20.0,
),
FutureBuilder(
future: _future,
builder: (context, AsyncSnapshot<List<String>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'${snapshot.error}',
style: TextStyle(color: Colors.red),
);
} else {
return DropDownField(
controller: cityselected,
hintText: "Select an item",
enabled: true,
itemsVisibleInDropdown: 5,
items: snapshot.data,
onValueChanged: (dynamic value) {
setState(() {
selectedcity = value;
});
});
}
}
})
],
),
),
);
}
}
final List<String> items = [];
Future<List<String>> getitem() async {
/*var response = await http
.get("basic json url here", headers: {"Accept": "application/json"});*/
String jsonString = '''
[{
"S/L NO":"1",
"Item Name":"item 1",
"Category": "c1"
},
{
"S/L NO":"2",
"Item Name":"item 2",
"Category": "c2"
},
{
"S/L NO":"3",
"Item Name":"item 3",
"Category": "c3"
},
{
"S/L NO":"4",
"Item Name":"item 4",
"Category": "c4"
},
{
"S/L NO":"5",
"Item Name":"item 5",
"Category": "c5"
}
]
''';
http.Response response = http.Response(jsonString, 200);
if (response.statusCode == 200) {
List<GetItem> getitem = getItemFromJson(response.body);
print(getitem.length);
for (int i = 0; i < getitem.length; i++) {
items.add(getitem[i].itemName);
}
}
return items;
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: ItemSelection(),
);
}
}
setState(() {
for(int i =0; i< getitem.length; i++){
items.add(getitem[i].itemName);
}
});

Flutter: RangeError (index): Invalid value: Valid value range is empty: -1

I have 2 JSON file like this:
json1 (API):
[
{
"json1Language": "English",
"json1Icon": "https://www.countryflags.io/gb/flat/64.png",
"json1Code": "en"
},
{
"json1Language": "French",
"json1Icon": "https://www.countryflags.io/fr/flat/64.png",
"json1Code": "fr"
},
{
"json1Language": "Spanish",
"json1Icon": "https://www.countryflags.io/es/flat/64.png",
"json1Code": "es"
}
]
json2 (API):
[
{
"json2Country": "Canada",
"json2Continent": "North American",
"json2Language": [
"French",
"English"
]
},
{
"json2Country": "Mexico",
"json2Continent": "North American",
"json2Language": [
"Spanish",
"English"
]
},
{
"json2Country": "United Kingdom",
"json2Continent": "Europe",
"json2Language": [
"English"
]
},
{
"json2Country": "France",
"json2Continent": "Europe",
"json2Language": [
"French"
]
},
{
"json2Country": "Spain",
"json2Continent": "Europe",
"json2Language": [
"Spanish"
]
}
]
I tried to show the data of json1Code from Json1, it shows an error Flutter: RangeError (index): Invalid value: Valid value range is empty: -1 for a few seconds then shows the data correctly, I'm not sure where I did wrong
I think maybe something wrong in the Build class:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following RangeError was thrown building Builder(dirty):
RangeError (index): Invalid value: Valid value range is empty: -1
The relevant error-causing widget was:
Builder file:///D:/Flutter/Test/load_data/lib/json2_page3.dart:80:17
When the exception was thrown, this was the stack:
#0 List.[] (dart:core-patch/growable_array.dart:177:60)
#1 _ShowContinentState.build.<anonymous closure> (package:load_data/json2_page3.dart:83:38)
#2 Builder.build (package:flutter/src/widgets/basic.dart:7183:48)
#3 StatelessElement.build (package:flutter/src/widgets/framework.dart:4644:28)
#4 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4570:15)
please help me, this is main file
import 'package:flutter/material.dart';
import 'package:load_data/model/json2_model.dart';
import 'package:load_data/service/json1_service.dart';
import 'package:load_data/service/json2_service.dart';
import 'model/json1_model.dart';
class Json2Page3 extends StatefulWidget {
#override
_Json2Page3State createState() => _Json2Page3State();
}
class _Json2Page3State extends State<Json2Page3> {
List<Json2> json2 = [];
List<String> _continent = [];
#override
void initState() {
super.initState();
setState(() {
Json2Services.getData().then((data) {
setState(() {
json2 = data;
_continent = json2.map<String>((x) => x.json2Continent).toSet().toList();
});
});
});
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: _continent.length,
child: Scaffold(
appBar: AppBar(
title: Text('Page 2'),
bottom: TabBar(tabs: _continent.map((String name) => Tab(text: name)).toList()),
),
body: TabBarView(
children: _continent.map((String name) {
return ShowContinent(
json2: List<Json2>.from(json2)..retainWhere((e) => e.json2Continent == name),
);
}).toList()),
));
}
}
class ShowContinent extends StatefulWidget {
final List<Json2> json2;
ShowContinent({this.json2});
#override
_ShowContinentState createState() => _ShowContinentState(json2);
}
class _ShowContinentState extends State<ShowContinent> {
final List<Json2> json2;
List<Json1> json1 = [];
_ShowContinentState(this.json2);
#override
void initState() {
super.initState();
Json1Services.getData().then((data) {
setState(() {
json1 = data;
});
});
}
#override
Widget build(BuildContext context) {
return Column(
children: [
for (Json2 j2 in json2)
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(j2.json2Country.toUpperCase()),
for (int i = 0; i < j2.json2Language.length; i++)
Builder(
builder: (_) {
int index = json1.indexWhere((e) => e.json1Language == j2.json2Language[i]);
return Row(
children: [
Image.network(json1[index].json1Icon),
Text(json1[index].json1Code),
],
);
},
)
],
),
],
);
}
}
You can copy paste run full code below
You can use addPostFrameCallback and bool isLoading to check loading status
when isLoading == true, return CircularProgressIndicator()
code snippet
bool isLoading = true;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
Json1Services.getData().then((data) {
setState(() {
json1 = data;
isLoading = false;
});
});
});
}
#override
Widget build(BuildContext context) {
return isLoading
? Center(child: CircularProgressIndicator())
: Column(
working demo
full code
import 'package:flutter/material.dart';
import 'dart:convert';
List<Json2> json2FromJson(String str) =>
List<Json2>.from(json.decode(str).map((x) => Json2.fromJson(x)));
String json2ToJson(List<Json2> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
List<Json1> json1FromJson(String str) =>
List<Json1>.from(json.decode(str).map((x) => Json1.fromJson(x)));
String json1ToJson(List<Json1> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Json1 {
Json1({
this.json1Language,
this.json1Icon,
this.json1Code,
});
String json1Language;
String json1Icon;
String json1Code;
factory Json1.fromJson(Map<String, dynamic> json) => Json1(
json1Language: json["json1Language"],
json1Icon: json["json1Icon"],
json1Code: json["json1Code"],
);
Map<String, dynamic> toJson() => {
"json1Language": json1Language,
"json1Icon": json1Icon,
"json1Code": json1Code,
};
}
class Json2 {
Json2({
this.json2Country,
this.json2Continent,
this.json2Language,
});
String json2Country;
String json2Continent;
List<String> json2Language;
factory Json2.fromJson(Map<String, dynamic> json) => Json2(
json2Country: json["json2Country"],
json2Continent: json["json2Continent"],
json2Language: List<String>.from(json["json2Language"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"json2Country": json2Country,
"json2Continent": json2Continent,
"json2Language": List<dynamic>.from(json2Language.map((x) => x)),
};
}
class Json2Services {
static Future<List<Json2>> getData() async {
await Future.delayed(Duration(seconds: 5), () {});
String jsonString = '''
[
{
"json2Country": "Canada",
"json2Continent": "North American",
"json2Language": [
"French",
"English"
]
},
{
"json2Country": "Mexico",
"json2Continent": "North American",
"json2Language": [
"Spanish",
"English"
]
},
{
"json2Country": "United Kingdom",
"json2Continent": "Europe",
"json2Language": [
"English"
]
},
{
"json2Country": "France",
"json2Continent": "Europe",
"json2Language": [
"French"
]
},
{
"json2Country": "Spain",
"json2Continent": "Europe",
"json2Language": [
"Spanish"
]
}
]
''';
return Future.value(json2FromJson(jsonString));
}
}
class Json1Services {
static Future<List<Json1>> getData() async {
await Future.delayed(Duration(seconds: 5), () {});
String jsonString = '''
[
{
"json1Language": "English",
"json1Icon": "https://www.countryflags.io/gb/flat/64.png",
"json1Code": "en"
},
{
"json1Language": "French",
"json1Icon": "https://www.countryflags.io/fr/flat/64.png",
"json1Code": "fr"
},
{
"json1Language": "Spanish",
"json1Icon": "https://www.countryflags.io/es/flat/64.png",
"json1Code": "es"
}
]
''';
return Future.value(json1FromJson(jsonString));
}
}
class Json2Page3 extends StatefulWidget {
#override
_Json2Page3State createState() => _Json2Page3State();
}
class _Json2Page3State extends State<Json2Page3> {
List<Json2> json2 = [];
List<String> _continent = [];
#override
void initState() {
super.initState();
setState(() {
Json2Services.getData().then((data) {
setState(() {
json2 = data;
_continent =
json2.map<String>((x) => x.json2Continent).toSet().toList();
});
});
});
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: _continent.length,
child: Scaffold(
appBar: AppBar(
title: Text('Page 2'),
bottom: TabBar(
tabs:
_continent.map((String name) => Tab(text: name)).toList()),
),
body: TabBarView(
children: _continent.map((String name) {
return ShowContinent(
json2: List<Json2>.from(json2)
..retainWhere((e) => e.json2Continent == name),
);
}).toList()),
));
}
}
class ShowContinent extends StatefulWidget {
final List<Json2> json2;
ShowContinent({this.json2});
#override
_ShowContinentState createState() => _ShowContinentState(json2);
}
class _ShowContinentState extends State<ShowContinent> {
final List<Json2> json2;
List<Json1> json1 = [];
_ShowContinentState(this.json2);
bool isLoading = true;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
Json1Services.getData().then((data) {
setState(() {
json1 = data;
isLoading = false;
});
});
});
}
#override
Widget build(BuildContext context) {
return isLoading
? Center(child: CircularProgressIndicator())
: Column(
children: [
for (Json2 j2 in json2)
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(j2.json2Country.toUpperCase()),
for (int i = 0; i < j2.json2Language.length; i++)
Builder(
builder: (_) {
int index = json1.indexWhere(
(e) => e.json1Language == j2.json2Language[i]);
return Row(
children: [
Image.network(json1[index].json1Icon),
Text(json1[index].json1Code),
],
);
},
)
],
),
],
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Json2Page3(),
);
}
}
When build() is called, json data is not ready.
So you need to fix code asynchronous.
I suggest one solution 'FutureBuilder'
https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

Flutter: How to use forEach method for this data

I have 2 JSON file:
Json1 file:
[
{
"json1language": "English"
},
{
"json1language": "French"
},
{
"json1language": "Spanish"
}
]
Json2 file:
[
{
"json2country": "Canada",
"json2languages": [
{
"json2language": "English"
},
{
"json2language": "French"
}
]
},
{
"json2country": "Mexico",
"json2languages": [
{
"json2language": "English"
},
{
"json2language": "Spanish"
}
]
},
{
"json2country": "France",
"json2languages": [
{
"json2language": "French"
}
]
}
]
I want that when I click on json1language => showDialog show json2country and json2language that match the condition json2language == json1language
Example: click "English" => show json2country: "Canada"; json2language: "English","French" & json2country: "Mexico"; json2language: "English","Spanish" (because json2language contains "English")
So pls help me, this is the main file
import 'package:flutter/material.dart';
import 'dart:convert';
List<Json1> json1FromJson(String str) => List<Json1>.from(json.decode(str).map((x) => Json1.fromJson(x)));
String json1ToJson(List<Json1> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Json1 {
Json1({
this.json1Language,
});
String json1Language;
factory Json1.fromJson(Map<String, dynamic> json) => Json1(
json1Language: json["json1language"],
);
Map<String, dynamic> toJson() => {
"json1language": json1Language,
};
}
List<Json2> json2FromJson(String str) => List<Json2>.from(json.decode(str).map((x) => Json2.fromJson(x)));
String json2ToJson(List<Json2> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Json2 {
Json2({
this.json2Country,
this.json2Languages,
});
String json2Country;
List<Json2Language> json2Languages;
factory Json2.fromJson(Map<String, dynamic> json) => Json2(
json2Country: json["json2country"],
json2Languages: List<Json2Language>.from(json["json2languages"].map((x) => Json2Language.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"json2country": json2Country,
"json2languages": List<dynamic>.from(json2Languages.map((x) => x.toJson())),
};
}
class Json2Language {
Json2Language({
this.json2Language,
});
String json2Language;
factory Json2Language.fromJson(Map<String, dynamic> json) => Json2Language(
json2Language: json["json2language"],
);
Map<String, dynamic> toJson() => {
"json2language": json2Language,
};
}
class Json1Services {
static Future<List<Json1>> getData() {
String jsonString = '''
[
{
"json1language": "English"
},
{
"json1language": "French"
},
{
"json1language": "Spanish"
}
]
''';
return Future.value(json1FromJson(jsonString));
}
}
class Json2Services {
static Future<List<Json2>> getData() {
String jsonString = '''
[
{
"json2country": "Canada",
"json2languages": [
{
"json2language": "English"
},
{
"json2language": "French"
}
]
},
{
"json2country": "Mexico",
"json2languages": [
{
"json2language": "English"
},
{
"json2language": "Spanish"
}
]
},
{
"json2country": "France",
"json2languages": [
{
"json2language": "French"
}
]
}
]
''';
return Future.value(json2FromJson(jsonString));
}
}
class Json extends StatefulWidget {
#override
_JsonState createState() => _JsonState();
}
class _JsonState extends State<Json> {
List<Json1> _json1 = [];
List<Json2> _json2 = [];
#override
void initState() {
super.initState();
setState(() {
Json1Services.getData().then((data) {
setState(() {
_json1 = data;
});
});
Json2Services.getData().then((data) {
setState(() {
_json2 = data;
});
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (Json1 j1 in _json1)
RaisedButton(
child: Text(j1.json1Language),
onPressed: () => showDialog(
context: context,
builder: (_) {
return AlertDialog(
content: null,
);
}))
],
),
));
}
}
You can copy paste run full code below
code snippet
filtered = [];
_json2.forEach((element) {
for (int i = 0;
i < element.json2Languages.length;
i++) {
if (element.json2Languages[i].json2Language ==
j1.json1Language) {
filtered.add(element);
}
}
});
...
return AlertDialog(
content: SizedBox(
width: 300,
height: 70,
child: Row(
children: [
Expanded(
flex: 2,
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
for (int i = 0;
i < filtered.length;
i++)
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
filtered[i].json2Country,
),
for (int j = 0;
j <
filtered[i]
.json2Languages
.length;
j++)
Column(children: [
Text(filtered[i]
.json2Languages[j]
.json2Language)
]),
],
working demo
full code
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import 'dart:convert';
List<Json1> json1FromJson(String str) =>
List<Json1>.from(json.decode(str).map((x) => Json1.fromJson(x)));
String json1ToJson(List<Json1> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Json1 {
Json1({
this.json1Language,
});
String json1Language;
factory Json1.fromJson(Map<String, dynamic> json) => Json1(
json1Language: json["json1language"],
);
Map<String, dynamic> toJson() => {
"json1language": json1Language,
};
}
List<Json2> json2FromJson(String str) =>
List<Json2>.from(json.decode(str).map((x) => Json2.fromJson(x)));
String json2ToJson(List<Json2> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Json2 {
Json2({
this.json2Country,
this.json2Languages,
});
String json2Country;
List<Json2Language> json2Languages;
factory Json2.fromJson(Map<String, dynamic> json) => Json2(
json2Country: json["json2country"],
json2Languages: List<Json2Language>.from(
json["json2languages"].map((x) => Json2Language.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"json2country": json2Country,
"json2languages":
List<dynamic>.from(json2Languages.map((x) => x.toJson())),
};
}
class Json2Language {
Json2Language({
this.json2Language,
});
String json2Language;
factory Json2Language.fromJson(Map<String, dynamic> json) => Json2Language(
json2Language: json["json2language"],
);
Map<String, dynamic> toJson() => {
"json2language": json2Language,
};
}
class Json1Services {
static Future<List<Json1>> getData() {
String jsonString = '''
[
{
"json1language": "English"
},
{
"json1language": "French"
},
{
"json1language": "Spanish"
}
]
''';
return Future.value(json1FromJson(jsonString));
}
}
class Json2Services {
static Future<List<Json2>> getData() {
String jsonString = '''
[
{
"json2country": "Canada",
"json2languages": [
{
"json2language": "English"
},
{
"json2language": "French"
}
]
},
{
"json2country": "Mexico",
"json2languages": [
{
"json2language": "English"
},
{
"json2language": "Spanish"
}
]
},
{
"json2country": "France",
"json2languages": [
{
"json2language": "French"
}
]
}
]
''';
return Future.value(json2FromJson(jsonString));
}
}
class Json extends StatefulWidget {
#override
_JsonState createState() => _JsonState();
}
class _JsonState extends State<Json> {
List<Json1> _json1 = [];
List<Json2> _json2 = [];
#override
void initState() {
super.initState();
setState(() {
Json1Services.getData().then((data) {
setState(() {
_json1 = data;
});
});
Json2Services.getData().then((data) {
setState(() {
_json2 = data;
});
});
});
}
List<Json2> filtered = [];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (Json1 j1 in _json1)
RaisedButton(
child: Text(j1.json1Language),
onPressed: () => showDialog(
context: context,
builder: (_) {
filtered = [];
_json2.forEach((element) {
for (int i = 0;
i < element.json2Languages.length;
i++) {
if (element.json2Languages[i].json2Language ==
j1.json1Language) {
filtered.add(element);
}
}
});
return AlertDialog(
content: SizedBox(
width: 300,
height: 70,
child: Row(
children: [
Expanded(
flex: 2,
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
for (int i = 0;
i < filtered.length;
i++)
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
filtered[i].json2Country,
),
for (int j = 0;
j <
filtered[i]
.json2Languages
.length;
j++)
Column(children: [
Text(filtered[i]
.json2Languages[j]
.json2Language)
]),
],
)
],
),
)
],
),
),
);
}))
],
),
));
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Json(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

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

I have a JSON like this:
[
{
"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:
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.
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
]),
)
]))));
}
}
Edit 1:
I want to add 2 things as follows:
1. Add background image in TabBarView for each _tabs and each
isDayTime
For each continentName, there will be 2 images for Day or Night.
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. Display cityName on background image based on X Y percent position of image
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("'", "_");
}
So please help me update class DisplayCountry to be able to combine the 2 things above
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. 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).
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)
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
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. 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

How to show data from 2nd JSON based on the same value of String

I have 2 JSON file:
country.json
[
{
"countryName": "United States",
"countryContinent": "North America"
},
{
"countryName": "Germany",
"countryContinent": "Europe"
},
{
"countryName": "United Kingdom",
"countryContinent": "Europe"
}
]
continent.json
[
{
"continentName": "North America",
"continentCountry": [
"Canada",
"Mexico",
"Cuba"
],
"continentArea": 24790000,
"continentFlag": [
"https://www.countryflags.io/ca/shiny/64.png",
"https://www.countryflags.io/mx/shiny/64.png",
"https://www.countryflags.io/cu/shiny/64.png"
]
},
{
"continentName": "Europe",
"continentCountry": [
"Denmark",
"Finland",
"France"
],
"continentArea": 10180000,
"continentFlag": [
"https://www.countryflags.io/dk/shiny/64.png",
"https://www.countryflags.io/fi/shiny/64.png",
"https://www.countryflags.io/fr/shiny/64.png"
]
}
]
I want to build a list based on country.json, then for each value of countryContinent that == value of continentName => show data of value continentCountry from continent.json like this
So pls help me, this is main file:
import 'package:ask/model/continent_model.dart';
import 'package:ask/model/country_model.dart';
import 'package:ask/services/continent_services.dart';
import 'package:ask/services/country_service.dart';
import 'package:flutter/material.dart';
class Demo2 extends StatefulWidget {
Demo2() : super();
#override
_Demo2State createState() => _Demo2State();
}
class _Demo2State extends State<Demo2> {
List<Country> _country = [];
List<Continent> _continent = [];
#override
void initState() {
super.initState();
CountryServices.getCountry().then((countries) {
setState(() {
_country = countries;
});
});
ContinentServices.getContinent().then((continents) {
setState(() {
_continent = continents;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Demo 2')),
body: Column(
children: <Widget>[
for (Country country in _country)
Row(
children: <Widget>[
Expanded(child: Text(country.countryName)),
Expanded(child: Text(country.countryContinent)),
Expanded(child: Text('')), // How to show data of continentCountry
],
)
],
));
}
}
EDIT 2:
To avoid creating another post, I extend the following question:
in continent.json, there is more data about continentCountry ("Greenland"; "Panama"; "Jamaica") that matches the value of continentName("North America") like this:
continent.json (edit 2)
[
{
"continentName": "North America",
"continentArea": "Area1",
"continentCountry": [
"Canada",
"Mexico",
"Cuba"
],
"continentFlag": [
"https://www.countryflags.io/ca/shiny/64.png",
"https://www.countryflags.io/mx/shiny/64.png",
"https://www.countryflags.io/cu/shiny/64.png"
]
},
{
"continentName": "North America",
"continentArea": "Area2",
"continentCountry": [
"Greenland",
"Panama",
"Jamaica"
],
"continentFlag": [
"https://www.countryflags.io/gl/shiny/64.png",
"https://www.countryflags.io/pa/shiny/64.png",
"https://www.countryflags.io/jm/shiny/64.png"
]
},
{
"continentName": "Europe",
"continentArea": "Area3",
"continentCountry": [
"Denmark",
"Finland",
"France"
],
"continentFlag": [
"https://www.countryflags.io/dk/shiny/64.png",
"https://www.countryflags.io/fi/shiny/64.png",
"https://www.countryflags.io/fr/shiny/64.png"
]
},
{
"continentName": "Asia",
"continentArea": "Area4",
"continentCountry": [
"Japan"
],
"continentFlag": [
"https://www.countryflags.io/jp/shiny/64.png"
]
}
]
so I want to display all of the continentCountry of "North America" as follows:
Pls help
Expanded(
child: MyCountries(
continent: List<Continent>.from(_continent)..retainWhere((continent) =>
continent.continentName == country.countryContinent)
),
I create a widget MyCountries that receives a List, first I create a copy of that list (List.from) to avoid changing the original list _continent. RetainWhere keeps only the elements that sastisfy the condition. You wanted to keep only the continent where countryContinent == continentName
continent.continentName == country.countryContinent
and in the Mycountries widget
class MyCountries extends StatelessWidget{
final String countries;
MyCountries({List<Continent> continent}) :
this.countries = continent.reduce((prev, curr) =>
prev..continentCountry.addAll(curr.continentCountry)).continentCountry.join(' | ');
#override
Widget build(BuildContext context) {
return Text(countries);
}
}
it gets the continent List and reduce it to a single continent object with a continentCountry List with all the countries, then just apply continentCountry.join(' | ') to join all the Strings of that list and create your countries String that you will use in the Text widget
UPDATE
From what I understand of your comments there should only be one ocurrence countryContinent that == value of continentName, I thought there could be more (my bad), so maybe you should change it like this
Expanded(
child: MyArea(
continent: _continent.firstWhere((continent) => continent.continentName == country.countryContinent)
//This should return the first ocurrence, only one continent object
),
class MyArea extends StatelessWidget{
final String area;
MyArea({Continent continent}) :
this.area = continent.continentArea.toString(); //You need to give a string to the Text widget anyway
#override
Widget build(BuildContext context) {
return Text(area);
}
}
I suppose List is a list of urls of the flags
Expanded(
child: MyFlags(
continent: _continent.firstWhere((continent) => continent.continentName == country.countryContinent)
//This should return the first ocurrence, only one continent object
),
class MyFlags extends StatelessWidget{
final List<String> flags;
MyFlags ({Continent continent}) :
this.flags= continent.continentFlag;
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, //or try other alignments or using Spacer() between widget in children
children: [
for(String flag in flags)
Image.network(flag, height: 16, width: 25, fit: BoxFit.contain)
//A flag is wider thatn taller, but you could try other sizes if you want
]
);
}
}
UPDATE 2
Expanded(
child: MyFlags(
continent: List<Continent>.from(_continent)..retainWhere((continent) =>
continent.continentName == country.countryContinent)
),
class MyFlags extends StatelessWidget{
final List<String> flags;
final List<String> countries;
MyFlags ({List<Continent> continent}) :
this.flags = continents.expand<String>((continent) => continent.continentFlag).toList(),
this.countries = continents.expand<String>((continent) => continent.continentCountry).toList(),
assert(flags.length == countries.length); //They should have the same length
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, //or try other alignments or using Spacer() between widget in children
children: [
for(int i = 0; i < flags.length; i++)
Column(
children: [
Image.network(flags[i], height: 16, width: 25, fit: BoxFit.contain),
Text(countries[i])
]
)
]
);
}
}
UPDATE 3
Expanded(
child: MyFlags(
continent: List<Continent>.from(_continent)..retainWhere((continent) =>
continent.continentName == country.countryContinent)
),
class MyFlags extends StatelessWidget{
final List<Continent> continent;
MyFlags({this.continent});
#override
Widget build(BuildContext context) {
return Column(
children: [
for(int i = 0; i < continent.length; i++)
Row(
children: [
Text(continent[i].continentArea.toString()), // or 'Area ${i+1}' if you want to show Area 1, Area 2, etc
for(String flag in continent[i].continentFlag)
Image.network(flags[i], height: 16, width: 25, fit: BoxFit.contain),
]
)
]
);
}
}
you can loop over the list of strings List<String> in an seperate function like List<Text> getSameContinentCountries.
class for data representation
Country
class Country {
String countryName;
String countryContinent;
Country({this.countryName, this.countryContinent});
factory Country.fromJson(Map<String, dynamic> json) {
return Country(
countryName: json['countryName'],
countryContinent: json['countryContinent'],
);
}
}
Continent
class Continent {
String continentName;
List<String> continentCountry;
Continent({this.continentName, this.continentCountry});
factory Continent.fromJson(Map<String, dynamic> json) {
return Continent(
continentName: json['continentName'],
continentCountry: json['continentCountry'],
);
}
}
You can now generate the List<Country> _country and List<Continent> _continent in the initState with:
// TODO generate Lists
for (Map<String, dynamic> _json in continentListResponse) {
continent2 = Continent.fromJson(_json);
this._continent.add(continent2);
}
for (Map<String, dynamic> _json in countryListResponse) {
country2 = Country.fromJson(_json);
this._country.add(country2);
}
function for Countries in Continents
Text only
List<Text> getSameContinentCountries({#required String countryContinent}) {
int continentIndex = this
._continent
.indexWhere((continent) => continent.continentName == countryContinent);
List<String> _cC = this._continent[continentIndex].continentCountry;
List<Text> wrapText = [Text('no Countries found')];
if (_cC.length > 0) {
wrapText = [];
for (String country in _cC) {
wrapText.add(Text(country));
}
}
return wrapText;
}
all widgets
List<Widget> getSameContinentCountries({#required String countryContinent}) {
// returns a List of any Widget, many combinations possible
// such as chips, cards etc.
int continentIndex = this
._continent
.indexWhere((continent) => continent.continentName == countryContinent);
List<String> _cC = this._continent[continentIndex].continentCountry;
List<Widget> wrapText = [Text('no Countries found')];
if (_cC.length > 0) {
wrapText = [];
for (String country in _cC) {
// you may want to use Padding
wrapText.add(Padding(
padding: const EdgeInsets.symmetric(horizontal: 2),
child: Text(country),
));
// simple | divider with Text widget
wrapText.add(Padding(
padding: const EdgeInsets.symmetric(horizontal: 2),
child: Text('|'),
));
// or use the VerticalDivider which needs a parent with height
wrapText.add(
Container(
height: 15,
child: VerticalDivider(
color: Colors.red,
width: 4,
thickness: 2,
)),
);
// or use any other widget, eg. chips
wrapText.add(Chip(
label: Text(country),
));
}
}
// remove the last "divider"
wrapText.removeLast();
return wrapText;
}
use function generated widget
Here I would suggest you to use the Wrap Widget.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Demo 2')),
body: Column(
children: <Widget>[
for (Country country in _country)
Row(
children: <Widget>[
Expanded(child: Text(country.countryName)),
Expanded(child: Text(country.countryContinent)),
Expanded(
child: Wrap(
children: getSameContinentCountries(
countryContinent: country.countryContinent)),
),
],
)
],
));
}
Link to the result Image
UPDATE
I can't comment on another answer yet, so I have to include this short info in my answer.
To use the images, just return an Image widget, you may want to wrap it in a Sized Box or an Button / GestureDetctor for click events.
List<Widget> getFlags({#required String countryContinent}) {
// returns a List of any Widget, many combinations possible
// such as chips, cards, images, buttons etc.
int continentIndex = this
._continent
.indexWhere((continent) => continent.continentName == countryContinent);
List<String> _cF = this._continent[continentIndex].continentFlag;
List<Widget> wrapWidget = [Text('no Countries found')];
if (_cF.length > 0) {
wrapWidget = [];
for (String flag in _cF) {
// add the Flags
wrapWidget.add(Image.network(flag));
}
}
return wrapWidget;
}
Result:
UPDATE 2
To have access to all flags with the same continentName, you have to extract the matching elemtens and then loop over the new List<Continent>.
Change the code of the getFlags function like so:
List<Widget> getFlags({#required String countryContinent}) {
// returns a List of any Widget, many combinations possible
// such as chips, cards, images, buttons etc.
//! UPDATE
List<Continent> sameContient = this
._continent
.where((continent) => continent.continentName == countryContinent)
.toList();
List<String> _cF = [];
for (Continent sc in sameContient) {
for (String flag in sc.continentFlag) {
_cF.add(flag);
}
}
List<Widget> wrapWidget = [Text('no Countries found')];
if (_cF.length > 0) {
wrapWidget = [];
for (String flag in _cF) {
// add the Flags
wrapWidget.add(
Image.network(flag, height: 16, width: 25, fit: BoxFit.contain));
}
}
return wrapWidget;
}
and you will get the following result: