displaying Json data as a list - json

I have a json file that i'm trying to display as a list in my app.
here is the json file and how it is laid out:
{
"peoplesnames": [
"name1",
"name2",
"name3",
"name4",
"name5",
"name6",
"name7",
"name8"
]
}
and here is the code from my app:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class test extends StatefulWidget {
const test({Key? key}) : super(key: key);
#override
State<test> createState() => _testState();
}
class _testState extends State<test> {
List<String> peopleNames = [];
void getData() async {
http.Response response = await http.get(
Uri.parse('www.genericwebsite.com'),
);
if (response.statusCode == 200) {
String data = response.body;
final names = jsonDecode(data);
peopleNames.addAll((names['peoplesnames'] as List));
setState(() {});
return names;
} else {
print(response.statusCode);
}
}
#override
Widget build(BuildContext context) {
getData();
return Scaffold(
body: ListView.builder(
shrinkWrap: true,
itemCount: peopleNames.length,
itemBuilder: (context, index) {
return Text(peopleNames[index].toString());
}),
);
}
}
The problem seems to be coming from as List in the following line of code:
peopleNames.addAll((names['peoplesnames'] as List));
when as List is there I get the followiing red underline error and the code won't run.
The argument type 'List' can't be assigned to the parameter type 'Iterable'.
then, If i remove as List, the red line goes away but when I run the code, i get this error in the console
E/flutter ( 7999): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'List' is not a subtype of type 'Iterable'
I know the app is talking to the server correctly because if i replace
peopleNames.addAll((names['peoplesnames'] as List));
setState(() {});
return names;
with print(names), and run it, the names print in the console.
any help fixing this would be greatly appreciated.
cheers

Here is your answer
void convertJsonToList() {
String jsonData = '''
{
"peoplesnames": [
"name1",
"name2",
"name3",
"name4",
"name5",
"name6",
"name7",
"name8"
]
}
''';
Map<String, dynamic> jsonMap = jsonDecode(jsonData);
peoplesNamesList = List<String>.from(jsonMap['peoplesnames']);
print(peoplesNamesList);
}

Try this:
peopleNames.addAll((names['peoplesnames'].toList()));

Related

What push data payload structure I need for this handler? (Flutter)

I am using FLutterFlow to build an app and my coding level is beginner. I need to create a valid push data payload structure (JSON) at backend side to pass to app two parameters:
app page (tab) name that should be opened when user taps on push (is called "initialPageName" in code below)
push unique id assigned by backend (is called "pushGUID" in code below)
Below is push handler code that is under the hood of FlutterFlow web app builder, I can not change it. I know it works, because when I use FlutterFlow web push sender and fill in "initial page" and "push GUID" everything works fine. But when I send push from my backend using FCM HTTP API I get only notification itself, data payload with "push GUID" is not handled correctly.
import 'dart:async';
import 'dart:convert';
import 'serialization_util.dart';
import '../backend.dart';
import '../../flutter_flow/flutter_flow_theme.dart';
import '../../flutter_flow/flutter_flow_util.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import '../../index.dart';
import '../../main.dart';
final _handledMessageIds = <String?>{};
class PushNotificationsHandler extends StatefulWidget {
const PushNotificationsHandler({Key? key, required this.child})
: super(key: key);
final Widget child;
#override
_PushNotificationsHandlerState createState() =>
_PushNotificationsHandlerState();
}
class _PushNotificationsHandlerState extends State<PushNotificationsHandler> {
bool _loading = false;
Future handleOpenedPushNotification() async {
if (isWeb) {
return;
}
final notification = await FirebaseMessaging.instance.getInitialMessage();
if (notification != null) {
await _handlePushNotification(notification);
}
FirebaseMessaging.onMessageOpenedApp.listen(_handlePushNotification);
}
Future _handlePushNotification(RemoteMessage message) async {
if (_handledMessageIds.contains(message.messageId)) {
return;
}
_handledMessageIds.add(message.messageId);
if (mounted) {
setState(() => _loading = true);
}
try {
final initialPageName = message.data['initialPageName'] as String;
final initialParameterData = getInitialParameterData(message.data);
final parametersBuilder = parametersBuilderMap[initialPageName];
if (parametersBuilder != null) {
final parameterData = await parametersBuilder(initialParameterData);
context.pushNamed(
initialPageName,
params: parameterData.params,
extra: parameterData.extra,
);
}
} catch (e) {
print('Error: $e');
} finally {
if (mounted) {
setState(() => _loading = false);
}
}
}
#override
void initState() {
super.initState();
handleOpenedPushNotification();
}
#override
Widget build(BuildContext context) => _loading
? Container(
color: Colors.white,
child: Image.asset(
'assets/images/logo.png',
fit: BoxFit.contain,
),
)
: widget.child;
}
class ParameterData {
const ParameterData(
{this.requiredParams = const {}, this.allParams = const {}});
final Map<String, String?> requiredParams;
final Map<String, dynamic> allParams;
Map<String, String> get params => Map.fromEntries(
requiredParams.entries
.where((e) => e.value != null)
.map((e) => MapEntry(e.key, e.value!)),
);
Map<String, dynamic> get extra => Map.fromEntries(
allParams.entries.where((e) => e.value != null),
);
static Future<ParameterData> Function(Map<String, dynamic>) none() =>
(data) async => ParameterData();
}
final parametersBuilderMap =
<String, Future<ParameterData> Function(Map<String, dynamic>)>{
'PhoneAuth': ParameterData.none(),
'smsVerify': (data) async => ParameterData(
allParams: {
'phoneNumber': getParameter<String>(data, 'phoneNumber'),
},
),
'MainPage': (data) async => ParameterData(
allParams: {
'pushGUID': getParameter<String>(data, 'pushGUID'),
},
),
'Campaign': ParameterData.none(),
'LoyaltyPoints': ParameterData.none(),
'OnTheMap': ParameterData.none(),
'NewCollections': ParameterData.none(),
'Notifications': ParameterData.none(),
};
Map<String, dynamic> getInitialParameterData(Map<String, dynamic> data) {
try {
final parameterDataStr = data['parameterData'];
if (parameterDataStr == null ||
parameterDataStr is! String ||
parameterDataStr.isEmpty) {
return {};
}
return jsonDecode(parameterDataStr) as Map<String, dynamic>;
} catch (e) {
print('Error parsing parameter data: $e');
return {};
}
}
My guess looking at the code above is that I should use nested JSON like this:
{
"message":{
"notification":{
"body" : "my body",
"title" : "my title",
},
"data" : {
"initialPage":"mainPage",
"params":[
{"pushGUID":"1231231231"}
]
}
}
}
But as I mentioned my Futter code level is beginner, so please have a look at the handler code and correct my JSON structure, thanks for any advice.

Why isn't my API data showing in a list view, when the GET request is successful?

I am working with flutter for the first time and having issues understanding why my data will not show. For context, the API returns a list of (physical) libraries and their current occupancy. I have correctly implemented an API get request, got the data outputted in the terminal (printed) and thus I know my API call is working. I also implemented the data received from the JSON as a list using my data model, and then I created a body with the library name and occupancy as a ListTile. Nothing is shown when compiled on the device screen, just a blank screen, no circular progress indicator either.
Here is my home page:
import 'package:flutter/material.dart';
import '../models/post.dart';
import '../services/remote_service.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// Declare a list of Libraries
List<Libraries> _libraries = [];
#override
void initState() {
super.initState();
// Call the getPosts function when the HomePage is initialized
_getPosts();
}
// Function to make the GET request and store the response in a list of Libraries
void _getPosts() async {
var api = getDataAPI();
var librariesJson = await api.getPosts();
// Use the librariesFromJson function to convert the JSON string to a list of Libraries
setState(() {
_libraries = librariesFromJson(librariesJson!) as List<Libraries>;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Library Occupancy'),
),
body: _libraries == null
? const Center(
child: CircularProgressIndicator(),
)
: ListView.builder(
itemCount: _libraries.length,
itemBuilder: (context, index) {
// Display the library name and occupancy in a ListTile
return ListTile(
title: Text(_libraries[index].webster.occupancy),
subtitle:
Text(_libraries[index].webster.lastRecordTime.toString()),
);
},
),
);
}
}
Here is my API request code (remote_service.dart):
// ignore_for_file: camel_case_types
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:xconcordia/models/post.dart';
class getDataAPI {
Future<String?> getPosts() async {
var credentials = '520:276377ba5206683e51d50fb86c378dc5';
// Encode the credentials in base64 format
var base64Credentials = base64Encode(utf8.encode(credentials));
var headers = {'Authorization': 'Basic $base64Credentials'};
var request = http.Request('GET',
Uri.parse('https://opendata.concordia.ca/API/v1/library/occupancy/'));
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
String json = await response.stream.bytesToString();
// ignore: await_only_futures
Libraries library = await librariesFromJson(json);
print("webster: " + library.webster.occupancy);
return library.webster.occupancy;
} else {
print(response.statusCode);
}
return null;
}
}
And finally, here is my JSON data model:
import 'dart:convert';
Libraries librariesFromJson(String str) => Libraries.fromJson(json.decode(str));
String librariesToJson(Libraries data) => json.encode(data.toJson());
class Libraries {
Libraries({
required this.webster,
required this.vanier,
required this.greyNuns,
});
GreyNuns webster;
GreyNuns vanier;
GreyNuns greyNuns;
factory Libraries.fromJson(Map<String, dynamic> json) => Libraries(
webster: GreyNuns.fromJson(json["Webster"]),
vanier: GreyNuns.fromJson(json["Vanier"]),
greyNuns: GreyNuns.fromJson(json["GreyNuns"]),
);
Map<String, dynamic> toJson() => {
"Webster": webster.toJson(),
"Vanier": vanier.toJson(),
"GreyNuns": greyNuns.toJson(),
};
}
class GreyNuns {
GreyNuns({
required this.occupancy,
required this.lastRecordTime,
});
String occupancy;
DateTime lastRecordTime;
factory GreyNuns.fromJson(Map<String, dynamic> json) => GreyNuns(
occupancy: json["Occupancy"],
lastRecordTime: DateTime.parse(json["LastRecordTime"]),
);
Map<String, dynamic> toJson() => {
"Occupancy": occupancy,
"LastRecordTime": lastRecordTime.toIso8601String(),
};
}
Also, when I run this code, here is the terminal output:
Performing hot restart...
Restarted application in 302ms.
flutter: webster: .0000
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: FormatException: Unexpected character (at character 1)
.0000
^
#0 _ChunkedJsonParser.fail (dart:convert-patch/convert_patch.dart:1383:5)
#1 _ChunkedJsonParser.parseNumber (dart:convert-patch/convert_patch.dart:1250:9)
#2 _ChunkedJsonParser.parse (dart:convert-patch/convert_patch.dart:915:22)
#3 _parseJson (dart:convert-patch/convert_patch.dart:35:10)
#4 JsonDecoder.convert (dart:convert/json.dart:612:36)
#5 JsonCodec.decode (dart:convert/json.dart:216:41)
#6 librariesFromJson (package:xconcordia/models/post.dart:3:68)
#7 _HomePageState._getPosts.<anonymous closure> (package:xconcordia/views/home_page.dart:28:20)
#8 State.setState (package:flutter/src/widgets/framework.dart:1114:30)
#9 _HomePageState._getPosts (package:xconcordia/views/home_page.dart:27:5)
<asynchronous suspension>
Modify Your Model Class
Convert the data to String in modal class:
factory GreyNuns.fromJson(Map<String, dynamic> json) => GreyNuns(
occupancy: json["Occupancy"].toString(),
lastRecordTime: DateTime.parse(json["LastRecordTime"].toString()),
);

How do I initialize my json response in another class that depends on a search input in flutter?

this is my first question here on stackoverflow.
I'm working on my first real app after attending a few CS classes at University and some courses on Udemy. So I'm lacking a lot of software engineering knowledge.
My goal: I want to build a search for stocks by using an external API endpoint. For that, I have created 4 dart files to handle the searchscreen with inputs (My UI), networking, parsing, and basically returning the data.
My NetworkAPI class to handle all sorts of network requests looks like this. I'm trying to use the jsonDecode already here and returning that. This class waits for an URL that will be put together in another class (financeData):
class NetworkAPI {
final String url;
NetworkAPI(this.url);
Future getData() async {
http.Response response = await http.get(url);
if (response.statusCode == 200) {
String data = response.body;
return jsonDecode(data);
} else {
print(response.statusCode);
}
}
}
This is basically the Json I want to parse. If I understood the theory correctly it's a map with a list of objects.
{
"bestMatches": [
{
"1. symbol": "TESO",
"2. name": "Tesco Corporation USA",
"3. type": "Equity",
"4. region": "United States",
"5. marketOpen": "09:30",
"6. marketClose": "16:00",
"7. timezone": "UTC-04",
"8. currency": "USD",
"9. matchScore": "0.8889"
}
{....}
]
}
To parse this, I saw some really good explanations here on stack overflow. I'm basically trying to retrieve the information I'm interested in.
class SearchOutput {
final List<BestMatch> bestMatches;
SearchOutput({this.bestMatches});
factory SearchOutput.fromJson(Map<String, dynamic> parsedJson){
var list = parsedJson['bestMatches'] as List;
print(list.runtimeType);
List<BestMatch> searchResultList = list.map((i) => BestMatch.fromJson(i)).toList();
return SearchOutput(
bestMatches: searchResultList,
);
}
}
class BestMatch {
String symbol;
String name;
String type;
String region;
String currency;
BestMatch({
this.symbol,
this.name,
this.type,
this.region,
this.currency,
});
factory BestMatch.fromJson(Map<String, dynamic> parsedJson){
return BestMatch(
symbol: parsedJson["1. symbol"],
name: parsedJson["2. name"],
type: parsedJson["3. type"],
region: parsedJson["4. region"],
currency: parsedJson["8. currency"],
);
}
}
Now I created a class with a method that should return all my data and format it into a list. The URL still needs an searchInput, which will be handed over from a TextField Widget in the UI.
const apiKey = 'demo';
const alphaVantageSearchUrl =
'https://www.alphavantage.co/query?function=SYMBOL_SEARCH';
class FinanceData {
Future<dynamic> getSearchData(String searchInput) async {
var url = '$alphaVantageSearchUrl&keywords=$searchInput&apikey=$apiKey';
NetworkAPI networkAPI = NetworkAPI(url);
var searchData = await networkAPI.getData();
SearchOutput searchOutput = new SearchOutput.fromJson(searchData);
return searchOutput;
}
}
Testing with some print statements seems to fullfill my goal of being able to access the data. For example printing out the name of the second object out of my response.
print(searchOutput.bestMatches[1].name);
The last file is my UI where we have a TextField, that I push to the financeData class in order to build the URL. Currently, my goal would be, to be able to show just the name of any given object in my response in the UI. I have no idea how to initialize my financeDate and how to use the return from my finaceData class in the UI. (I took some design styles out of the code snippet).
class SearchScreenWatchlist extends StatefulWidget {
#override
_SearchScreenWatchlistState createState() => _SearchScreenWatchlistState();
}
class _SearchScreenWatchlistState extends State<SearchScreenWatchlist> {
String searchInput;
FinanceData financeData = FinanceData();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Container(
child: TextField(
onChanged: (value) {
searchInput = value;
},
),
),
TextButton(
onPressed: () {
financeData.getSearchData(searchInput);
setState(() {
});
},
child: Text('Search')),
Text(('Search Results for: $searchInput')),
Container(child: Text('**SHOW HERE THE NAME**')),
],
),
);
}
}
It would be my goal to show in the last container the name on any given object from my response for example RESPONSE.name[1]. Later on I will try to iterate throw all the objects and show a list of all names of the response.
Thanks a lot! I really appreciate your help!
I think changing SearchScreenWatchlist to the following should display the name of the first bestMatches entry after you pressed the Search button (if I didn't miss something).
Make sure to look further into setState and initState (And the StatefulWidget lifecycle in general). Also it may not be the most elegant way to initialize FinanceData() and SearchOutput() this way - However, awesome starter project!
class SearchScreenWatchlist extends StatefulWidget {
#override
_SearchScreenWatchlistState createState() => _SearchScreenWatchlistState();
}
class _SearchScreenWatchlistState extends State<SearchScreenWatchlist> {
String searchInput;
FinanceData financeData;
SearchOutput searchOutput;
#override
initState() {
super.initState();
financeData = FinanceData();
searchOutput = SearchOutput();
}
_handleButtonPress() async {
final fetchedSearchOutput = await financeData.getSearchData(searchInput);
setState(() {
searchOutput = fetchedSearchOutput;
});
}
_buildSearchResult() {
if(searchOutput.bestMatches != null && searchOutput.bestMatches.isNotEmpty) {
return Text(searchOutput.bestMatches.first.name);
}
else {
return Text("No data fetched");
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Container(
child: TextField(
onChanged: (value) {
setState(() {
searchInput = value;
});
},
),
),
TextButton(
onPressed: () => _handleButtonPress(),
child: Text('Search'),
),
Text(('Search Results for: $searchInput')),
Container(
child: _buildSearchResult(),
),
],
),
);
}
}

How can I parse this api json with flutter?

I am pretty new to coding. I finished all of the UI of an app for myself but I cannot figure out how to input the data from an api into my app.
I have been trying for over a week. Read so many articles and videos but this api never works. I have come to the conclusion that I am doing something wrong with the fetching part. Maybe because everything I read or watch demonstrates with a List instead of a Map.
Here is a copy of my code.
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<Stock> futureStock;
#override
void initState() {
super.initState();
futureStock = fetchStock();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: Text('Material App Bar'),
),
body: Center(
child: FutureBuilder<Stock>(
future: futureStock,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.companyName);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
),
),
),
);
}
}
Future<Stock> fetchStock() async {
final response = await http.get(
'https://sandbox.iexapis.com/stable/stock/aapl/stats?token=Tsk_38ddda0b877a4510b42a37ae713cdc96');
if (response.statusCode == 200) {
return Stock.fromJson(json.decode(response.body));
} else {
throw Exception('Fail to load');
}
}
Stock stockFromJson(String str) => Stock.fromJson(json.decode(str));
String stockToJson(Stock data) => json.encode(data.toJson());
class Stock {
Stock({
this.week52Change,
this.week52High,
this.week52Low,
this.marketcap,
this.employees,
this.day200MovingAvg,
this.day50MovingAvg,
this.float,
this.avg10Volume,
this.avg30Volume,
this.ttmEps,
this.ttmDividendRate,
this.companyName,
this.sharesOutstanding,
this.maxChangePercent,
this.year5ChangePercent,
this.year2ChangePercent,
this.year1ChangePercent,
this.ytdChangePercent,
this.month6ChangePercent,
this.month3ChangePercent,
this.month1ChangePercent,
this.day30ChangePercent,
this.day5ChangePercent,
this.nextDividendDate,
this.dividendYield,
this.nextEarningsDate,
this.exDividendDate,
this.peRatio,
this.beta,
});
double week52Change;
double week52High;
double week52Low;
int marketcap;
int employees;
double day200MovingAvg;
double day50MovingAvg;
int float;
double avg10Volume;
double avg30Volume;
double ttmEps;
double ttmDividendRate;
String companyName;
int sharesOutstanding;
double maxChangePercent;
double year5ChangePercent;
double year2ChangePercent;
double year1ChangePercent;
double ytdChangePercent;
double month6ChangePercent;
double month3ChangePercent;
double month1ChangePercent;
double day30ChangePercent;
double day5ChangePercent;
dynamic nextDividendDate;
double dividendYield;
DateTime nextEarningsDate;
DateTime exDividendDate;
double peRatio;
double beta;
factory Stock.fromJson(Map<String, dynamic> json) => Stock(
week52Change: json["week52change"].toDouble(),
week52High: json["week52high"].toDouble(),
week52Low: json["week52low"].toDouble(),
marketcap: json["marketcap"],
employees: json["employees"],
day200MovingAvg: json["day200MovingAvg"].toDouble(),
day50MovingAvg: json["day50MovingAvg"].toDouble(),
float: json["float"],
avg10Volume: json["avg10Volume"].toDouble(),
avg30Volume: json["avg30Volume"].toDouble(),
ttmEps: json["ttmEPS"].toDouble(),
ttmDividendRate: json["ttmDividendRate"].toDouble(),
companyName: json["companyName"],
sharesOutstanding: json["sharesOutstanding"],
maxChangePercent: json["maxChangePercent"].toDouble(),
year5ChangePercent: json["year5ChangePercent"].toDouble(),
year2ChangePercent: json["year2ChangePercent"].toDouble(),
year1ChangePercent: json["year1ChangePercent"].toDouble(),
ytdChangePercent: json["ytdChangePercent"].toDouble(),
month6ChangePercent: json["month6ChangePercent"].toDouble(),
month3ChangePercent: json["month3ChangePercent"].toDouble(),
month1ChangePercent: json["month1ChangePercent"].toDouble(),
day30ChangePercent: json["day30ChangePercent"].toDouble(),
day5ChangePercent: json["day5ChangePercent"].toDouble(),
nextDividendDate: json["nextDividendDate"],
dividendYield: json["dividendYield"].toDouble(),
nextEarningsDate: DateTime.parse(json["nextEarningsDate"]),
exDividendDate: DateTime.parse(json["exDividendDate"]),
peRatio: json["peRatio"].toDouble(),
beta: json["beta"].toDouble(),
);
Map<String, dynamic> toJson() => {
"week52change": week52Change,
"week52high": week52High,
"week52low": week52Low,
"marketcap": marketcap,
"employees": employees,
"day200MovingAvg": day200MovingAvg,
"day50MovingAvg": day50MovingAvg,
"float": float,
"avg10Volume": avg10Volume,
"avg30Volume": avg30Volume,
"ttmEPS": ttmEps,
"ttmDividendRate": ttmDividendRate,
"companyName": companyName,
"sharesOutstanding": sharesOutstanding,
"maxChangePercent": maxChangePercent,
"year5ChangePercent": year5ChangePercent,
"year2ChangePercent": year2ChangePercent,
"year1ChangePercent": year1ChangePercent,
"ytdChangePercent": ytdChangePercent,
"month6ChangePercent": month6ChangePercent,
"month3ChangePercent": month3ChangePercent,
"month1ChangePercent": month1ChangePercent,
"day30ChangePercent": day30ChangePercent,
"day5ChangePercent": day5ChangePercent,
"nextDividendDate": nextDividendDate,
"dividendYield": dividendYield,
"nextEarningsDate":
"${nextEarningsDate.year.toString().padLeft(4, '0')}-${nextEarningsDate.month.toString().padLeft(2, '0')}-${nextEarningsDate.day.toString().padLeft(2, '0')}",
"exDividendDate":
"${exDividendDate.year.toString().padLeft(4, '0')}-${exDividendDate.month.toString().padLeft(2, '0')}-${exDividendDate.day.toString().padLeft(2, '0')}",
"peRatio": peRatio,
"beta": beta,
};
}
Any help would be greatly appreciated!
I followed the official example from https://flutter.dev/docs/cookbook/networking/fetch-data
I have tested your code and can confirm it is working fine.
But if you are running the release build of your app, you need to add the following line into your AndroidManifest.xml file.
<uses-permission android:name="android.permission.INTERNET"/>

Flutter and Distance Matrix API parsing json

I am currently building a flutter app where I want to calculate the distance between some objects and am using the Google Distance Matrix API to do so. I am having trouble parsing the json using Dart. All I want ultimately is a list of the distances from the json results so that I can index them and apply them to the data in my app.
The json results look like this:
{
"destination_addresses" : [
"destination address",
"destination address"
],
"origin_addresses" : [ "Origin addresses here" ],
"rows" : [
{
"elements" : [
{
"distance" : {
"text" : "4.3 mi",
"value" : 6998
},
"duration" : {
"text" : "14 mins",
"value" : 848
},
"status" : "OK"
},
{
"distance" : {
"text" : "6.7 mi",
"value" : 10728
},
"duration" : {
"text" : "22 mins",
"value" : 1327
},
"status" : "OK"
}
]
}
],
"status" : "OK"
}
I ultimately would like to end up with a list (in dart) that is just a list of the distance "text" values within the elements array but I am having trouble getting this to return. I have tried creating a class and mapping the json results to this but unsuccesfully as I am not very good at parsing json so any advice on how to end up with this list would be gratefully received!
I have tried this code to parse the json but am really struggling to make it work and then apply it:
class Topleveladd {
final String elements;
Topleveladd({this.elements});
factory Topleveladd.fromJson(Map<String, dynamic> parsedJson) {
return Topleveladd(elements: parsedJson['rows']);
}
}
class Elements {
List<Distance> distanceslist;
Elements({this.distanceslist});
factory Elements.fromJson(Map<String, dynamic> parsedJson) {
var list = parsedJson['elements'] as List;
print(list.runtimeType); //returns List<dynamic>
List<Distance> distancesList =
list.map((i) => Distance.fromJson(i)).toList();
return Elements(distanceslist: distancesList);
}
}
class Distance {
String text;
Distance({this.text});
factory Distance.fromJson(Map<String, dynamic> parsedJson) {
return new Distance(
text: parsedJson['distance'],
);
}
}
Okay this is it working with me accessing the JSON you've given as an asset so you'll probably have to change the loadData method for it to fit your needs.
DistanceMatrix class:
import 'dart:convert';
import 'dart:async' show Future;
import 'package:flutter/services.dart' show rootBundle;
class DistanceMatrix {
final List<String> destinations;
final List<String> origins;
final List<Element> elements;
final String status;
DistanceMatrix({this.destinations, this.origins, this.elements, this.status});
factory DistanceMatrix.fromJson(Map<String, dynamic> json) {
var destinationsJson = json['destination_addresses'];
var originsJson = json['origin_addresses'];
var rowsJson = json['rows'][0]['elements'] as List;
return DistanceMatrix(
destinations: destinationsJson.cast<String>(),
origins: originsJson.cast<String>(),
elements: rowsJson.map((i) => new Element.fromJson(i)).toList(),
status: json['status']);
}
static Future<DistanceMatrix> loadData() async {
DistanceMatrix distanceMatrix;
try{
String jsonData = await rootBundle.loadString('assets/data.json');
distanceMatrix = new DistanceMatrix.fromJson(json.decode(jsonData));
} catch (e){
print(e);
}
return distanceMatrix;
}
}
class Element {
final Distance distance;
final Duration duration;
final String status;
Element({this.distance, this.duration, this.status});
factory Element.fromJson(Map<String, dynamic> json) {
return Element(
distance: new Distance.fromJson(json['distance']),
duration: new Duration.fromJson(json['duration']),
status: json['status']);
}
}
class Distance {
final String text;
final int value;
Distance({this.text, this.value});
factory Distance.fromJson(Map<String, dynamic> json) {
return new Distance(text: json['text'], value: json['value']);
}
}
class Duration {
final String text;
final int value;
Duration({this.text, this.value});
factory Duration.fromJson(Map<String, dynamic> json) {
return new Duration(text: json['text'], value: json['value']);
}
}
Main.dart which uses ListView.builder to display the distances text and values as a ListTile:
import 'package:flutter/material.dart';
import 'package:hello_world/distance_matrix.dart';
void main() async {
runApp(new MyApp(
distanceMatrix: await DistanceMatrix.loadData(),
));
}
class MyApp extends StatefulWidget {
final DistanceMatrix distanceMatrix;
#override
_MyAppState createState() => new _MyAppState();
MyApp({this.distanceMatrix});
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Home"),
),
body: Material(
child: ListView.builder(
itemCount: widget.distanceMatrix.elements.length,
itemBuilder: (context, index){
return ListTile(
title: Text(widget.distanceMatrix.elements[index].distance.text),
subtitle: Text(widget.distanceMatrix.elements[index].distance.value.toString()),
);
},
)
)));
}
}
Image to show what you should get: