What I'm trying to do is that if someone opens part 1 (JSON), then their respective article should be visible on the next screen. I was able to list out all the parts but how to show their respective articles on the next screen. For example, the first part contains 3 articles and the second part would contain 3
basically, every part would contain a different number of articles.
JSON
[
[ // Article Names
{
"ArtNo": "0",
"Name": "PREAMBLE",
},
{
"ArtNo": "1",
"Name": "Name and territory of the Union.",
},
{
"ArtNo": "2",
"Name": "Admission or establishment of new States.",
},
{
"ArtNo": "3",
"Name": "Formation of new States and alteration of areas, boundaries or names of existing States.",
},
{
"ArtNo": "4",
"Name": "Laws made under articles 2 and 3 to provide for the amendment of the First and the Fourth Schedules and supplemental, incidental and consequential matters.",
},
{
"ArtNo": "5",
"Name": "Citizenship at the commencement of the Constitution."
}
],
[ // Article Parts
{
"PartNo": "I",
"Name": "THE UNION AND ITS TERRITORY",
"Articles": ["1", "2", "3"]
},
{
"PartNo": "II",
"Name": "CITIZENSHIP",
"Articles": ["4", "5"]
}
]
]
Flutter (list out all parts)
class _ConstitutionPartsState extends State<ConstitutionParts> {
Future parts() async {
final data = await rootBundle.loadString('assets/json/file.json');
final jsonResult = jsonDecode(data);
final parts = jsonResult[1];
return parts;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: parts(), builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if(snapshot.connectionState == ConnectionState.waiting){
return const CircularProgressIndicator(color: Colors.deepOrangeAccent);
}
return Container(
margin: const EdgeInsets.all(25.0),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: const EdgeInsets.only(bottom: 20.0),
child: ListTile(
title: Text(snapshot.data[index]['Name']),
),
);
},
),
);
},
),
);
}
}
any help would be appreciated! thank you
You can use where condition for filteration,
more info
Here's the Full Code
class _ConstitutionPartsState extends State<ConstitutionParts> {
Future parts() async {
final data = await rootBundle.loadString('assets/json/file.json');
final jsonResult = jsonDecode(data);
return jsonResult; // return whole json
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: parts(), builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if(snapshot.connectionState == ConnectionState.waiting){
return const CircularProgressIndicator(color: Colors.deepOrangeAccent);
}
return Container(
margin: const EdgeInsets.all(25.0),
child: ListView.builder(
itemCount: snapshot.data[1].length,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: const EdgeInsets.only(bottom: 20.0),
child: ListTile(
title: Text(snapshot.data[index]['Name']),
onTap():{
//navigate to name page with
NewPage(articePartIndex : index, json: snapshot.data)
}
),
);
},
),
);
},
),
);
}
}
NewPage {
final List<List<dynamic>> json;
final int index;
#override
Widget build(BuildContext context) {
var articles = json[1][index]['Articles'];
var filteredArticleList = json[0].where((a) => articles.contains(a['ArtNo'])).toList();
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(filteredArticleList[index]['Name']),
),
});
}
}
Related
I'm trying to populate a ReorderableSliverList with data from an API using FutureBuilder.
Model (RouteOrderTable)
RouteOrderTable(
{required this.deliveryPeriod,
required this.driverId,
required this.processId,
required this.routeId,
required this.routeLines,
required this.status,
required this.vanId});
RouteOrderTable.fromJson(Map<String, dynamic> json) {
deliveryPeriod = json['deliveryPeriod'];
driverId = json['driverId'];
processId = json['processId'];
routeId = json['routeId'];
if (json['routeLines'] != null) {
routeLines = <RouteOrderLine>[];
json['routeLines'].forEach((v) {
routeLines!.add(new RouteOrderLine.fromJson(v));
});
}
status = json['status'];
vanId = json['vanId'];
}
Model (RouteOrderLine)
RouteOrderLine(
{required this.city,
required this.custAccount,
required this.custName,
required this.invoiceId,
required this.deliveryDate,
required this.productType,
required this.routeId,
required this.salesId,
required this.volume,
required this.weight});
RouteOrderLine.fromJson(Map<String, dynamic> json) {
city = json['city'];
custAccount = json['custAccount'];
custName = json['custName'];
deliveryDate = DateTime.tryParse(json['deliveryDate']);
invoiceId = json['invoiceId'];
productType = json['productType'];
routeId = json['routeId'];
salesId = json['salesId'];
volume = json['volume'];
weight = json['weight'];
}
JSON Data
[
{
"deliveryPeriod": 1,
"driverId": "",
"processId": "PRO000475",
"routeId": "ROU001804",
"routeLines": [
{
"city": "Naxxar",
"custAccount": "CUST010917",
"custName": "",
"deliveryDate": "2021-07-06T12:00:00",
"invoiceId": "",
"productType": 0,
"routeId": "ROU001804",
"salesId": "SO002496",
"volume": 0.011113889,
"weight": 8.700435
}
],
"status": 10,
"vanId": "TFI 943"
},
{
"deliveryPeriod": 2,
"driverId": "",
"processId": "PRO000475",
"routeId": "ROU001805",
"routeLines": [
{
"city": "Birkirkara",
"custAccount": "CUST010916",
"custName": "",
"deliveryDate": "2021-07-06T12:00:00",
"invoiceId": "",
"productType": 0,
"routeId": "ROU001805",
"salesId": "SO002497",
"volume": 0.005556111,
"weight": 4.349565
}
],
"status": 30,
"vanId": "HBA 804"
}
]
Screen - One of the issues here is that I cannot use removeAt() and insert() for the onReorder() because they aren't defined for the type Future.
class _SliverScreenState extends State<SliverScreenState> {
late Future<List<RouteOrderTable>> futureRouteTable;
#override
void initState() {
super.initState();
futureRouteTable = fetchData();
}
#override
Widget build(BuildContext context) {
void _onReorder(int oldIndex, int newIndex) {
/* setState(() {
Widget row = futureRouteTable!.removeAt(oldIndex);
futureRouteTable.insert(newIndex, row);
});*/
}
ScrollController _scrollController =
PrimaryScrollController.of(context) ?? ScrollController();
final someOtherSliver = SliverToBoxAdapter();
return FutureBuilder<List<RouteOrderTable>>(
future: futureRouteTable,
builder: (context, snapshot) {
Widget routeOrderListSliver;
final List<RouteOrderTable>? posts = snapshot.data;
if (snapshot.hasData) {
routeOrderListSliver = CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
SliverAppBar(
flexibleSpace: FlexibleSpaceBar(
title: Text('ROUTE NO'),
),
),
ReorderableSliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ListTile(
title: Text(posts![index].routeId.toString(),
style: TextStyle(fontSize: 20.0),),
),
childCount: posts!.length,
),
onReorder: _onReorder,
),
],
);
}else {
routeOrderListSliver = Center(child: SliverToBoxAdapter(child: CircularProgressIndicator(),));
}
return CustomScrollView(
slivers: <Widget>[
someOtherSliver,
routeOrderListSliver
],
);
},
);
}
}
FetchData
Future<List<RouteOrderTable>> fetchData() async {
final response =
await http.get(Uri.parse('https://10.0.2.2:7038/api/route/1?siteId=1'));
try {
if (response.statusCode == 200) {
List<RouteOrderTable> lstRouteOrderTable = [];
Iterable l = jsonDecode(response.body);
lstRouteOrderTable = List<RouteOrderTable>.from(l.map((model) => RouteOrderTable.fromJson(model)));
return lstRouteOrderTable;
} else {
throw Exception('Failed to load route');
}
} catch (e) {
print(e);
}
return new List<RouteOrderTable>.empty();
}
ListView.builder
ListView _buildRouteOrderTable(
BuildContext context, List<RouteOrderTable>? lstRouteTable) {
return ListView.builder(
itemCount: lstRouteTable == null ? 0 : lstRouteTable.length,
//padding: EdgeInsets.all(8),
itemBuilder: (context, index) {
return Card(
elevation: 4,
child: ListTile(
title: Text(
lstRouteTable == null ? "" : lstRouteTable[index].routeId.toString(),
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(
lstRouteTable == null ? "" : lstRouteTable[index].processId.toString()),
),
);
},
);
}
You can define the _onReorder function inside the FutureBuilder and then use the snapshot to create a variable of RouteOrderTable.
This will give you access to the removeAt & insert functions.
There are two ways of doing this.
final List<RouteOrderTable>? posts = snapshot.data;
if (snapshot.hasData) {
void _onReorder(int oldIndex, int newIndex){
//1. Define the function here and then pass it as a parameter.
}
routeOrderListSliver = CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
SliverAppBar(
flexibleSpace: FlexibleSpaceBar(
title: Text('ROUTE NO'),
),
),
ReorderableSliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ListTile(
title: Text(posts![index].routeId.toString(),
style: TextStyle(fontSize: 20.0),),
),
childCount: posts!.length,
),
onReorder: (int oldIndex, int newIndex){
// OR
// 2. You can directly create the function here.
},
),
],
);
}
Both ways, you get access to the posts variable where you can do your operations.
My current JSON file is in this format:
{
"feed":{
"entry":[
{
"id":{
"$t":"somedata"
},
"title":{
"type":"text",
"$t":"Stack Smash"
},
"content":{
"type":"text",
"$t":"somedata"
},
},
{
"id":{
"$t":"somedata"
},
"title":{
"type":"text",
"$t":"Stack Smash"
},
"content":{
"type":"text",
"$t":"somedata"
},
}
]
}
}
I need to parse json['feed']['entry'] and the contents within that (id, title, content).
This is my current implementation:
Future<List<Feed>> getData() async {
List<Feed> list;
String link = '$url';
var res = await http.get(link);
if (res.statusCode == 200) {
var data = json.decode(res.body);
var rest = data["feed"]["entry"] as List;
list = rest.map<Feed>((json) => Feed.fromJson(json)).toList();
}
return list;
}
}
class Feed {
ImageJSON image;
Feed({this.image});
factory Feed.fromJson(Map<String, dynamic> json) {
return Feed(
image: ImageJSON.fromJson(json["title"]));
}
}
class ImageJSON {
String image;
ImageJSON({this.image});
factory ImageJSON.fromJson(Map<String, dynamic> json) {
return ImageJSON(
image: json["\$t"] as String,
);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: gameAppBar(),
body: FutureBuilder(
future: getData(),
builder: (context, snapshot) {
return snapshot.data != null
? CustomScrollView(slivers: <Widget>[
SliverPadding(
padding: const EdgeInsets.only(
top: 20.0, left: 8, right: 8),
sliver: gamesGrid(snapshot.data)) ])
: Container(child: Text(snapshot.error));
}));
}
Widget gamesGrid(List<Feed> feed) {
return SliverPadding(
padding: const EdgeInsets.only(left: 8, right: 8),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 2 / 2.9),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int i) {
return Column(
children: [
GridTile(
child: Image.network(
'${feed[i].image}',
fit: BoxFit.cover,
),
),
],
);
},
childCount: 100,
),
),
);
}
The code prints Instance of ImageJSON, how do I get my String from the ImageJSON?
GridTile(
child: Image.network(
'${feed[i].image.image}',
fit: BoxFit.cover,
),
),
I have a JSON list of products of two companies, Apple and Windows. The below is my JSON data structure:
[
{
"category": "Apple",
"name": "Macbook Air"
},
{
"category": "Apple",
"name": "Macbook Pro"
},
{
"category": "Microsoft",
"name": "Surface"
},
{
"category": "Apple",
"name": "iPad"
},
{
"category": "Microsoft",
"name": "Windows"
},
{
"category": "Apple",
"name": "Siri"
},
{
"category": "Microsoft",
"name": "Office"
}
]
I am trying to create a listview of either Apple or Microsoft category. But in my current listview, it shows all the products from the JSON file:
List data;
Future<String> loadJsonData() async{
var jsonText = await rootBundle.loadString('assets/json/products.json');
setState(() => data = json.decode(jsonText));
}
#override
void initState() {
this.loadJsonData();
super.initState();
}
The above code loads all the products of both companies. But how can I load only the products of Apple or Microsoft?
This is my ListView
Container(
child: data == null ? Container() : ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return Container(
child: ListTile(
title: Text(
data[index]['name'],
),
),
);
}
),
),
You can do something like this:
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xfff5f5f5),
appBar: AppBar(
title: Text('Demo'),
),
body: Container(
child: data == null
? Container()
: ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return Container(
child: ((data[index]['category'] == "Apple")
? ListTile(title: Text(data[index]['name']))
: Container()));
}),
));
}
}
You can remove items except for Apple or Microsoft:
data.removeWhere((item) {
item['category'] != 'Apple' || item['category'] != 'Microsoft'
});
See also List Dartdoc
I am trying to load the JSON from a local variable in my class file and convert the JSON to a list of objects using PODO class but I don't know why the list isn't getting generated. I am so much frustrated. Please help me where I am doing wrong.
I have tried every possible way by manipulating the code, even the same code works for a different JSON format with its PODO class.
Thank you.
Flutter class source code:
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
#override
_Demo createState() => _Demo();
}
class _Demo extends State<Demo> {
final localJson = '''
[
{
"message": "Some message here 1"
},
{
"message": "Some message here 2"
},
{
"message": "Some message here 3"
}
]
''';
Widget getCommentItem({#required PodoClass item}) {
return Text(item.message);
}
Future<List<PodoClass>> fetchComments() async {
return compute(parseJson, localJson);
}
List<PodoClass> parseJson(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<PodoClass>((json) => PodoClass.fromJson(json)).toList();
}
Widget _bodyBuild({#required List<PodoClass> items}) {
return ListView.builder(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 20),
itemCount: items.length,
itemBuilder: (BuildContext ctxt, int index) {
return getCommentItem(item: items[index]);
});
}
Widget body() {
return FutureBuilder<List<PodoClass>>(
future: fetchComments(),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? _bodyBuild(items: snapshot.data)
: Center(child: Text('Loading..'));
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Comments",
),
Text("Battle ID", style: TextStyle(fontSize: 12))
])),
body: body());
}
}
// podo class
class PodoClass {
String message;
PodoClass({this.message});
PodoClass.fromJson(Map<String, dynamic> json) {
message = json['message'];
}
}
you must move parseJson function outside the class
it should be top level function
https://api.flutter.dev/flutter/foundation/compute.html
The callback argument must be a top-level function, not a closure or
an instance or static method of a class.
The compute does not work in this case, simply call the function.
I've included a delay which may help you with testing.
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
#override
_Demo createState() => _Demo();
}
class _Demo extends State<Demo> {
final localJson = '''
[
{
"message": "Some message here 1"
},
{
"message": "Some message here 2"
},
{
"message": "Some message here 3"
}
]
''';
Widget getCommentItem({#required PodoClass item}) {
return Text(item.message);
}
Future<List<PodoClass>> fetchComments() async {
await Future.delayed(Duration(seconds: 5));
return parseJson(localJson);
}
List<PodoClass> parseJson(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<PodoClass>((json) => PodoClass.fromJson(json)).toList();
}
Widget _bodyBuild({#required List<PodoClass> items}) {
return ListView.builder(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 20),
itemCount: items.length,
itemBuilder: (BuildContext ctxt, int index) {
return getCommentItem(item: items[index]);
});
}
Widget body() {
return FutureBuilder<List<PodoClass>>(
future: fetchComments(),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? _bodyBuild(items: snapshot.data)
: Center(child: Text('Loading..'));
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Comments",
),
Text("Battle ID", style: TextStyle(fontSize: 12))
])),
body: body());
}
}
// podo class
class PodoClass {
String message;
PodoClass({this.message});
PodoClass.fromJson(Map<String, dynamic> json) {
message = json['message'];
}
}
this json has two data first only name and second is inside there also name which is service name. ex 'Travel and Stay' and 'Religious' is main name which has to be displayed in expansion tile name and The 'Church/ Baptism' and 'Hindu Temples' is a subitem which is displayed inside checkbox list tile. Hope you understand :slightly_smiling_face: Please Help me
class _MyHomePageState extends State<MyHomePage> {
var lovCountryServices = [
{
"services": [
{
"service_group_id": 22,
"service_category": "B",
"name": "Air Tickets",
"id": 228
},
{
"service_group_id": 22,
"service_category": "W",
"name": "Boys Hostel",
"id": 229
},
],
"name": "Travel and Stay",
"is_active": true,
"id": 22
},
{
"services": [
{
"service_group_id": 19,
"service_category": "B",
"name": "Church/ Baptism",
"id": 193
},
{
"service_group_id": 19,
"service_category": "B",
"name": "Hindu Temples",
"id": 194
}
],
"name": "Religious",
"is_active": true,
"id": 19
}
];
List<_Result> _results = [];
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: <Widget>[
ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8.0),
itemCount: lovCountryServices.length,
itemBuilder: (BuildContext context, int index) {
var item = lovCountryServices[index];
var items= lovCountryServices[index]['services'];
return ExpansionTile(
title: Text(item['name']),
children: <Widget>[
CheckboxListTile(
title: Text("temp"),
value: item['is_active'],
onChanged: (val) {
setState(() {
item['is_active'] = val;
});
},
),
],
);
},
),
RaisedButton(
onPressed: () => print("sending to backend"),
child: Text("SEND"),
)
],
)),
);
}
}
I want thw data in checkbox list tile right there is dummy data called TEMP and i want the data from json right now in json there is 'Boys Hostel' this data needs to comes inside the checkbox listtile. Hope you undestand please help me
Working Code: You can use a Map variable to store boolean value.
Map<String, bool> _selection = {};
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Dummy'),
),
body: Center(
child: Column(
children: <Widget>[
ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8.0),
itemCount: lovCountryServices.length,
itemBuilder: (BuildContext context, int index) {
var item =
lovCountryServices[index]; // should be outside build function
List items = item['services']; // should be outside build function
return ExpansionTile(
title: Text(item['name']),
children: List.generate(items.length, (i) {
_selection[items[i]['name']] =
_selection[items[i]['name']] ?? item['is_active'];
return CheckboxListTile(
title: Text(items[i]['name']),
value: _selection[items[i]['name']],
onChanged: (val) {
setState(() {
_selection[items[i]['name']] = val;
});
},
);
}),
);
},
),
RaisedButton(
onPressed: () => print("sending to backend"),
child: Text("SEND"),
)
],
)),
);
}