Reputation: 1727
I've been struggling a lot with this, not able to parse the data from my firebase realtime database using the following code. I have tried everything but I cannot do so. Below is code of necessary files. I'm using provider for state management.
My Firebase Realtime DB: Consist of 2 documents, 1 contains List of object as you can see, 2nd is just a single object.
My Provider code:
class StockProvider with ChangeNotifier {
List<ChartHistoricalData> _histChartData = [];
List<ChartHistoricalData> get histChartData => _histChartData;
Future<void> fetchHistoricalStockData(ref) async {
ref.once().then((DataSnapshot data) {
print(data.value['SBIN_Historical']);
_histChartData = List<ChartHistoricalData>.from(data
.value['SBIN_Historical']
.map((x) => ChartHistoricalData.fromJson(x as Map<String, dynamic>)));
notifyListeners();
});
}
}
The widget where I call:
class _SBINChartState extends State<SBINChart> {
final fb = FirebaseDatabase.instance;
@override
Widget build(BuildContext context) {
final ref = fb.reference();
context.read<StockProvider>().fetchHistoricalStockData(ref);
...
My Model Class:
class ChartHistoricalData {
DateTime? x; //DateTime
double? open;
double? high;
double? low;
double? close;
ChartHistoricalData(
{required this.x,
required this.open,
required this.high,
required this.low,
required this.close});
ChartHistoricalData.fromJson(Map<String, dynamic> json) {
x = DateTime.parse(json['date']);
open = json['open'];
high = json['high'];
low = json['low'];
close = json['close'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['date'] = this.x;
data['open'] = this.open;
data['high'] = this.high;
data['low'] = this.low;
data['close'] = this.close;
return data;
}
}
I'm getting the following error:
Reloaded 1 of 620 libraries in 1,111ms.
I/flutter (32700): [{date: 2021-01-01, high: 280.0, low: 274.4, close: 279.4, open: 274.9}, {date: 2021-01-04, high: 283.9, low: 277.75, close: 281.05, open: 281.85}, {date: 2021-01-05, high: 282.45, low: 277.0, close: 281.75, open: 278.05}, {date: 2021-01-06, high: 289.15, low: 281.4, close: 285.05, open: 283.0}, {date: 2021-01-07, high: 291.8, low: 287.0, close: 287.7, open: 289.0}, {date: 2021-01-08, high: 291.4, low: 285.2, close: 286.0, open: 290.1}, {date: 2021-01-11, high: 288.2, low: 279.6, close: 282.5, open: 288.0}, {date: 2021-01-12, high: 293.85, low: 277.9, close: 292.5, open: 280.0}, {date: 2021-01-13, high: 308.0, low: 294.5, close: 306.8, open: 296.0}, {date: 2021-01-14, high: 309.25, low: 303.8, close: 307.25, open: 306.7}, {date: 2021-01-15, high: 310.9, low: 301.3, close: 303.85, open: 306.8}, {date: 2021-01-18, high: 308.65, low: 292.2, close: 294.45, open: 303.5}, {date: 2021-01-19, high: 302.5, low: 296.4, close: 298.6, open: 297.65}, {date: 2021-01-20, high: 304.7, low: 296.85, close: 302.55, open: 298.8
E/flutter (32700): [ERROR:flutter/shell/common/shell.cc(103)] Dart Unhandled Exception: type '_InternalLinkedHashMap<Object?, Object?>' is not a subtype of type 'Map<String, dynamic>' in type cast, stack trace: #0 StockProvider.fetchHistoricalStockData.<anonymous closure>.<anonymous closure>
package:algoguru/providers/stock_provider.dart:15
E/flutter (32700): #1 MappedListIterable.elementAt (dart:_internal/iterable.dart:412:31)
E/flutter (32700): #2 ListIterator.moveNext (dart:_internal/iterable.dart:341:26)
E/flutter (32700): #3 new List.from (dart:core-patch/array_patch.dart:40:17)
E/flutter (32700): #4 StockProvider.fetchHistoricalStockData.<anonymous closure>
package:algoguru/providers/stock_provider.dart:13
E/flutter (32700): <asynchronous suspension>
E/flutter (32700): #5 StockProvider.fetchHistoricalStockData
package:algoguru/providers/stock_provider.dart:11
E/flutter (32700): <asynchronous suspension>
E/flutter (32700):
If anyone could help, I would really appreciate it, it's been long debugging...
Upvotes: 3
Views: 3231
Reputation: 7696
You can cast the data.value
to a Map<String, dynamic>
and use it in the fetchHistoricalStockData
method like below:
Future<void> fetchHistoricalStockData(ref) async {
ref.once().then((DataSnapshot data) {
Map<String, dynamic> dataValue = data.value as Map<String, dynamic>;
_histChartData = List<ChartHistoricalData>.from(dataValue['SBIN_Historical']
.map((x) => ChartHistoricalData.fromJson(x as Map<String, dynamic>)));
notifyListeners();
});
}
Upvotes: 2
Reputation: 1727
Okay so finally! It is not a great solution but it did work for me. I don't know and i'm not sure about the data type which firebase_database return us, but by first json encoding snapshot instance data and then json decoding it solved the data type issue. The Code:
Future<void> fetchHistoricalStockData(ref) async {
return ref.once().then((DataSnapshot data) {
final cleanup = jsonDecode(jsonEncode(data.value)); // THIS IS THE ANSWER
_histChartData = List<ChartHistoricalData>.from(cleanup['SBIN_Historical']
.map((x) => ChartHistoricalData.fromJson(x as Map<String, dynamic>)));
notifyListeners();
});
}
Upvotes: 1
Reputation: 3049
Problem:
The problem is located in the method called fetchHistoricalStockData(ref)
, when you are calling ref.once()
it is an Asynchronous method which would take some time to execute, you are awaiting the method but also you are providing .then(data)
callback, so the problem is when the method ref.once()
finished, the code inside .then(data
and the code after ref.once()
are begin executed together, so you are calling notify notifyListeners()
before the data being parsed.
Solution:
Move the call of notifyListeners()
inside .then(data)
callback after completely parsing the data then remove the await
before ref.once()
because no need for it ( the .then(data)
callback is doing the same job ) and replace it with a return
to directly return the feature of ref.once()
and do await
on it in the place where you are calling the method.
I hope it works, good luck.
Upvotes: 1