Flutter JSON duplicate index - json

I have a list of users that I am reading from JSON.
This is the JSON file:
{
"Dependents": [
{
"Name": "Kim",
"Relationship": "Parent"
},
{
"Name": "Tim",
"Relationship": "Spouse"
}
]
}
This is the model class:
new_fifth_model.dart
class NewFifthModel {
String name;
String relationship;
NewFifthModel(this.name, this.relationship);
}
And this is the class to bring out the users in a list.
NewFifth.dart
import 'package:flutter/material.dart';
import 'package:emas_app/model/new_fifth_model.dart';
import 'dart:convert';
import 'dart:async' show Future;
import 'package:http/http.dart' as http;
final String url = "http://crm.emastpa.com.my/MemberInfo.json";
final int page = 5;
//Future to get list of dependent names
Future<List<NewFifthModel>> fetchUserInfo() async{
var response = await http.get(url, headers: {"Accept": "application/json"});
List data = json.decode(response.body)["Dependents"];
var fifthmodel = <NewFifthModel>[];
data.forEach((f) => fifthmodel.add(new NewFifthModel(f["Name"], f["Relationship"])));
print(fifthmodel);
return fifthmodel;
}
class NewFifth extends StatefulWidget {
#override
_FifthState createState() => _FifthState();
}
class _FifthState extends State<NewFifth> {
List<NewFifthModel> fifthList;
#override
void initState() {
super.initState();
if (fifthList == null) {
fetchUserInfo().then((data) {
this.setState(() {
fifthList = data;
});
});
}
}
#override
Widget build(BuildContext context) {
//body widget
Widget _createBody() {
if(fifthList == null){
return new Center(
child: new CircularProgressIndicator(),
);
}
else{
return new ListView.builder(
shrinkWrap: true,
itemCount: fifthList.length,
itemBuilder: (context, index){
return new Column(
children: fifthList.map((f){
return new Card(
child: new ListTile(
title: new Text(f.name),
subtitle: new Text(f.relationship),
trailing: new Text(index.toString()),
onTap: (){
makeDialog(index.toString());
},
),
);
}).toList(),
);
});
}
}
return new Scaffold(
body: _createBody(),
);
}
}
This is the output on the screen.
The problem I am having (as you can see in the picture) is that the index number I put in the trailing part of the ListTile is duplicating and I really need the index number in order to proceed.
How do I rectify this problem?
Any help is very much appreciated.

you are creating 2 list here, you are recreating a Column with the entire list inside the item build, the ListView.builder is already taking care of iterating on your list using the itemCount.
itemBuilder: (context, index) {
final f = fifthList[index];
return Card(
child: new ListTile(
title: new Text(f.name),
subtitle: new Text(f.relationship),
trailing: new Text(index.toString()),
onTap: (){
makeDialog(index.toString());
},
),
);
}

Looks like you have only 2 items in the JSON object but you are showing 4.
I think you meant to only show 2? If so, in your itemBuilder function, you should do this:
return new ListView.builder(
shrinkWrap: true,
itemCount: fifthList.length,
itemBuilder: (context, index) {
var f = fifthList[index];
return new Card(
child: new ListTile(
title: new Text(f.name),
subtitle: new Text(f.relationship),
trailing: new Text(index.toString()),
onTap: () {
makeDialog(index.toString());
},
),
);
});
You were using .map() which looped through the list again on each item. You had 2 items, so you ended up with 4. If you had 3, it would show 6 items, and so on.

Related

parsing JSON file in flutter

I am parsing this type of JSON file in flutter but I am getting error.
initially, I am able to print the whole data in the console but when I want only the name it gives me an error.
bellow in the image for JSON file.
this the code that I wrote for parsing which gives me 1st value of name as "Abul-Abbas" then it throws an error.
import 'package:flutter/material.dart';
import 'package:flutter_appnew/Constants.dart';
import 'package:flutter_appnew/network.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
const URL ='https://xxxx-api.someapp.com/xxx';
class TabData extends StatefulWidget {
#override
_TabDataState createState() => _TabDataState();
}
class _TabDataState extends State<TabData> {
List image = [];
List Name = [];
#override
void initState() {
super.initState();
this.fetchUser();
}
fetchUser() async{
var response = await http.get(URL);
print(response.statusCode);
if(response.statusCode == 200){
var items = json.decode(response.body)[0]['name'];
print(items);
setState(() {
Name = items;
});
}
else{
setState(() {
Name = [];
});
}
}
#override
Widget build(BuildContext context) {
return ListView.separated(
itemCount: Name.length,
itemBuilder: (BuildContext context, int index){
return getCard(context, index);
},
separatorBuilder: (context, index) => Divider(thickness: 2,),
);
}
Widget getCard(BuildContext context, int index){
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
title: Row(
children: [
Container(
height: 60,
width: 60,
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(30),
image: DecorationImage(
image: NetworkImage('${image[index]}'),
fit: BoxFit.cover,
),
),
),
SizedBox(width: 20,),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('${Name[index]}', style: kListTileTextNameStyle,),
],
),
],
),
),
),
);
}
}
and the same is happening with the image I am not able to show it.
please help this is my first time with JSON file parsing.
I can advice you to try to avoid the dynamic type. Your
var items = json.decode(response.body)[0]['name'];
assigns the items to be the type String and the value is "Abul-Abbas". After that you assign Name which is a List to items which is of type String.
What you actually want to do is to add all names to the list (I guess).
So you should do:
response.body.forEach((element) {
Name.add(element["name"]);
});
I also recommend Renaming Name to names.
If you just wanted the first value of your response, then Name should be of type String.

Flutter transferring Json data to another page

Hey I have the problem that I want to transfer the data from a ListTile which was pressed. So i can read the data from a Json file on another page.
I have already written in the code where I want to get something where. I hope you know what I mean if not just ask under here. I always look in and answer. I've been through all sorts of sites but none of them helped me. So here now. You are my last hope
So hier is my Code
class PokemonDB extends StatefulWidget {
_PokemonDB createState() => _PokemonDB();
}
class _PokemonDB extends State<PokemonDB> {
List pokemon = const [];
Future loadPokemon() async {
var content = await rootBundle.loadString("json/pokemon.json");
var collection = json.decode(content);
setState(() {
pokemon = collection;
});
}
void initState() {
loadPokemon();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 0,
backgroundColor: Colors.black,
),
body: ListView.separated(
separatorBuilder: (BuildContext context, int index) => Divider(),
itemCount: pokemon.length,
itemBuilder: (BuildContext context, int index) {
var pokemonn = pokemon[index];
return Container(
child: ListTile(
onTap: () async{
Map name = await Navigator.push(
context,
MaterialPageRoute<Map>(builder: (BuildContext context) {
return ShinyHuntCounter(); <-- from here I want the data on which click was
},
),);
},
isThreeLine: false,
title: Text(
pokemonn['name'],
style: TextStyle(
fontFamily: 'pokemon',
fontSize: 30,
color: Colors.black
),
),
leading: Image(image: AssetImage(pokemonn['image'])),
),
decoration:
BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(50)),
color: Color.fromRGBO(234, 180, 59, 1),
),
);
},
),
);
}
}
I think it would be simple like this,
In your ShinyHuntCounter class,
class ShinyHuntCounter extends StatefulWidget {
final String pokemonn;
const ShinyHuntCounter(this.pokemonn);
#override
ShinyHuntCounterState createState() => ShinyHuntCounterState();
}
class ShinyHuntCounterState extends State<ShinyHuntCounter> {
#override
Widget build(BuildContext context) {
return Text(widget.pokemonn); // Here you direct access using widget
}
}
and for passing the data, do something like this,
MaterialPageRoute<Map>(builder: (BuildContext context) {
return ShinyHuntCounter(pokemonn['name']); }, )
Hope that suits your case.

Flutter how to save list data locally

I am building a to-do list app and I would like to store the data locally such that every time I open the app, I get all the tasks that I had created previously. I am new to flutter and this is my first app. I already tried saving the data to a file, creating a JSON file to save the data and tried using a database. Nothing seems to work. Can someone help me with this?
This is my code: -
import 'package:flutter/material.dart';
class toDoList extends StatefulWidget
{
bool data = false;
#override
createState()
{
return new toDoListState();
}
}
class toDoListState extends State<toDoList>
{
List<String> tasks = [];
List<bool> completedTasks = [];
List<String> descriptions = [];
List<bool> importance = [];
#override
Widget build(BuildContext context)
{
return new Scaffold
(
body: buildToDoList(),
floatingActionButton: new FloatingActionButton
(
onPressed: addToDoItemScreen,
tooltip: 'Add Task',
child: new Icon(Icons.add),
),
);
}
Widget buildToDoList()
{
return new ListView.builder
(
itemBuilder: (context, index)
{
if(index < tasks.length)
{
if(tasks[index] == "#45jiodg{}}{OHU&IEB")
{
tasks.removeAt(index);
descriptions.removeAt(index);
importance.removeAt(index);
}
return row(tasks[index], descriptions[index], index);
};
},
);
}
Widget row(String task, String description, int index)
{
return Dismissible(
key: UniqueKey(),
background: Container(color: Colors.red, child: Align(alignment: Alignment.center, child: Text('DELETE', textAlign: TextAlign.center, style: TextStyle(color: Colors.white, fontSize: 18),))),
direction: DismissDirection.horizontal,
onDismissed: (direction) {
setState(() {
tasks.removeAt(index);
if(completedTasks[index])
{
completedTasks.removeAt(index);
}
descriptions.removeAt(index);
importance.removeAt(index);
});
Scaffold.of(context).showSnackBar(SnackBar(content: Text(task+" dismissed")));
},
child: CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
title: Text(task, style: (completedTasks[index]) ? TextStyle(decoration: TextDecoration.lineThrough) : TextStyle(),),
subtitle: Text(descriptions[index], style: (completedTasks[index]) ? TextStyle(decoration: TextDecoration.lineThrough) : TextStyle(),),
isThreeLine: true,
secondary: (importance[index])? Icon(Icons.error, color: Colors.red,) : Text(''),
value: completedTasks[index],
onChanged: (bool value) {
setState(() {
if(completedTasks[index])
{
completedTasks[index] = false;
}
else
{
completedTasks[index] = true;
}
});
},
));
}
void addToDoItemScreen() {
int index = tasks.length;
while (importance.length > tasks.length) {
importance.removeLast();
}
importance.add(false);
tasks.add("#45jiodg{}}{OHU&IEB");
descriptions.add("No Description");
completedTasks.add(false);
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return StatefulBuilder(builder: (context, setState) { // this is new
return new Scaffold(
appBar: new AppBar(title: new Text('Add a new task')),
body: Form(
child: Column(
children: <Widget>[
TextField(
autofocus: true,
onSubmitted: (name) {
addToDoItem(name);
//Navigator.pop(context); // Close the add todo screen
},
decoration: new InputDecoration(
hintText: 'Enter something to do...',
contentPadding: const EdgeInsets.all(20.0),
border: OutlineInputBorder()),
),
TextField(
//autofocus: true,
//enabled: descriptions.length > desc,
onSubmitted: (val) {
addDescription(val, index);
},
decoration: new InputDecoration(
hintText: 'Enter a task decription...',
contentPadding: const EdgeInsets.all(20.0),
border: OutlineInputBorder()),
),
Row(
children: <Widget> [
Switch(
value: importance[index],
onChanged: (val) {
setState(() {
});
impTask(index);
},
),
Text('Important Task', style: TextStyle(fontSize: 18)),
],
),
RaisedButton(onPressed: () { Navigator.pop(context); }, child: Text('DONE', style: TextStyle(fontSize: 20)),)
],
),
));
});
}));
}
void addToDoItem(String task)
{
setState(() {
tasks.last = task;
});
}
void addDescription(String desc, int index)
{
setState(() {
descriptions.last = desc;
});
}
void impTask(int index)
{
setState(() {
if(importance[index])
{
importance[index] = false;
}
else
{
importance[index] = true;
}
});
}
}
I have 4 lists with the data. I need a simple way to save the lists such that the next time I open the app, the lists retain the data that was saved in them, the last time I had closed the app.
To do this you'll certainly have to use the path_provider package with this tutorial on the flutter.dev website. You should then be able to register a file and read it at the start of your application.
Once you have imported the path_provider and the dart:io packages, you can do something like this :
final directory = await getApplicationDocumentsDirectory();
final File file = File('${directory.path}/jsonObjects.json');
if (await file.exists()) {
json = await file.readAsString();
} else {
file.writeAsString(json);
}
First you get the application document directory ( the path ), then you create a File with the right path. Then if the file already exist, you read it, else you create it with the json you got and you should be good to go !

RSS / XML parsing

Am stuck trying to parse and display rss feed data. How can I easily get these links to all work? What library should be used. Thanks!
Currently using https://pub.dev/packages/webfeed
DART /Flutter mobile application
Cannot seem to parse and render links below:
https://academic.oup.com/rss/site_5267/3133.xml (Returns 405)
https://journals.lww.com/co-anesthesiology/_layouts/15/OAKS.Journals/feed.aspx?FeedType=MostPopularArticles (won't load or import xml)
Have this link working ok:
http://anesthesiology.pubs.asahq.org/rss/site_157/173.xml
import 'dart:io';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:wcm_anes/common/circularIndicator.dart';
import 'package:wcm_anes/customIcons/custom_icons_icons.dart';
import 'package:wcm_anes/styles/colorAsset.dart';
import 'package:wcm_anes/styles/stringAssets.dart';
import 'package:wcm_anes/styles/textStyles.dart';
import 'package:webfeed/webfeed.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class RssUrlListScreen extends StatefulWidget {
String screenName;
String feedUrl;
String categoryId;
RssUrlListScreen({this.screenName,this.feedUrl,this.categoryId});
#override
_RssUrlListScreenState createState() => _RssUrlListScreenState();
}
class _RssUrlListScreenState extends State<RssUrlListScreen> {
static const String loadingMessage = 'Loading Feed...';
static const String feedLoadErrorMessage = 'Error Loading Feed.';
static const String feedOpenErrorMessage = 'Error Opening Feed.';
String _title;
RssFeed feed;
bool isLoading = false;
updateTitle(title) {
setState(() {
_title = title;
});
}
updateFeed(feed) {
setState(() {
feed = feed;
});
}
Future<void> openFeed(String url) async {
if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: true,
forceWebView: false,
);
return;
}
updateTitle(feedOpenErrorMessage);
}
// Method to load the RSS data.
load() async {
updateTitle(loadingMessage);
loadFeed().then((result) {
if (result == null || result.toString().isEmpty) {
// Notify user of error.
updateTitle(feedLoadErrorMessage);
return;
}
setState(() {
feed = result;
isLoading = false;
});
});
}
// Method to get the RSS data
Future<RssFeed> loadFeed() async {
try {
final client = http.Client();
final response = await client.get(widget.feedUrl);
print("responssssurl>> ${response.body}");
return RssFeed.parse(utf8.decode(response.bodyBytes));
} catch (e) {
print("execption>> $e");
// handle any exceptions here
}
return null;
}
#override
void initState() {
updateTitle(widget.screenName);
isLoading = true;
load();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
brightness: Platform.isIOS?Brightness.light:Brightness.dark,
title:feed != null?Text(feed.title,style:headerTextStyle):Container(),
leading: IconButton(
onPressed: (){
Navigator.pop(context);
},
icon:Icon(Icons.arrow_back_ios,color: ColorAsset.tabColorGray,size:20.0,)),
backgroundColor: Colors.white,
elevation: 0.0,
titleSpacing: 0.0,
centerTitle: true,
),
body: Stack(
children: <Widget>[
feed != null?
feed.items.length>0?ListView.builder(
physics: new ClampingScrollPhysics(),
itemCount: feed.items.length,
itemBuilder: (BuildContext context, int index){
final feedItems = feed.items[index];
print("rssTitle>>> ${feedItems.title}");
return Container(
margin: EdgeInsets.only(left:13.0,right:13.0),
child: Center(
child: Card(
elevation: 1.0,
child: ListTile(
onTap: (){
openFeed(feedItems.link);
},
title: Text(feedItems.title,style:listTextStyle),
subtitle: Text(feedItems.pubDate,style: subCategoroyTextStyle,),
contentPadding: EdgeInsets.all(5.0),
trailing: Padding(
padding: EdgeInsets.only(top:15.0,bottom:15.0),
child: Icon(CustomIcons.next,size:20.0,),
)
),
),
),
);
}
):Container(
margin: EdgeInsets.only(top:(MediaQuery.of(context).size.height/2)-110),
child: Text(StringAssets.noData,
style: listTextStyle,
),
)
:Container(),
Visibility(
visible: isLoading,
child: Container(
//margin: EdgeInsets.only(top:(MediaQuery.of(context).size.height/2)-110),
child: Center(
child: CircularIndiacator()
),
),
)
],
),
);
}
}

Autocomplete suggestion and search using json data

I want to display data from local json in list as suggestions when user types in a textfield. The suggestions displayed should be based on id that is associated with text to be displayed.
Somehow I am not able to achieve to display the data in UI and how to build the hierarchy of widgets that will display suggestions in list. Not sure what am I missing here. Looking for guidance. End result I am looking to achieve is:
Json snippet:
{
"data": [{
"serviceCategory": "ELECTRICAL",
"serviceCategoryDesc": "Electrical",
"serviceCategoryId": 3,
"autocompleteTerm": "Accent Lighting Installation",
"category": "IMPROVEMENT",
Ex : If user types electrical, then autocompleteterm value should be displayed in the list.
For this, I created model class and fetching it's data which is displayed in console properly.
class Categories {
String serviceCategory;
String servCategoryDesc;
int id;
String autocompleteterm;
String category;
String desc;
Categories({
this.serviceCategory,
this.servCategoryDesc,
this.id,
this.autocompleteterm,
this.category,
this.desc
});
factory Categories.fromJson(Map<String, dynamic> parsedJson) {
return Categories(
serviceCategory: parsedJson['serviceCategory'] as String,
servCategoryDesc: parsedJson['serviceCategoryDesc'] as String,
id: parsedJson['serviceCategoryId'],
autocompleteterm: parsedJson['autocompleteTerm'] as String,
category: parsedJson['category'] as String,
desc: parsedJson['description'] as String
);
}
}
Code :
// Get json result and convert it to model. Then add
Future<String> getUserDetails() async {
String jsonData = await DefaultAssetBundle.of(context).loadString('assets/services.json');
Map data = json.decode(jsonData);
print(data);
setState(() {
final List<Categories> items = (data['data'] as List).map((i) => new Categories.fromJson(i)).toList();
for (final item in items) {
print(item.autocompleteterm);
}
});
}
GlobalKey<AutoCompleteTextFieldState<Categories>> key = new GlobalKey();
get categories => List<Categories>();
AutoCompleteTextField textField;
String currentText = "";
List<Categories> added = [];
#override
void initState() {
textField = AutoCompleteTextField<Categories>
(style: new TextStyle(
color: Colors.white,
fontSize: 16.0),
decoration: new InputDecoration(
suffixIcon: Container(
width: 85.0,
height: 60.0,
color:Colors.green,
child: new IconButton(
icon: new Image.asset('assets/search_icon_ivory.png',color: Colors.white,
height: 18.0,),
onPressed: (){},
),
),
fillColor: Colors.black,
contentPadding: EdgeInsets.fromLTRB(10.0, 30.0, 10.0, 20.0),
filled: true,
hintText: 'Search',
hintStyle: TextStyle(
color: Colors.white
)
),
itemSubmitted: null,
submitOnSuggestionTap: true,
clearOnSubmit: true,
textChanged: (item) {
currentText = item;
},
textSubmitted: (item) {
setState(() {
currentText = item;
added.add(widget.categories.firstWhere((i) => i.autocompleteterm.toLowerCase().contains(currentText)));
});
},
key: key,
suggestions: widget.categories,
itemBuilder: (context, item) {
return new Padding(
padding: EdgeInsets.all(8.0), child: new Text(item.autocompleteterm),
);
},
itemSorter: (a,b) {
return a.autocompleteterm.compareTo(b.autocompleteterm);
},
itemFilter: (item, query){
return item.autocompleteterm.toLowerCase().startsWith(query.toLowerCase());
});
super.initState();
_getUser();
getUserDetails();
}
#override
Widget build(BuildContext context) {
Column body = new Column(
children: <Widget>[
ListTile(
title: textField,
)
],
);
body.children.addAll(added.map((item) {
return ListTile(title: Text(item.autocompleteterm),
);
}
)
);
return Scaffold(
resizeToAvoidBottomPadding: false,
backgroundColor: Color(0xFF13212C),
appBar: AppBar(
title: Text('Demo'),
),
drawer: appDrawer(),
body: new Center(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Column(
children: <Widget>[
textField,
]
),
The autocomplete_field package has been updated since this question was asked and now allows the use of objects other than Strings to work:
HomePage:
import 'package:flutter/material.dart';
import 'package:hello_world/category.dart';
import 'package:autocomplete_textfield/autocomplete_textfield.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<Category> added = [];
String currentText = "";
GlobalKey<AutoCompleteTextFieldState<Category>> key = new GlobalKey();
AutoCompleteTextField textField;
#override void initState() {
textField = new AutoCompleteTextField<Category>(
decoration: new InputDecoration(
hintText: "Search Item",
),
key: key,
submitOnSuggestionTap: true,
clearOnSubmit: true,
suggestions: CategoryViewModel.categories,
textInputAction: TextInputAction.go,
textChanged: (item) {
currentText = item;
},
itemSubmitted: (item) {
setState(() {
currentText = item.autocompleteterm;
added.add(item);
currentText = "";
});
},
itemBuilder: (context, item) {
return new Padding(
padding: EdgeInsets.all(8.0), child: new Text(item.autocompleteterm));
},
itemSorter: (a, b) {
return a.autocompleteterm.compareTo(b.autocompleteterm);
},
itemFilter: (item, query) {
return item.autocompleteterm.toLowerCase().startsWith(query.toLowerCase());
}
);
super.initState();
}
#override
Widget build(BuildContext context) {
Column body = new Column(children: [
new ListTile(
title: textField,
trailing: new IconButton(
icon: new Icon(Icons.add),
onPressed: () {
setState(() {
if (currentText != "") {
added.add(CategoryViewModel.categories.firstWhere((i) => i.autocompleteterm.toLowerCase().contains(currentText)));
textField.clear();
currentText = "";
}
});
}))
]);
body.children.addAll(added.map((item) {
return ListTile(title: Text(item.autocompleteterm), subtitle: Text(item.serviceCategory));
}));
return body;
}
}
Category classes:
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle;
class Category {
String serviceCategory;
String servCategoryDesc;
int id;
String autocompleteterm;
Category(
{this.serviceCategory,
this.servCategoryDesc,
this.id,
this.autocompleteterm});
factory Category.fromJson(Map<String, dynamic> parsedJson) {
return new Category(
serviceCategory: parsedJson['serviceCategory'],
servCategoryDesc: parsedJson['serviceCategoryDesc'],
id: parsedJson['serviceCategoryId'],
autocompleteterm: parsedJson['autocompleteTerm']);
}
}
class CategoryViewModel {
static List<Category> categories;
static Future loadCategories() async {
try {
categories = new List<Category>();
String jsonString = await rootBundle.loadString('assets/categories.json');
Map parsedJson = json.decode(jsonString);
var categoryJson = parsedJson['data'] as List;
for (int i = 0; i < categoryJson.length; i++) {
categories.add(new Category.fromJson(categoryJson[i]));
}
} catch (e) {
print(e);
}
}
}
Main with loading data:
void main() async {
await CategoryViewModel.loadCategories();
runApp(App());
}
Note, there are a few ways to load the data from the JSON but I find this way is easiest to do for a simple demo.