Saving data with the hive package - flutter

How can I save and retrieve this map using hive?:
Map<DateTime, List<PressureModel>> pressures = {
DateTime(2023, 1, 11): [
PressureModel(
pressure: '144 / 64 - 74', date: DateTime(2023, 1, 11, 8, 45)),
PressureModel(
pressure: '157 / 64 - 77', date: DateTime(2023, 1, 11, 20, 8)),
],
DateTime(2023, 1, 12): [
PressureModel(
pressure: '146 / 71 - 92', date: DateTime(2023, 1, 12, 7, 46))
],
DateTime(2023, 1, 14): [
PressureModel(
pressure: '132 / 68 - 74', date: DateTime(2023, 1, 14, 10, 45)),
PressureModel(
pressure: '141 / 53 - 83', date: DateTime(2023, 1, 14, 21, 14)),
],
DateTime(2023, 1, 15): [
PressureModel(
pressure: '124 / 52 - 62', date: DateTime(2023, 1, 15, 10, 26)),
PressureModel(
pressure: '134 / 62 - 91', date: DateTime(2023, 1, 15, 18, 57)),
PressureModel(
pressure: '153 / 68 - 85', date: DateTime(2023, 1, 15, 21, 18)),
]};
This is what the PressureModel class looks like:
class PressureModel {
String pressure;
String action;
DateTime date;
PressureModel({required this.pressure, this.action = '', required this.date});
}
In case anyone is wondering, I did it through SharedPreferences, and ChatGTP helped me do it:
Here is the code to save the data:
Future<void> savePressures(
Map<DateTime, List<PressureModel>> pressures) async {
final prefs = await SharedPreferences.getInstance();
final encodedPressures = json.encode(
pressures.map((key, value) => MapEntry(key.toIso8601String(), value)));
await prefs.setString('pressures', encodedPressures);
}
Here is the code to get the data:
Future<Map<DateTime, List<PressureModel>>> loadPressures() async {
final prefs = await SharedPreferences.getInstance();
final encodedPressures = prefs.getString('pressures_list');
if (encodedPressures == null) {
return {};
}
final decodedPressures =
json.decode(encodedPressures) as Map<String, dynamic>;
final pressures = <DateTime, List<PressureModel>>{};
decodedPressures.forEach((key, value) {
pressures[DateTime.parse(key)] = (value as List<dynamic>)
.map((e) => PressureModel.fromJson(e))
.toList();
});
return pressures;
}

I think you can do that
for retrieve your data:
import 'package:hive/hive.dart';
Box<PressureModel> pressureBox = await Hive.openBox<PressureModel>('pressure');
Map<DateTime, List<PressureModel>> pressures = {};
for (var pressure in pressureBox.values) {
DateTime date = pressure.date;
if (pressures.containsKey(date)) {
pressures[date].add(pressure);
} else {
pressures[date] = [pressure];
}
}
for save your data:
List<PressureModel> pressureList = [];
for (var pressuresForDate in pressures.values) {
pressureList.addAll(pressuresForDate);
}
Box<PressureModel> pressureBox = await Hive.openBox<PressureModel>('pressure');
pressureBox.addAll(pressureList);

Related

The argument type 'Future<String>' can't be assigned to the parameter type 'List<TimelineItem>'

I can use it directly without joining async, but return items to items in DynamicTimeline without finishing execution,
final items = [
TimelineItem(
startDateTime: DateTime(1973, 1, 1, 8, 30),
endDateTime: DateTime(1973, 1, 1, 9, 55),
child: const Event(title: '占位课程'),
),
];
Future<List<TimelineItem>> loadItems(DateTime date) async {
DateTime firstWeek = DateTime(2022, 9, 5);
int week = date.difference(firstWeek).inDays ~/ 7 + 1;
int weekday = date.weekday;
getCourseInfo().then((value) {
List courseList = value.split('},');
items.clear();
for (int i = 0; i < courseList.length; i++) {
if (courseList[i].contains('zc":$week') &&
courseList[i].contains('xq":$weekday')) {
if (courseList[i].contains('jc":"0102')) {
items.add(TimelineItem(
startDateTime: DateTime(1971, 1, 1, 8, 30),
endDateTime: DateTime(1971, 1, 1, 9, 55),
child: Event(
title:
courseList[i].substring(
courseList[i].indexOf('coursename":"') + 12,
courseList[i].indexOf('","teacher"')),
),
));
}
});
return items;
}
SingleChildScrollView(
padding: const EdgeInsets.all(40),
child: DynamicTimeline(
firstDateTime: DateTime(1971, 1, 1, 8, 30),
lastDateTime: DateTime(1971, 1, 1, 22, 30),
labelBuilder: DateFormat('HH:mm').format,
intervalDuration: const Duration(hours: 2),
maxCrossAxisItemExtent: 200,
intervalExtent: 80,
minItemDuration: const Duration(minutes: 30),
items: getCourseInfo()),
),
Items only receives List < TimelineItem >
Items only receives List < TimelineItem >
I would like to ask if I can only return items after all the code in loadItems has been executed.
Future<List<TimelineItem>> loadItems(DateTime date) async {
final items = <TimelineItem>[];
DateTime firstWeek = DateTime(2022, 9, 5);
int week = date.difference(firstWeek).inDays ~/ 7 + 1;
int weekday = date.weekday;
final value = await getCourseInfo();
List courseList = value.split('},');
for (int i = 0; i < courseList.length; i++) {
if (courseList[i].contains('zc":$week') &&
courseList[i].contains('xq":$weekday')) {
if (courseList[i].contains('jc":"0102')) {
items.add(TimelineItem(
startDateTime: DateTime(1971, 1, 1, 8, 30),
endDateTime: DateTime(1971, 1, 1, 9, 55),
child: Event(
title: courseList[i].substring(
courseList[i].indexOf('coursename":"') + 12,
courseList[i].indexOf('","teacher"')),
),
));
}
}
}
return items;
}
BAD code
i think that error from another place, not from this one

Display the below data in List widget flutter and search it

I have below API data display it in Listview and try to search filter in Month Name(date)
[
{
"transGroup": 1,
"transType": 0,
"serviceTypeId":85,
},
{
"transGroup": 2,
"transType": 9,
"serviceTypeId": 78,
}
]
Search filter
void searchFilter(String enteredKeyword) {
List results = [];
if (enteredKeyword.isEmpty) {
results = myData;
} else {
results = myData
.where(
(user) => user["date"].toString().contains(
enteredKeyword.toLowerCase(),
),
)
.toList();
}
}
Textfield Widget
TextFormField(
onChanged: (value) => searchFilter(value),
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
),
initState:
List _foundUsers = [];
#override
initState() {
_foundUsers = myData;
super.initState();
}
My Result->
For your second issue, lets assume this is your list:
var myData = [
{
"transGroup": 1,
"transType": 0,
"serviceTypeId": 0,
"serviceDescription": "Opening Balance",
"financialYear": "2022/2023",
"july": 54818.34,
"august": 54818.34,
"september": 0,
"october": 0,
"november": 0,
"december": 0,
"january": 0,
"febuary": 0,
"march": 0,
"april": 0,
"may": 0,
"june": 0
},
{
"transGroup": 990,
"transType": 0,
"serviceTypeId": 0,
"serviceDescription": "Closing Balance",
"financialYear": "2022/2023",
"july": 54818.34,
"august": 54818.34,
"september": 0,
"october": 0,
"november": 0,
"december": 0,
"january": 0,
"febuary": 0,
"march": 0,
"april": 0,
"may": 0,
"june": 0
}
];
you need define this class model:
class BalanceModel {
final String date;
final double opening;
final double closing;
final double receipts;
BalanceModel({
required this.date,
required this.opening,
required this.closing,
required this.receipts,
});
static const List months = [
"january",
"febuary",
"march",
"april",
"may",
"june",
"july",
"august",
"september",
"october",
"november",
"december"
];
static List<BalanceModel> getData(List data) {
List<BalanceModel> result = [];
for (var month in months) {
double openValue = 0;
double closeValue = 0;
var year = '';
for (var e in data) {
if (e['serviceDescription'] == "Opening Balance") {
var rawYear = e['financialYear'] as String;
year = rawYear.substring(0, rawYear.lastIndexOf('/'));
openValue =
e[month] is int ? double.parse(e[month].toString()) : e[month];
} else if (e['serviceDescription'] == "Closing Balance") {
closeValue =
e[month] is int ? double.parse(e[month].toString()) : e[month];
}
}
result.add(BalanceModel(
date: "$month $year",
opening: openValue,
closing: closeValue,
receipts: closeValue - openValue));
}
return result;
}
}
you can pass your list to it like this:
var result = BalanceModel.getData(myData);
and then use the result list in listview and create your card widget base on it.

Flutter read local Json from assets

I am trying to understand how to work with Json in Flutter/Dart.
I want to read a local Json from the assets folder and display it on the screen.
I have in the assets folder the following Json
{
"title": [
{
"number": 8,
"firstarray": [
26.6, 27, 26.6, 22.9, 27.1, 26.8,
],
"secondarray": [
26.6, 27, 26.6, 22.9, 27.1, 26.8,
]
},
{
"number": 9,
"firstarray": [
26.6, 27, 26.6, 22.9, 27.1, 26.8,
],
"secondarray": [
26.6, 27, 26.6, 22.9, 27.1, 26.8,
]
}
]
}
I tried to created a Class "DataModel" to be able to read from JSON file.
class DataModel {
DataModel({this.measure});
List<DataTitle>? measure;
factory DataModel.fromJson(Map<String, dynamic> json) {
return DataModel(
measure: List<DataTitle>.from(
json['title'].map((c) => DataTitle.fromJson(c)).toList()),
);
}
}
class DataTitle {
DataTitle(
{required this.number,
required this.firstarray,
required this.secondarray});
int? number;
List<double>? firstarray;
List<double>? secondarray;
DataTitle.fromJson(Map<String, dynamic> json) {
number = json['number'];
firstarray = json['firstarray'] == null
? []
: List<double>.from(json['firstarray'].map((x) => x.toDouble()));
secondarray = json['secondarray'] == null
? []
: List<double>.from(json['secondarray'].map((x) => x.toDouble()));
}
}
and I am trying to read and print to the console as follows.
Future loadData() async {
String jsonString = await _loadData();
final jsonResponse = json.decode(jsonString);
DataTitle measure = DataTitle.fromJson(jsonResponse);
print('${measure.number} - ${measure.firstarray} - ${measure.secondarray}');
}
I get at the console printed out,
flutter: null - [] - []
but i was expecting
flutter: 8 - 26.6, 27, 26.6, 22.9, 27.1, 26.8 - 26.6, 27, 26.6, 22.9, 27.1, 26.8,
Updated part:
You tried to convert the whole json structure to an object of your DataModel class. However, in your json, only the list which is under the title node contains DataModelobjects.
Please check the loadData function below, where I first extract the list out of the title node and then make one DataModel object for each element in the list.
class DataModel {
DataModel({this.measure});
List<DataTitle>? measure;
factory DataModel.fromJson(Map<String, dynamic> json) {
return DataModel(
measure: List<DataTitle>.from(
json['title'].map((c) => DataTitle.fromJson(c)).toList()),
);
}
}
class DataTitle {
DataTitle(
{required this.number,
required this.firstarray,
required this.secondarray});
int? number;
List<double>? firstarray;
List<double>? secondarray;
DataTitle.fromJson(Map<String, dynamic> json) {
number = json['number'];
firstarray = json['firstarray'] == null
? []
: List<double>.from(json['firstarray'].map((x) => x.toDouble()));
secondarray = json['secondarray'] == null
? []
: List<double>.from(json['secondarray'].map((x) => x.toDouble()));
}
#override
String toString() {
return 'DataTitle{number: $number, firstarray: $firstarray, secondarray: $secondarray}';
}
}
Future loadData() async {
final jsonString = await rootBundle.loadString('assets/data.json');
final decodedJson = json.decode(jsonString);
List<DataTitle> dataTileList= (decodedJson ['title'] as List)
.map((jsonElement) => DataTitle.fromJson(jsonElement))
.toList();
print(dataTileList.first);
print(dataTileList.last);
}
Original Answer
In your code your json data example does not match the way you try to read it. You provide a single element but you try to read a list of elements.
If the json data contains a single element
Your json structure is of type Map<String, dynamic> so this is the data type which json.decode returns (or you also use the method jsonDecode from import 'dart:convert';).
You then try to cast this Map to List<dynamic>which obviously fails.
Instead, the first part should look like this:
final jsonString =
await rootBundle.rootBundle.loadString('assets/measurelist.json');
final json = json.decode(jsonString) as Map<String, dynamic>;
Afterwards you need to call the fromJsonmethod of your data model class.
In your example you try to use map to create one object for each element in the json array. However, in your example json you only have one object which is not part of an array. So that map doesn't make sense.
What should work is directly calling the fromJson method on the single data element you provide, like:
return DataModel.fromJson(json);
Putting all together, your method should look like this:
Future<DataModel> readJsonData() async {
final jsonString =
await rootBundle.rootBundle.loadString('assets/measurelist.json');
final json = json.decode(jsonString) as Map<String, dynamic>;
return DataModel.fromJson(json);
}
If the json data contains a list of elements
If your datamodel actually contains a list of elements, the json data would look something like this:
[
{
"title": [
{
"number": 8,
"firstarray": [
26.6, 27, 26.6, 2, 27.1, 26.8, 26.6, 26.8, 26.8, 27.2, 26.9, 0, 26.8,
26.8, 26.9, 0, 27.1, 26.8, 27.2, 26.7
],
"secondarray": [
0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 6.4, 6.4, 2.1, 2, 0, 0, 2, 0, 0, 6.3
]
}
]
},
{
"title": [
{
"number": 9,
"firstarray": [
26.6, 27, 26.6, 2, 27.1, 26.8, 26.6, 26.8, 26.8, 27.2, 26.9, 0, 26.8,
26.8, 26.9, 0, 27.1, 26.8, 27.2, 26.7
],
"secondarray": [
0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 6.4, 6.4, 2.1, 2, 0, 0, 2, 0, 0, 6.3
]
}
]
},
...
]
Then your method for parsing it should look something like this:
Future<List<DataModel>> readJsonData() async {
final jsonString =
await rootBundle.rootBundle.loadString('assets/measurelist.json');
final listOfJsonElements = json.decode(jsonString) as List;
return listOfJsonElements.map((jsonElement) => DataModel.fromJson(jsonElement)).toList();
}
Fix your model and future void:
DataModel
class DataModel {
List<Title>? title;
DataModel({this.title});
DataModel.fromJson(Map<String, dynamic> json) {
if (json['title'] != null) {
title = <Title>[];
json['title'].forEach((v) {
title!.add(new Title.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.title != null) {
data['title'] = this.title!.map((v) => v.toJson()).toList();
}
return data;
}
}
class Title {
int? number;
List<double>? firstarray;
List<int>? secondarray;
Title({this.number, this.firstarray, this.secondarray});
Title.fromJson(Map<String, dynamic> json) {
number = json['number'];
firstarray = json['firstarray'].cast<double>();
secondarray = json['secondarray'].cast<int>();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['number'] = this.number;
data['firstarray'] = this.firstarray;
data['secondarray'] = this.secondarray;
return data;
}
}
Function
Future<DataModel> readJsonData() async {
final jsondata = await rootBundle.loadString('assets/measurelist.json');
final list = json.decode(jsondata) as Map<String, dynamic> ;
DataModel res = DataModel.fromJson(list);
return res;
}

How to filter a listview by DateTime

I am trying to create a sensor readings monitoring app and I need to get the records of the readings. I have written the records in list view by order of the time it was sent by the database. I need to be able to search the date and get all the readings that was received along that day.
This is where I get my data from:
http://mushroomdroid.online/dbscript-1.php
and this how I displayed the data using listview:
class RecordsPage extends StatefulWidget {
const RecordsPage({Key key}) : super(key: key);
#override
State<RecordsPage> createState() => _RecordsPageState();
}
class _RecordsPageState extends State<RecordsPage> {
final String url = "http://mushroomdroid.online/dbscript-1.php";
List<Readings> AllData = [];
final controller = TextEditingController();
#override
void initState() {
loadData();
}
loadData() async {
var response =
await http.get(Uri.parse(url), headers: {"Accept": "application/json"});
if (response.statusCode == 200) {
String responseBody = response.body;
var jsonBody = json.decode(responseBody);
for (var data in jsonBody) {
AllData.add(new Readings(
int.parse(data['id']),
double.parse(data['temperature']),
double.parse(data['humidity']),
data['FanStatus'],
data['MistStatus'],
DateTime.parse(data['Time'])));
}
setState(() {});
AllData.forEach((someData) => print("FanStatus: ${someData.FanStatus}"));
} else {
print('Something went wrong');
}
}
#override
Widget build(BuildContext context) {
var container;
return ListView.builder(
itemCount: AllData.length,
itemBuilder: (_, index) {
return new Container(
child: new Card(
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/records.jpg'),
fit: BoxFit.fill,
)),
padding: new EdgeInsets.all(12.0),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(
'READINGS ON: ${AllData[index].Time}',
style: TextStyle(fontSize: 10),
),
new Text(
'TEMPERATURE: ${AllData[index].temperature}',
style: TextStyle(fontSize: 20),
),
new Text(
'HUMIDITY: ${AllData[index].humidity}',
style: TextStyle(fontSize: 20),
),
new Text(
'FAN STATUS: ${AllData[index].FanStatus}',
style: TextStyle(fontSize: 15),
),
new Text(
'MIST STATUS: ${AllData[index].FanStatus}',
style: TextStyle(fontSize: 15),
),
],
),
),
),
);
});
}
}
Please help me. Thank you in advance!
In loadData() method for (var data in jsonBody), you can add some condition to check datetime valid before execute AllData.add.
Example: i write a filter extension that allow i get new list filtered by my condition (filter by name or time).
import 'package:intl/intl.dart';
final timeFm = DateFormat.yMd();
void main() {
var history = <LogHistory>[
LogHistory("A", DateTime(1990, 1, 1).millisecondsSinceEpoch),
LogHistory("A", DateTime(2020, 1, 1).millisecondsSinceEpoch),
LogHistory("B", DateTime(2020, 1, 1).millisecondsSinceEpoch),
];
print('originalObjects: $history');
print('---------------------------------------');
print('filtered by name: A');
print('result: ${history.filterBy(name: "A")}');
print('---------------------------------------');
print('filtered by time: 1/1/2000');
print('result: ${history.filterBy(time: DateTime(2000, 1, 1).millisecondsSinceEpoch)}');
print('---------------------------------------');
print('filtered by time: 1/1/2020');
print('result: ${history.filterBy(time: DateTime(2020, 1, 1).millisecondsSinceEpoch)}');
print('---------------------------------------');
print('filtered by both name & time: B + 1/1/2020');
print('result: ${history.filterBy(name: "B", time: DateTime(2020, 1, 1).millisecondsSinceEpoch)}');
}
class LogHistory {
String name;
int timeInMiliseconds;
LogHistory(this.name, this.timeInMiliseconds);
DateTime get time => DateTime.fromMillisecondsSinceEpoch(timeInMiliseconds);
#override
String toString() => "{name: $name, time: ${timeFm.format(time)}}";
}
extension LFilter on List<LogHistory> {
List<LogHistory> filterBy({String? name, int? time}){
final nameFilter = name ?? "";
final timeFilter = time != null ? DateTime.fromMillisecondsSinceEpoch(time) : null;
List<LogHistory> ret = [];
for (var object in this) {
var passFilter = true;
if(nameFilter.isNotEmpty && object.name != nameFilter){
passFilter = false;
}
if(timeFilter != null && object.time != timeFilter){
passFilter = false;
}
if(passFilter) ret.add(object);
}
return ret;
}
}
Result:
originalObjects: [{name: A, time: 1/1/1990}, {name: A, time: 1/1/2020}, {name: B, time: 1/1/2020}]
---------------------------------------
filtered by name: A
result: [{name: A, time: 1/1/1990}, {name: A, time: 1/1/2020}]
---------------------------------------
filtered by time: 1/1/2000
result: []
---------------------------------------
filtered by time: 1/1/2020
result: [{name: A, time: 1/1/2020}, {name: B, time: 1/1/2020}]
---------------------------------------
filtered by both name & time: B + 1/1/2020
result: [{name: B, time: 1/1/2020}]
Update: update example with your json
import 'package:intl/intl.dart';
final timeFm = DateFormat.yMd();
final json = [
{
"id": "1416",
"temperature": "32.65",
"humidity": "379.4",
"FanStatus": "ON",
"MistStatus": "OFF",
"Time": "2022-05-11 04:30:55"
},
{
"id": "1415",
"temperature": "32.47",
"humidity": "70.61",
"FanStatus": "ON",
"MistStatus": "ON \r\n",
"Time": "2022-05-11 04:20:54"
},
{
"id": "1352",
"temperature": "29.02",
"humidity": "77.61",
"FanStatus": "ON",
"MistStatus": "ON \r\n",
"Time": "2022-05-10 16:48:57"
},
{
"id": "1351",
"temperature": "28.99",
"humidity": "383.65",
"FanStatus": "OFF",
"MistStatus": "OFF",
"Time": "2022-05-10 16:38:57"
},
{
"id": "34",
"temperature": "33.71",
"humidity": "68.53",
"FanStatus": "ON",
"MistStatus": "ON",
"Time": "2021-06-01 03:31:47"
},
{
"id": "33",
"temperature": "33.7",
"humidity": "68.61",
"FanStatus": "ON",
"MistStatus": "ON",
"Time": "2021-05-09 03:31:47"
},
];
void main() {
var history = json.map(LogHistory.fromJson).toList();
print('total results from api: ${history.length}');
print('---------------------------------------');
print('filter by year: 2021');
print('found: ${history.filterByTime(year: 2021).length}');
print('---------------------------------------');
print('filter by month: 5');
print('found: ${history.filterByTime(month: 5).length}');
print('---------------------------------------');
print('filter by day: 10');
print('found: ${history.filterByTime(day: 10).length}');
print('---------------------------------------');
print('filter by both year and month: 2021-06');
print('found: ${history.filterByTime(year: 2021, month: 6).length}');
}
class LogHistory {
int id;
String timeRaw;
LogHistory.fromJson(Map<String, dynamic> data)
: id = int.parse(data['id']),
timeRaw = data['Time'];
DateTime get time => DateTime.parse(timeRaw);
#override
String toString() => "{id: $id, time: ${timeFm.format(time)}}";
}
extension LFilter on List<LogHistory> {
List<LogHistory> filterByTime({int? year, int? month, int? day}) {
List<LogHistory> ret = [];
for (var object in this) {
var passFilter = true;
if (year != null && object.time.year != year) {
passFilter = false;
}
if (month != null && object.time.month != month) {
passFilter = false;
}
if (day != null && object.time.day != day) {
passFilter = false;
}
if (passFilter) ret.add(object);
}
return ret;
}
}
Result:
total results from api: 6
---------------------------------------
filter by year: 2021
found: 2
---------------------------------------
filter by month: 5
found: 5
---------------------------------------
filter by day: 10
found: 2
---------------------------------------
filter by both year and month: 2021-06
found: 1

Flutter OpenWeatherMap api fetch forecast

So... I got this One Call Api which also includes forecast for 7 days. I managed to get display the current weather (temp, and icon) but how do I get the forecast for the next 4 days?
My API: https://api.openweathermap.org/data/2.5/onecall?lat=lat&lon=lon&exclude=minutely,hourly,alerts&cnt=4&appid={MyKey}&units=metric
My model:
class CurrentWeatherModel {
final WeatherInfo weatherInfo;
final double temp;
const CurrentWeatherModel({
required this.weatherInfo, required this.temp
});
factory CurrentWeatherModel.fromJson(json){
return CurrentWeatherModel(
weatherInfo: WeatherInfo.fromJson(json['weather'][0]),
temp: json['temp'],
);
}
}
class HourlyWeatherModel {
final double temp;
final WeatherInfo weatherInfo;
const HourlyWeatherModel(
{
required this.temp,
required this.weatherInfo
}
);
factory HourlyWeatherModel.fromJson(json){
return HourlyWeatherModel(
temp: json['temp'],
weatherInfo: WeatherInfo.fromJson(json['weather'][0])
);
}
}
class DailyWeatherInfoModel {
final TempModel dailyTemp;
final WeatherInfo weatherInfo;
final DateTime date;
final int dt;
const DailyWeatherInfoModel(
{
required this.dailyTemp,
required this.weatherInfo,
required this.date,
required this.dt,
}
);
factory DailyWeatherInfoModel.fromJson(json){
return DailyWeatherInfoModel(
dailyTemp: TempModel.fromJson(json['temp']),
dt: json['dt'],
date: DateTime.fromMillisecondsSinceEpoch(json['dt'] * 1000,
isUtc: true),
weatherInfo: WeatherInfo.fromJson(json['weather'][0]),
);
}
}
class TempModel {
final double day;
final double min;
final double max;
const TempModel(
{
required this.day,
required this.min,
required this.max
}
);
factory TempModel.fromJson(json){
return TempModel(
day: json['day'],
min: json['min'],
max: json['max']
);
}
}
class WeatherInfo {
final String? description;
final String? icon;
WeatherInfo({this.description, this.icon});
factory WeatherInfo.fromJson(Map<String, dynamic> json) {
final description = json['description'];
final icon = json['icon'];
return WeatherInfo(description: description, icon: icon);
}
}
class WeatherForecastResponse {
final CurrentWeatherModel current;
final HourlyWeatherModel hourly;
final DailyWeatherInfoModel daily;
String get iconCurrentWeatherUrl{
return 'https://merakiapp.be/wp-content/uploads/2022/04/${current.weatherInfo.icon}.png';
}
String get iconDailyWeatherUrl{
return 'https://merakiapp.be/wp-content/uploads/2022/04/${daily.weatherInfo.icon}.png';
}
const WeatherForecastResponse(
{
required this.current,
required this.daily,
required this.hourly
}
);
factory WeatherForecastResponse.fromJson(json){
return WeatherForecastResponse(
current: CurrentWeatherModel.fromJson(json['current']),
hourly: HourlyWeatherModel.fromJson(json['hourly'][0]),
daily: DailyWeatherInfoModel.fromJson(json['daily'][0])
);
}
}
My Response:
Future<WeatherForecastResponse> getForecast(double lat, double lon) async {
try {
String api = 'https://api.openweathermap.org/data/2.5/onecall';
String appId = 'MyAPIKey';
String units = 'metric';
String cnt = '4';
String url = '$api?lat=$lat&lon=$lon&cnt=$cnt&appid=$appId&units=$units';
final response = await http.get(Uri.parse(url));
final json = jsonDecode(response.body);
return WeatherForecastResponse.fromJson(json);
} catch (e) {
rethrow;
}
}
Respons object:
void _currentWeather() async {
await FirebaseFirestore.instance.collection('Users').doc(currentUser).get().then((value) => _userLocation = value.data()!['Location']);
setState(() {
lat = _userLocation.latitude;
lon = _userLocation.longitude;
});
final response = await _dataWeatherService.getForecast(lat!, lon!);
setState(() {
temp = response.current.temp.round();
weather = response.current.weatherInfo.description;
icon = response.iconCurrentWeatherUrl;
});
}
void _dailyForecast() async {
final response = await _dataWeatherService.getForecast(lat!, lon!);
setState(() {
tempDaily = response.daily.dailyTemp.day.round();
weatherDaily = response.daily.weatherInfo.description;
iconDaily = response.iconDailyWeatherUrl;
date = response.daily.date;
});
}
JSON from OneCall API:
{
"lat": 33.44,
"lon": -94.04,
"timezone": "America/Chicago",
"timezone_offset": -21600,
"current": {
"dt": 1618317040,
"sunrise": 1618282134,
"sunset": 1618333901,
"temp": 284.07,
"feels_like": 282.84,
"pressure": 1019,
"humidity": 62,
"dew_point": 277.08,
"uvi": 0.89,
"clouds": 0,
"visibility": 10000,
"wind_speed": 6,
"wind_deg": 300,
"weather": [
{
"id": 500,
"main": "Rain",
"description": "light rain",
"icon": "10d"
}
],
"rain": {
"1h": 0.21
}
},
"minutely": [
{
"dt": 1618317060,
"precipitation": 0.205
},
...
},
"hourly": [
{
"dt": 1618315200,
"temp": 282.58,
"feels_like": 280.4,
"pressure": 1019,
"humidity": 68,
"dew_point": 276.98,
"uvi": 1.4,
"clouds": 19,
"visibility": 306,
"wind_speed": 4.12,
"wind_deg": 296,
"wind_gust": 7.33,
"weather": [
{
"id": 801,
"main": "Clouds",
"description": "few clouds",
"icon": "02d"
}
],
"pop": 0
},
...
}
"daily": [
{
"dt": 1618308000,
"sunrise": 1618282134,
"sunset": 1618333901,
"moonrise": 1618284960,
"moonset": 1618339740,
"moon_phase": 0.04,
"temp": {
"day": 279.79,
"min": 275.09,
"max": 284.07,
"night": 275.09,
"eve": 279.21,
"morn": 278.49
},
"feels_like": {
"day": 277.59,
"night": 276.27,
"eve": 276.49,
"morn": 276.27
},
"pressure": 1020,
"humidity": 81,
"dew_point": 276.77,
"wind_speed": 3.06,
"wind_deg": 294,
"weather": [
{
"id": 500,
"main": "Rain",
"description": "light rain",
"icon": "10d"
}
],
"clouds": 56,
"pop": 0.2,
"rain": 0.62,
"uvi": 1.93
},
...
},
"alerts": [
{
"sender_name": "NWS Tulsa",
"event": "Heat Advisory",
"start": 1597341600,
"end": 1597366800,
"description": "...HEAT ADVISORY REMAINS IN EFFECT FROM 1 PM THIS AFTERNOON TO\n8 PM CDT THIS EVENING...\n* WHAT...Heat index values of 105 to 109 degrees expected.\n* WHERE...Creek, Okfuskee, Okmulgee, McIntosh, Pittsburg,\nLatimer, Pushmataha, and Choctaw Counties.\n* WHEN...From 1 PM to 8 PM CDT Thursday.\n* IMPACTS...The combination of hot temperatures and high\nhumidity will combine to create a dangerous situation in which\nheat illnesses are possible.",
"tags": [
"Extreme temperature value"
]
},
...
]
I would change your WeatherForecastResponse to have a list of DailyWeatherInfoModel vs a single model.
class WeatherForecastResponse {
final CurrentWeatherModel current;
final HourlyWeatherModel hourly;
final List<DailyWeatherInfoModel> daily;
...
}
Then you parse a full list in the fromJson constructor
factory WeatherForecastResponse.fromJson(json) {
final dailyResponse = json['daily'] as List;
final dailyForecastList = <DailyWeatherInfoModel>[];
for (final day in dailyResponse) {
dailyForecastList.add(DailyWeatherInfoModel.fromJson(day));
}
return WeatherForecastResponse(
current: CurrentWeatherModel.fromJson(json['current']),
hourly: HourlyWeatherModel.fromJson(json['hourly'][0]),
daily: dailyForecastList);
}
I would also just put the icon url in each daily/hourly model. This way you have a fully self contained list of DailyWeatherInfoModel with everything you need.
class DailyWeatherInfoModel {
final TempModel dailyTemp;
final WeatherInfo weatherInfo;
final DateTime date;
final int dt;
final String iconUrl; // adding this
const DailyWeatherInfoModel({
required this.dailyTemp,
required this.weatherInfo,
required this.date,
required this.dt,
required this.iconUrl,
});
factory DailyWeatherInfoModel.fromJson(json) {
final weatherInfo = WeatherInfo.fromJson(json['weather'][0]);
final iconUrl =
'https://merakiapp.be/wp-content/uploads/2022/04/${weatherInfo.icon}.png'; // init iconUrl here
return DailyWeatherInfoModel(
dailyTemp: TempModel.fromJson(json['temp']),
dt: json['dt'],
date: DateTime.fromMillisecondsSinceEpoch(json['dt'] * 1000, isUtc: true),
weatherInfo: weatherInfo,
iconUrl: iconUrl,
);
}
}
You can apply the same concept to the hourly forecast as well.