I am trying to send data from Flutter to Node-RED (to Raspberry Pi) over HTTP (not HTTPS). The URL http://bierbrauserver.ddns.net is from NO-IP (DDNS). The transfer to a test server from a Youtube Tutorial works fine. But when I change the URL from "https://reqres.in/api/users" to "http://bierbrauserver.ddns.net:1880" I get no input in the debug window of Node-RED. I tried in the AndroidManifest.xml to add the entry
so that http is allowed, but that didn't solve the problem either.
Furthermore I checked the port forwarding in the WLAN router configuration of the network where the Raspberry Pi is located. Here a port forwarding of HTTP and 1880 (Node-RED) was configured. The connection of the IP addresses should be ok, because I can access a database from an external port, which is also located on the Raspberry Pi. Can it be that only httpS post requests are possible ?
Future<UserModel> createUser(String name, String jobTitle)async{
const String Url = "https://reqres.in/api/users";
// const String Url = "https://bierbrauserver.ddns.net:1880";
final response = await http.post(Uri.parse(Url),body:
{
"name": name,
"job": jobTitle
});
if (response.statusCode == 201) {
final String responseString = response.body;
return userModelFromJson(responseString);
}
else
{
print(response.statusCode);
return UserModel(name:'Fail',job: 'Fail', id: 'Fail', createdAt: DateTime.now());
}
}
Thank you for your help.
The problem is solved. The program code has already been modified because I forgot to answer the question immediately when the problem was fixed. The problem was hidden in the Node-RED program. The wrong Node was selected in Node-RED.
The URL was extended with "/test", which is reused in Node-RED in the "http in" node. Now everything works fine.
For your information: "Zutat_1", "Zutat_2", "Zutat_3" and "Zutat_4" are global variables in this program and therefore do not have to be passed to the "createRezept" function.
Additionally, it should be mentioned that some elements in the code that do not belong to the English language are executed in the German language.
Future<RezeptModel> createRezept()async{
const String Url = "http://bierbrauserver.ddns.net:1880/test";
final response = await http.post(Uri.parse(Url),body:
{
"zutat1": Zutat_1.toStringAsFixed(2),
"zutat2": Zutat_2.toStringAsFixed(2),
"zutat3": Zutat_3.toStringAsFixed(2),
"zutat4": Zutat_4.toStringAsFixed(2),
"createdAt" : DateTime.now().toIso8601String()
});
if (response.statusCode == 201) {
final String responseString = response.body;
return rezeptModelFromJson(responseString);
}
else
{
print(response.statusCode);
return RezeptModel(zutat1:"Fehler",zutat2: "Fehler", zutat3: "Fehler", zutat4: "Fehler",createdAt: DateTime.now());
}
}
Here is the code which is used for parsing.(This is in a separate file, which in my case is named "Rezept_model.dart" )
import 'dart:convert';
RezeptModel rezeptModelFromJson(String str) => RezeptModel.fromJson(json.decode(str));
String rezeptModelToJson(RezeptModel data) => json.encode(data.toJson());
class RezeptModel {
RezeptModel({
required this.zutat1,
required this.zutat2,
required this.zutat3,
required this.zutat4,
required this.createdAt,
});
String zutat1;
String zutat2;
String zutat3;
String zutat4;
DateTime createdAt;
factory RezeptModel.fromJson(Map<String, dynamic> json) => RezeptModel(
zutat1: json["zutat1"],
zutat2: json["zutat2"],
zutat3: json["zutat3"],
zutat4: json["zutat4"],
createdAt: DateTime.parse(json["createdAt"]),
);
Map<String, dynamic> toJson() => {
"zutat1": zutat1,
"zutat2": zutat2,
"zutat3": zutat3,
"zutat4": zutat4,
"createdAt": createdAt.toIso8601String(),
};
}
For completeness here is the whole code of the main program.
import 'dart:convert';
import 'dart:math';
import 'package:bier_brau_project/variables.dart';
import 'package:bier_brau_project/rezept_model.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bier Brau App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'HTTP Test Site'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
Future<RezeptModel> createRezept()async{
const String Url = "http://bierbrauserver.ddns.net:1880/test";
final response = await http.post(Uri.parse(Url),body:
{
"zutat1": Zutat_1.toStringAsFixed(2),
"zutat2": Zutat_2.toStringAsFixed(2),
"zutat3": Zutat_3.toStringAsFixed(2),
"zutat4": Zutat_4.toStringAsFixed(2),
"createdAt" : DateTime.now().toIso8601String()
});
if (response.statusCode == 201) {
final String responseString = response.body;
return rezeptModelFromJson(responseString);
}
else
{
print(response.statusCode);
return RezeptModel(zutat1:"Fehler",zutat2: "Fehler", zutat3: "Fehler", zutat4: "Fehler",createdAt: DateTime.now());
}
}
class _MyHomePageState extends State<MyHomePage> {
RezeptModel _rezept = RezeptModel(zutat1: "init", zutat2: "init", zutat3: "init",zutat4: "init", createdAt: DateTime.now());
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
MySlider(Min:22.0,Max:40.0,Devisions: 100,unit:'l',title: 'Wasser',ID:1),
MySlider(Min:4.0,Max:20.0,Devisions: 100,unit:'kg',title:'Malz',ID: 2,),
MySlider(Min:60.0,Max:300.0,Devisions: 100,unit:'g',title:'Hopfen',ID: 3,),
MySlider(Min:12.0,Max:60.0,Devisions: 100,unit:'g',title:'Hefe',ID:4),
const SizedBox(height:32.0,),
Text( "Wasser "+ _rezept.zutat1 +"\n"
+"Malz "+_rezept.zutat2 +"\n"
+"Mopfen "+ _rezept.zutat3 +"\n"
+"Hefe "+ _rezept.zutat4 +"\n"
+"ist Erfolgreich übertragen worden "
+ _rezept.createdAt.toIso8601String())
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: ()async{
final RezeptModel rezept= await createRezept();
setState(() {
_rezept=rezept;
});
},
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
/// This is the stateful widget that the main application instantiates.
class MySlider extends StatefulWidget {
MySlider({Key? key,required this.Min, required this.Max,required this.Devisions,required this.unit,required this.title,required this.ID}): super(key: key);
final double Min;
final double Max;
final String unit;
final String title;
final int Devisions;
final int ID;
bool ValueChanged= false;
#override
State<MySlider> createState() => _MySliderState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MySliderState extends State<MySlider> {
double _currentSliderValue =0;
#override
Widget build(BuildContext context) {
return Padding (
padding: const EdgeInsetsDirectional.fromSTEB(32.0, 32.0, 32.0, 16.0),
child: Column(children: <Widget> [
Text(
widget.title,
style: const TextStyle(
color: Colors.black54,
height: 1,
fontSize: 18
),
),
Slider(
value: widget.ValueChanged== false ? ((widget.Max-widget.Min)/2)+widget.Min : _currentSliderValue,
min: widget.Min,
max: widget.Max,
divisions: widget.Devisions,
label: _currentSliderValue.toStringAsFixed(2),
onChanged: (double value) {
setState(() {
_currentSliderValue = value;
first_call = false;
widget.ValueChanged =true;
switch(widget.ID) {
case 1: {Zutat_1 =double.parse(value.toStringAsFixed(2));}
break;
case 2: {Zutat_2 =double.parse(value.toStringAsFixed(2));}
break;
case 3: {Zutat_3 =double.parse(value.toStringAsFixed(2));}
break;
case 4: {Zutat_4 =double.parse(value.toStringAsFixed(2));}
break;
default: {}
break;
}
});
},
),
Text(
(widget.ValueChanged== false ?((widget.Max-widget.Min)/2)+widget.Min : _currentSliderValue.toStringAsFixed(2)).toString() + ' '+widget.unit,
),
])
);
}
}
Many thanks to all those who have dealt with this problem.
Here are still two images deposited around the connections to clarify.
Node-RED program to receive from http post
Android Studio emulator with the test application
Change your code to this:
final response = await http.post(Uri.parse(Url),body:
{
"name": name,
"job": jobTitle
});
if (response.statusCode == 201) {
final responseString = jsonDecode(_res.body)
return userModelFromJson(responseString);
}
else
{
print(response.statusCode);
return UserModel(name:'Fail',job: 'Fail', id: 'Fail', createdAt: DateTime.now());
}
}
New to Flutte/Dart. What could be causing my mapping issues?
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<List<Photo>> fetchPhotos(http.Client client) async {
final response =
await client.get('https://cloud.iexapis.com/stable/stock/market/batch?symbols=aapl,fb&types=quote&token=hidden');
// Use the compute function to run parsePhotos in a separate isolate.
return compute(parsePhotos, response.body);
}
// A function that converts a response body into a List<Photo>.
List<Photo> parsePhotos(String response) {
List<Photo> Photos = new List<Photo>();
List parsePhotos= json.decode(response.toString());
for (int i = 0; i < parsePhotos.length; i++) {
Photos.add(new Photo.fromJson(parsePhotos[i]));
}
return Photos;
}
class Photo {
final String symbol;
final String companyName;
final String primaryExchange;
final String calculationPrice;
final int open;
final int openTime;
final double close;
final int closeTime;
final double high;
final double low;
final double latestPrice;
final String latestSource;
final String latestTime;
final int latestUpdate;
final int latestVolume;
final dynamic iexRealtimePrice;
final dynamic iexRealtimeSize;
final dynamic iexLastUpdated;
final double delayedPrice;
final int delayedPriceTime;
final double extendedPrice;
final double extendedChange;
final double extendedChangePercent;
final int extendedPriceTime;
final double previousClose;
final int previousVolume;
final int change;
final double changePercent;
final int volume;
final dynamic iexMarketPercent;
final dynamic iexVolume;
final int avgTotalVolume;
final dynamic iexBidPrice;
final dynamic iexBidSize;
final dynamic iexAskPrice;
final dynamic iexAskSize;
final int marketCap;
final double peRatio;
final double week52High;
final int week52Low;
final double ytdChange;
final int lastTradeTime;
final bool isUsMarketOpen;
Photo({
this.symbol,
this.companyName,
this.primaryExchange,
this.calculationPrice,
this.open,
this.openTime,
this.close,
this.closeTime,
this.high,
this.low,
this.latestPrice,
this.latestSource,
this.latestTime,
this.latestUpdate,
this.latestVolume,
this.iexRealtimePrice,
this.iexRealtimeSize,
this.iexLastUpdated,
this.delayedPrice,
this.delayedPriceTime,
this.extendedPrice,
this.extendedChange,
this.extendedChangePercent,
this.extendedPriceTime,
this.previousClose,
this.previousVolume,
this.change,
this.changePercent,
this.volume,
this.iexMarketPercent,
this.iexVolume,
this.avgTotalVolume,
this.iexBidPrice,
this.iexBidSize,
this.iexAskPrice,
this.iexAskSize,
this.marketCap,
this.peRatio,
this.week52High,
this.week52Low,
this.ytdChange,
this.lastTradeTime,
this.isUsMarketOpen,
});
factory Photo.fromJson(String str) => Photo.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory Photo.fromMap(Map<String, dynamic> json) => Photo(
symbol: json["symbol"],
companyName: json["companyName"],
primaryExchange: json["primaryExchange"],
calculationPrice: json["calculationPrice"],
open: json["open"],
openTime: json["openTime"],
close: json["close"].toDouble(),
closeTime: json["closeTime"],
high: json["high"].toDouble(),
low: json["low"].toDouble(),
latestPrice: json["latestPrice"].toDouble(),
latestSource: json["latestSource"],
latestTime: json["latestTime"],
latestUpdate: json["latestUpdate"],
latestVolume: json["latestVolume"],
iexRealtimePrice: json["iexRealtimePrice"],
iexRealtimeSize: json["iexRealtimeSize"],
iexLastUpdated: json["iexLastUpdated"],
delayedPrice: json["delayedPrice"].toDouble(),
delayedPriceTime: json["delayedPriceTime"],
extendedPrice: json["extendedPrice"].toDouble(),
extendedChange: json["extendedChange"].toDouble(),
extendedChangePercent: json["extendedChangePercent"].toDouble(),
extendedPriceTime: json["extendedPriceTime"],
previousClose: json["previousClose"].toDouble(),
previousVolume: json["previousVolume"],
change: json["change"],
changePercent: json["changePercent"].toDouble(),
volume: json["volume"],
iexMarketPercent: json["iexMarketPercent"],
iexVolume: json["iexVolume"],
avgTotalVolume: json["avgTotalVolume"],
iexBidPrice: json["iexBidPrice"],
iexBidSize: json["iexBidSize"],
iexAskPrice: json["iexAskPrice"],
iexAskSize: json["iexAskSize"],
marketCap: json["marketCap"],
peRatio: json["peRatio"].toDouble(),
week52High: json["week52High"].toDouble(),
week52Low: json["week52Low"],
ytdChange: json["ytdChange"].toDouble(),
lastTradeTime: json["lastTradeTime"],
isUsMarketOpen: json["isUSMarketOpen"],
);
Map<String, dynamic> toMap() => {
"symbol": symbol,
"companyName": companyName,
"primaryExchange": primaryExchange,
"calculationPrice": calculationPrice,
"open": open,
"openTime": openTime,
"close": close,
"closeTime": closeTime,
"high": high,
"low": low,
"latestPrice": latestPrice,
"latestSource": latestSource,
"latestTime": latestTime,
"latestUpdate": latestUpdate,
"latestVolume": latestVolume,
"iexRealtimePrice": iexRealtimePrice,
"iexRealtimeSize": iexRealtimeSize,
"iexLastUpdated": iexLastUpdated,
"delayedPrice": delayedPrice,
"delayedPriceTime": delayedPriceTime,
"extendedPrice": extendedPrice,
"extendedChange": extendedChange,
"extendedChangePercent": extendedChangePercent,
"extendedPriceTime": extendedPriceTime,
"previousClose": previousClose,
"previousVolume": previousVolume,
"change": change,
"changePercent": changePercent,
"volume": volume,
"iexMarketPercent": iexMarketPercent,
"iexVolume": iexVolume,
"avgTotalVolume": avgTotalVolume,
"iexBidPrice": iexBidPrice,
"iexBidSize": iexBidSize,
"iexAskPrice": iexAskPrice,
"iexAskSize": iexAskSize,
"marketCap": marketCap,
"peRatio": peRatio,
"week52High": week52High,
"week52Low": week52Low,
"ytdChange": ytdChange,
"lastTradeTime": lastTradeTime,
"isUSMarketOpen": isUsMarketOpen,
};
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final appTitle = 'Isolate Demo';
return MaterialApp(
title: appTitle,
home: MyHomePage(title: appTitle),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: FutureBuilder<List<Photo>>(
future: fetchPhotos(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? PhotosList(photos: snapshot.data)
: Center(child: CircularProgressIndicator());
},
),
);
}
}
class PhotosList extends StatelessWidget {
final List<Photo> photos;
// final parsePhotos = json.decode(response.body).cast<Map<String, dynamic>>();
PhotosList({Key key, this.photos}) : super(key: key);
#override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: photos.length,
itemBuilder: (context, index) {
return Image.network(photos[index].companyName);
},
);
}
}
Error
Launching lib\main.dart on Android SDK built for x86 in debug mode...
Initializing gradle...
Resolving dependencies...
Running Gradle task 'assembleDebug'...
Built build\app\outputs\apk\debug\app-debug.apk.
Flutter is taking longer than expected to report its views. Still trying...
I/OpenGLRenderer( 5371): Initialized EGL, version 1.4
D/OpenGLRenderer( 5371): Swap behavior 1
D/ ( 5371): HostConnection::get() New Host Connection established 0xdd5d9200, tid 5399
D/EGL_emulation( 5371): eglCreateContext: 0xde80f4c0: maj 2 min 0 rcv 2
D/EGL_emulation( 5371): eglMakeCurrent: 0xde80f4c0: ver 2 0 (tinfo 0xde37faf0)
D/ ( 5371): HostConnection::get() New Host Connection established 0xc8bb7680, tid 5390
D/EGL_emulation( 5371): eglCreateContext: 0xde37cc00: maj 2 min 0 rcv 2
D/EGL_emulation( 5371): eglMakeCurrent: 0xde37cc00: ver 2 0 (tinfo 0xde37f810)
I/Choreographer( 5371): Skipped 411 frames! The application may be doing too much work on its main thread.
D/EGL_emulation( 5371): eglMakeCurrent: 0xde80f4c0: ver 2 0 (tinfo 0xde37faf0)
Syncing files to device Android SDK built for x86...
I/OpenGLRenderer( 5371): Davey! duration=8078ms; Flags=1, IntendedVsync=1110681936778, Vsync=1117531936504, OldestInputEvent=9223372036854775807, NewestInputEvent=0, HandleInputStart=1117546732479, AnimationStart=1117546825482, PerformTraversalsStart=1117546831170, DrawStart=1117600733845, SyncQueued=1117602574377, SyncStart=1117645210311, IssueDrawCommandsStart=1117645436189, SwapBuffers=1118338480166, FrameCompleted=1118803458523, DequeueBufferDuration=29600000, QueueBufferDuration=176000,
D/EGL_emulation( 5371): eglMakeCurrent: 0xde37cc00: ver 2 0 (tinfo 0xde37f810)
I/flutter ( 5371): Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List<dynamic>'
Json results:
{"AAPL":{"stats":{"week52change":0.232986,"week52high":255.93,"week52low":142,"marketcap":1136677331400,"employees":137000,"day200MovingAvg":199.29,"day50MovingAvg":225.28,"float":4436680630.59,"avg10Volume":26482506.4,"avg30Volume":26817874.5,"ttmEPS":11.9342,"ttmDividendRate":3,"companyName":"Apple, Inc.","sharesOutstanding":4443270000,"maxChangePercent":252.2871,"year5ChangePercent":1.3384,"year2ChangePercent":0.5329,"year1ChangePercent":0.1512,"ytdChangePercent":0.6199,"month6ChangePercent":0.2152,"month3ChangePercent":0.2274,"month1ChangePercent":0.1391,"day30ChangePercent":0.1696,"day5ChangePercent":0.0272,"nextDividendDate":"2019-11-07","dividendYield":0.011726995543741695,"nextEarningsDate":"2020-02-04","exDividendDate":"2019-11-07","peRatio":21.44,"beta":1.534927573962706},"quote":{"symbol":"AAPL","companyName":"Apple, Inc.","primaryExchange":"NASDAQ","calculationPrice":"close","open":249.5,"openTime":1572615000544,"close":255.82,"closeTime":1572638400572,"high":255.93,"low":249.16,"latestPrice":255.82,"latestSource":"Close","latestTime":"November 1, 2019","latestUpdate":1572638400572,"latestVolume":38139225,"iexRealtimePrice":null,
Json Screenshot
[
Stack Trace
{"AAPL":{"stats":{"week52change":0.232986,"week52high":255.93,"week52low":142,"marketcap":1136677331400,"employees":137000,"day200MovingAvg":199.29,"day50MovingAvg":225.28,"float":4436680630.59,"avg10Volume":26482506.4,"avg30Volume":26817874.5,"ttmEPS":11.9342,"ttmDividendRate":3,"companyName":"Apple, Inc.","sharesOutstanding":4443270000,"maxChangePercent":252.2871,"year5ChangePercent":1.3384,"year2ChangePercent":0.5329,"year1ChangePercent":0.1512,"ytdChangePercent":0.6199,"month6ChangePercent":0.2152,"month3ChangePercent":0.2274,"month1ChangePercent":0.1391,"day30ChangePercent":0.1696,"day5ChangePercent":0.0272,"nextDividendDate":"2019-11-07","dividendYield":0.011726995543741695,"nextEarningsDate":"2020-02-04","exDividendDate":"2019-11-07","peRatio":21.44,"beta":1.534927573962706},"quote":{"symbol":"AAPL","companyName":"Apple, Inc.","primaryExchange":"NASDAQ","calculationPrice":"close","open":249.5,"openTime":1572615000544,"close":255.82,"closeTime":1572638400572,"high":255.93,"low":249.16,"latestPrice":255.82,"latestSource":"Close","latestTime":"November 1, 2019","latestUpdate":1572638400572,"latestVolume":38139225,"iexRealtimePrice":null,
Screenshot
Expected:
Example: https://flutter.dev/docs/cookbook/networking/background-parsing
Root of JSON that you attached as "Json results" is map, but you're trying to assign it to List variable here:
List parsePhotos= json.decode(response.toString());
Make sure you're working right with your response structure. Also access body of the response, don't convert whole response object to string:
Map parsedResponse = json.decode(response.body);
Also I highly recommend using code generators for JSON (de)serialization to make it easier for you. json_serializable and build_runner will help you do that. I'll show you an example how to deserialize an object with nested maps:
{"d":0.5,"nestedMaps":{"test":{"s":"a"},"test2":{"s":"a"}},"list":["a","b"]}
Define class for it and use JsonSerializable annotation:
import 'package:json_annotation/json_annotation.dart';
part 'photo.g.dart';
#JsonSerializable()
class Photo {
final double d;
final Map<String, Map> nestedMaps;
final List<String> list;
Photo(this.d, this.nestedMaps, this.list);
factory Photo.fromJson(Map<String, dynamic> json) => _$PhotoFromJson(json);
Map<String, dynamic> toJson() => _$PhotoToJson(this);
}
You can specify dynamic type as well. Now run code generator:
flutter packages pub run build_runner build
And now you can parse JSON:
Photo.fromJson(jsonDecode('{"d":0.5,"nestedMaps":{"test":{"s":"a"},"test2":{"s":"a"}},"list":["a","b"]}'));
Dart can only decode/encode Map with String keys, so be careful.
Full working demo tested with real token, you can copy paste run full code below
In example code, I have remove token, you have to added it back
json string in real response is "quote" and in your question is "stat"
You have to check response string before layout
Currently use quote is photos[index].data["quote"]["iexRealtimePrice"]
Real json string response from URL
{"AAPL":{"quote":{"symbol":"AAPL","companyName":"Apple, Inc.","primaryExchange":"NASDAQ","calculationPrice":"close","open":265.69,"openTime":1574260200209,"close":263.19,"closeTime":1574283600212,"high":266.083,"low":260.4,"latestPrice":263.19,"latestSource":"Close","latestTime":"November 20, 2019","latestUpdate":1574283600212,"latestVolume":26015446,"iexRealtimePrice":262.83,"iexRealtimeSize":19,"iexLastUpdated":1574283604924,"delayedPrice":263.19,"delayedPriceTime":1574283600212,"extendedPrice":263.35,"extendedChange":0.16,"extendedChangePercent":0.00061,"extendedPriceTime":1574384397682,"previousClose":266.29,"previousVolume":19069597,"change":-3.1,"changePercent":-0.01164,"volume":26015446,"iexMarketPercent":0.015005278018297284,"iexVolume":390369,"avgTotalVolume":24440833,"iexBidPrice":0,"iexBidSize":0,"iexAskPrice":0,"iexAskSize":0,"marketCap":1169424231300,"peRatio":22.05,"week52High":268,"week52Low":142,"ytdChange":0.674594,"lastTradeTime":1574283604924,"isUSMarketOpen":false}},"FB":{"quote":{"symbol":"FB","companyName":"Facebook, Inc.","primaryExchange":"NASDAQ","calculationPrice":"close","open":198.58,"openTime":1574260200212,"close":197.51,"closeTime":1574283600333,"high":199.59,"low":195.43,"latestPrice":197.51,"latestSource":"Close","latestTime":"November 20, 2019","latestUpdate":1574283600333,"latestVolume":11999607,"iexRealtimePrice":197.5,"iexRealtimeSize":100,"iexLastUpdated":1574283599989,"delayedPrice":197.5,"delayedPriceTime":1574283599989,"extendedPrice":196.5,"extendedChange":-1.01,"extendedChangePercent":-0.00511,"extendedPriceTime":1574384390404,"previousClose":199.32,"previousVolume":19070291,"change":-1.81,"changePercent":-0.00908,"volume":11999607,"iexMarketPercent":0.020256580069663948,"iexVolume":243071,"avgTotalVolume":14362105,"iexBidPrice":0,"iexBidSize":0,"iexAskPrice":0,"iexAskSize":0,"marketCap":563391349700,"peRatio":31.36,"week52High":208.66,"week52Low":123.02,"ytdChange":0.459965,"lastTradeTime":1574283599989,"isUSMarketOpen":false}}}
full code remove your token
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<List<Photo>> fetchPhotos(http.Client client) async {
final response =
await client.get('https://cloud.iexapis.com/stable/stock/market/batch?symbols=aapl,fb&types=quote&token=xxx');
/*String response = '''
{"AAPL":
{"stats":{"week52change":0.232986,"week52high":255.93,"week52low":142,"marketcap":1136677331400,"employees":137000,"day200MovingAvg":199.29,"day50MovingAvg":225.28,"float":4436680630.59,"avg10Volume":26482506.4,"avg30Volume":26817874.5,"ttmEPS":11.9342,"ttmDividendRate":3,"companyName":"Apple, Inc."
}
},
"FB":
{"stats":{"week52change":0.1,"week52high":255.93,"week52low":142,"marketcap":1136677331400,"employees":137000,"day200MovingAvg":199.29,"day50MovingAvg":225.28,"float":4436680630.59,"avg10Volume":26482506.4,"avg30Volume":26817874.5,"ttmEPS":11.9342,"ttmDividendRate":3,"companyName":"Apple, Inc."
}
}
}
''';*/
// Use the compute function to run parsePhotos in a separate isolate.
return compute(parsePhotos, response.body);
}
// A function that converts a response body into a List<Photo>.
List<Photo> parsePhotos(String responseBody) {
//final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
//return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
dynamic Obj = json.decode(responseBody);
print(Obj.length);
List<Photo> photoList = [];
Obj.forEach((k, v) => photoList.add(Photo(k, v)));
return photoList;
}
class Photo {
String symbol;
dynamic data;
Photo(this.symbol, this.data);
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final appTitle = 'Isolate Demo';
return MaterialApp(
title: appTitle,
home: MyHomePage(title: appTitle),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: FutureBuilder<List<Photo>>(
future: fetchPhotos(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? PhotosList(photos: snapshot.data)
: Center(child: CircularProgressIndicator());
},
),
);
}
}
class PhotosList extends StatelessWidget {
final List<Photo> photos;
PhotosList({Key key, this.photos}) : super(key: key);
#override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: photos.length,
itemBuilder: (context, index) {
return ListTile(
leading: Icon(Icons.album),
title: Text(photos[index].symbol),
subtitle: Text( ' ${photos[index].data["quote"]["iexRealtimePrice"]}'),
);
},
);
}
}
demo
Try this:
List<Photo> parsePhotos(String response) {
return List<Photo>.from(
json.decode(response.body).map((x) => Photo.fromJson(x)));
}
By the way, jsontodart is really helpful to convert json to object or object to json
Assuming the JSON structure you're downloading is fixed, you can specify the "path" to each property in Photo.fromJson as follows:
close: json["AAPL"]["quote"]["close"],
instead of:
close: json["close"],
Please run the following sample code and you'll see that Photo.close and Photo.latestVolume are now correcly loaded from your JSON because I have specified the correct "path" in Photo.fromJson:
import 'dart:convert';
void main() {
var jsonStream = '{"AAPL":{"stats":{"companyName":"Apple, Inc.","sharesOutstanding":4443270000,"maxChangePercent":252.2871,"year5ChangePercent":1.3384,"year2ChangePercent":0.5329,"beta":1.534927573962706},"quote":{"symbol":"AAPL","companyName":"Apple, Inc.","primaryExchange":"NASDAQ","calculationPrice":"close","open":249.5,"openTime":1572615000544,"close":255.82,"closeTime":1572638400572,"high":255.93,"low":249.16,"latestPrice":255.82,"latestSource":"Close","latestTime":"November 1, 2019","latestUpdate":1572638400572,"latestVolume":38139225,"iexRealtimePrice":null}}}';
var j = json.decode(jsonStream);
print(j);
print("");
print("Latest Volume: ${j["AAPL"]["quote"]["latestVolume"]}");
print("Close: ${j["AAPL"]["quote"]["close"]}");
print("");
var p = Photo.fromJson(j);
print("Latest Volume: ${p.latestVolume}");
print("Close: ${p.close}");
}
class Photo {
final String symbol;
final String companyName;
final String primaryExchange;
final String calculationPrice;
final int open;
final int openTime;
final double close;
final int closeTime;
final double high;
final double low;
final double latestPrice;
final String latestSource;
final String latestTime;
final int latestUpdate;
final int latestVolume;
final dynamic iexRealtimePrice;
final dynamic iexRealtimeSize;
final dynamic iexLastUpdated;
final double delayedPrice;
final int delayedPriceTime;
final double extendedPrice;
final double extendedChange;
final double extendedChangePercent;
final int extendedPriceTime;
final double previousClose;
final int previousVolume;
final int change;
final double changePercent;
final int volume;
final dynamic iexMarketPercent;
final dynamic iexVolume;
final int avgTotalVolume;
final dynamic iexBidPrice;
final dynamic iexBidSize;
final dynamic iexAskPrice;
final dynamic iexAskSize;
final int marketCap;
final double peRatio;
final double week52High;
final int week52Low;
final double ytdChange;
final int lastTradeTime;
final bool isUsMarketOpen;
Photo({
this.symbol,
this.companyName,
this.primaryExchange,
this.calculationPrice,
this.open,
this.openTime,
this.close,
this.closeTime,
this.high,
this.low,
this.latestPrice,
this.latestSource,
this.latestTime,
this.latestUpdate,
this.latestVolume,
this.iexRealtimePrice,
this.iexRealtimeSize,
this.iexLastUpdated,
this.delayedPrice,
this.delayedPriceTime,
this.extendedPrice,
this.extendedChange,
this.extendedChangePercent,
this.extendedPriceTime,
this.previousClose,
this.previousVolume,
this.change,
this.changePercent,
this.volume,
this.iexMarketPercent,
this.iexVolume,
this.avgTotalVolume,
this.iexBidPrice,
this.iexBidSize,
this.iexAskPrice,
this.iexAskSize,
this.marketCap,
this.peRatio,
this.week52High,
this.week52Low,
this.ytdChange,
this.lastTradeTime,
this.isUsMarketOpen,
});
factory Photo.fromJson(Map<String, dynamic> json) => Photo(
symbol: json["symbol"],
companyName: json["companyName"],
primaryExchange: json["primaryExchange"],
calculationPrice: json["calculationPrice"],
open: json["open"],
openTime: json["openTime"],
close: json["AAPL"]["quote"]["close"] as double, // Points to correct "path"
closeTime: json["closeTime"],
high: json["high"] as double,
low: json["low"] as double,
latestPrice: json["latestPrice"] as double,
latestSource: json["latestSource"],
latestTime: json["latestTime"],
latestUpdate: json["latestUpdate"],
latestVolume: json["AAPL"]["quote"]["latestVolume"], // Points to correct "path"
iexRealtimePrice: json["iexRealtimePrice"],
iexRealtimeSize: json["iexRealtimeSize"],
iexLastUpdated: json["iexLastUpdated"],
delayedPrice: json["delayedPrice"] as double,
delayedPriceTime: json["delayedPriceTime"],
extendedPrice: json["extendedPrice"] as double,
extendedChange: json["extendedChange"] as double,
extendedChangePercent: json["extendedChangePercent"] as double,
extendedPriceTime: json["extendedPriceTime"],
previousClose: json["previousClose"] as double,
previousVolume: json["previousVolume"],
change: json["change"],
changePercent: json["changePercent"] as double,
volume: json["volume"],
iexMarketPercent: json["iexMarketPercent"],
iexVolume: json["iexVolume"],
avgTotalVolume: json["avgTotalVolume"],
iexBidPrice: json["iexBidPrice"],
iexBidSize: json["iexBidSize"],
iexAskPrice: json["iexAskPrice"],
iexAskSize: json["iexAskSize"],
marketCap: json["marketCap"],
peRatio: json["peRatio"] as double,
week52High: json["week52High"] as double,
week52Low: json["week52Low"],
ytdChange: json["ytdChange"] as double,
lastTradeTime: json["lastTradeTime"],
isUsMarketOpen: json["isUSMarketOpen"],
);
}
You can run the code in DartPad at: https://dartpad.dartlang.org
One more thing: please note that I have changed your original code:
prop: json["prop"].toDouble(),
to:
propr: json["prop"] as double,
This is because .ToDouble() would fail if the JSON field is null. Unless you have a particular reason to require dynamic fields, you can declare them directly as double
Hope this helps :)