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),
),
);
}
}
Related
I am trying to get the productID from the list of products in my cart bloc and then merge the list with the cart quantity so that the end result is this:
"aos": [
{
"product_id": 10,
"quantity": 1
},
{
"product_id": 11,
"quantity": 2
}
],
I have tried to encode the map as a json, but there is still no option for me to access the id of each product. Please can someone point me in the right direction?
My Checkout Screen:
class CheckoutScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
// final List<dynamic> aos;
return Scaffold(
appBar: AppBar(
backgroundColor: buttonBG,
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const ShippingAddress(),
const SizedBox(height: 20),
BlocBuilder<CartBloc, CartState>(
builder: (context, state) {
if (state is CartLoaded) {
Map aos = state.cart.productQuantity(state.cart.products);
var pio = aos.keys.toList();
String productInstance = json.encode(pio);
debugPrint(productInstance);
return Expanded(
child: SizedBox(
height: 400,
child: ListView.builder(
itemCount: aos.keys.length,
itemBuilder: (BuildContext context, int index) {
final pIndex = aos.keys.elementAt(index);
final qIndex = aos.values.elementAt(index);
return ProductCard.summary(
product: pIndex,
quantity: qIndex,
);
},
),
),
);
}
return const Text('Something went wrong');
},
),
],
));
}
}
Is there a way to achieve that because i am using bloc?
Below is my CartModel:
import 'package:afia4_shopping_app/logic/models/product_models.dart';
import 'package:equatable/equatable.dart';
class Cart extends Equatable {
final List<ProductModel> products;
const Cart({this.products = const <ProductModel>[]});
#override
List<Object?> get props => [products];
Map productQuantity(products) {
var quantity = {};
products.forEach((product) {
if (!quantity.containsKey(product)) {
quantity[product] = 1;
} else {
quantity[product] += 1;
}
});
return quantity;
}
}
For the cartBloc:
class CartBloc extends Bloc<CartEvent, CartState> {
CartBloc() : super(CartLoading()) {
on<LoadCart>(_onLoadCart);
on<AddProduct>(_onAddProduct);
on<RemoveProduct>(_onRemoveProduct);
}
void _onLoadCart(
LoadCart event,
Emitter<CartState> emit,
) async {
emit(CartLoading());
try {
await Future<void>.delayed(const Duration(seconds: 1));
emit(CartLoaded());
} catch (_) {
emit(CartError());
}
}
void _onAddProduct(
AddProduct event,
Emitter<CartState> emit,
) {
if (state is CartLoaded) {
try {
emit(
CartLoaded(
cart: Cart(
products: List.from((state as CartLoaded).cart.products)
..add(event.product),
),
),
);
} on Exception {
emit(CartError());
}
}
}
void _onRemoveProduct(
RemoveProduct event,
Emitter<CartState> emit,
) {
if (state is CartLoaded) {
try {
emit(
CartLoaded(
cart: Cart(
products: List.from((state as CartLoaded).cart.products)
..remove(event.product),
),
),
);
} on Exception {
emit(CartError());
}
}
}
}
For the productModel:
class ProductModel {
ProductModel(
{this.name,
this.id,
});
String? name;
int? id;
factory ProductModel.fromJson(Map<String, dynamic> json) => ProductModel(
name: json["name"],
id: json["id"],
Map<String, dynamic> toJson() => {
"name": name,
"id": id
};
List<ProductModel> productsFromJson(String str) => List<ProductModel>.from(
json.decode(str).map((x) => ProductModel.fromJson(x)));
String productsToJson(List<ProductModel> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
}
If you add this dependency: collection: ^1.16.0, and include that:
import "package:collection/collection.dart";
You can use the groupListsBy() method and run:
var group = cart.products.groupListsBy((element) => element.id).map((key, value) => MapEntry(key, value.length));
Which will give you a Map<int?, int> grouping product id to the quantity.
You can then present it as you wish in your UI.
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.
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
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:
How do I populate this JSON list into dropdown button?
{
"status": true,
"message": "success",
"data": {
"list": [
{
"idattribute": "2",
"attrName": "BBQ"
},
{
"idattribute": "1",
"attrName": "FRUIT JUICE"
}
]
}
}
class _YourPageState extends State<YourPage> {
Map yourJson = {
"status": true,
"message": "success",
"data": {
"list": [
{"idattribute": "2", "attrName": "BBQ"},
{"idattribute": "1", "attrName": "FRUIT JUICE"}
]
}
};
int _value = 1;
List<DropdownMenuItem<int>> _menuItems;
#override
void initState() {
super.initState();
List dataList = yourJson["data"]["list"];
_menuItems = List.generate(
dataList.length,
(i) => DropdownMenuItem(
value: int.parse(dataList[i]["idattribute"]),
child: Text("${dataList[i]["attrName"]}"),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: DropdownButton<int>(
items: _menuItems,
value: _value,
onChanged: (value) => setState(() => _value = value),
),
),
);
}
}