{"id":400437,"date":"2024-06-29T15:16:05","date_gmt":"2024-06-29T15:16:05","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=400437"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-06-29T15:16:05","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=400437","title":{"rendered":"<span>Creating a Weather Predictions App Using Flutter<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/c63\/21e\/833\/c6321e83325d3cf31ffa53b9728da6b6.jpeg\" alt=\"Photo by Wolfgang Hasselmann on Unsplash\" title=\"Photo by Wolfgang Hasselmann on Unsplash\" width=\"1920\" height=\"1280\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c63\/21e\/833\/c6321e83325d3cf31ffa53b9728da6b6.jpeg\" data-blurred=\"true\"\/><figcaption>Photo by Wolfgang Hasselmann on Unsplash<\/figcaption><\/figure>\n<p>This article describes how to develop a real-time weather prediction app in Flutter using the Tomorrow.io Weather API. The app consists of a single screen that displays the current weather and a 4-day daily forecast for a specific location. We\u2019re creating this prediction app on Flutter because it is an open-source UI development kit, which means it can be integrated beautifully into desktop and mobile apps across various platforms, making it scalable.\u00a0<\/p>\n<h3>Prerequisites<\/h3>\n<p>If you wish to follow along with this tutorial, you must have the following set up:<\/p>\n<ul>\n<li>\n<p>A free <a href=\"https:\/\/app.tomorrow.io\/?planid=60d46beae90c3b3549a59ff3\" rel=\"noopener noreferrer nofollow\">Tomorrow.io<\/a> account to access their weather-related features and data<\/p>\n<\/li>\n<li>\n<p>Any IDE that has the Flutter SDK installed (i.e., Android Studio, VSCode)<\/p>\n<\/li>\n<li>\n<p>A basic understanding of Dart and Flutter<\/p>\n<\/li>\n<\/ul>\n<p>This tutorial was verified with <a href=\"https:\/\/flutter.dev\/\" rel=\"noopener noreferrer nofollow\">Flutter<\/a> v2.5.1 and Android Studio v3.5.\u00a0<\/p>\n<p>So, with everything out the way, let&#8217;s get started.<\/p>\n<h3>Getting started with Tomorrow.io<\/h3>\n<p><a href=\"http:\/\/Tomorrow.io\" rel=\"noopener noreferrer nofollow\">Tomorrow.io<\/a> is a SaaS weather intelligence platform that provides real-time weather forecasts. It offers one of the most reliable free-to-use weather APIs. They provide an all-in-one endpoint that makes it easy to retrieve weather data by making a single call to the endpoint.<\/p>\n<p>After creating an account with Tomorrow.io, we need to retrieve our apikey from the Development menu on the sidebar. \u00a0The image below shows the Development screen.\u00a0<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/cab\/97b\/c18\/cab97bc18aff2eb44842a3ebcd3fcfd8.png\" width=\"1349\" height=\"625\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/cab\/97b\/c18\/cab97bc18aff2eb44842a3ebcd3fcfd8.png\"\/><figcaption><\/figcaption><\/figure>\n<h3>Project Setup<\/h3>\n<p>Step 1: Creating a new Flutter project<\/p>\n<p>Create a new Flutter project by running the following command in your terminal.<\/p>\n<p><code>flutter create weather_app<\/code><\/p>\n<p>Step 2: Installing Dependencies\u00a0<\/p>\n<p>Next, we need to add the dio package as a dependency in our project. The dio package will be used for making network requests.\u00a0<\/p>\n<p>Run the following command to get the newest version of the dio package in your project.<\/p>\n<p><code>flutter pub add dio<\/code><\/p>\n<p>This command automatically adds the dio package in your pubspec.yaml file.<\/p>\n<p>Step 3 : Creating the Weather Model<\/p>\n<p>Let&#8217;s create a weather model class. The class should have instance variables, a constructor for initializing fields upon object creation, and methods for translating JSON data from the API to <a href=\"https:\/\/www.w3adda.com\/dart-tutorial\/dart-classes\" rel=\"noopener noreferrer nofollow\">Dart Classes<\/a>. It is recommended that we write a model class to make our code easier to read, understand, and maintain.<\/p>\n<p>Create a new dart file weather_model.dart and paste the following code in it. \u00a0<\/p>\n<pre><code>import 'dart:convert';  Weather weatherFromJson(String str) => Weather.fromJson(json.decode(str));  String weatherToJson(Weather data) => json.encode(data.toJson());  class Weather {  \u00a0 Weather({  \u00a0 \u00a0 required this.data,  \u00a0 \u00a0 required this.warnings,  \u00a0 });  \u00a0 Data data;  \u00a0 List&lt;Warning> warnings;  \u00a0 factory Weather.fromJson(Map&lt;String, dynamic> json) => Weather(  \u00a0 \u00a0 \u00a0 \u00a0 data: Data.fromJson(json[\"data\"]),  \u00a0 \u00a0 \u00a0 \u00a0 warnings: List&lt;Warning>.from(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 json[\"warnings\"].map((x) => Warning.fromJson(x))),  \u00a0 \u00a0 \u00a0 );  \u00a0 Map&lt;String, dynamic> toJson() => {  \u00a0 \u00a0 \u00a0 \u00a0 \"data\": data.toJson(),  \u00a0 \u00a0 \u00a0 \u00a0 \"warnings\": List&lt;dynamic>.from(warnings.map((x) => x.toJson())),  \u00a0 \u00a0 \u00a0 };  }  class Data {  \u00a0 Data({  \u00a0 \u00a0 required this.timelines,  \u00a0 });  \u00a0 List&lt;Timeline> timelines;  \u00a0 factory Data.fromJson(Map&lt;String, dynamic> json) => Data(  \u00a0 \u00a0 \u00a0 \u00a0 timelines: List&lt;Timeline>.from(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 json[\"timelines\"].map((x) => Timeline.fromJson(x))),  \u00a0 \u00a0 \u00a0 );  \u00a0 Map&lt;String, dynamic> toJson() => {  \u00a0 \u00a0 \u00a0 \u00a0 \"timelines\": List&lt;dynamic>.from(timelines.map((x) => x.toJson())),  \u00a0 \u00a0 \u00a0 };  }  class Timeline {  \u00a0 Timeline({  \u00a0 \u00a0 required this.timestep,  \u00a0 \u00a0 required this.startTime,  \u00a0 \u00a0 required this.endTime,  \u00a0 \u00a0 required this.intervals,  \u00a0 });  \u00a0 String timestep;  \u00a0 DateTime startTime;  \u00a0 DateTime endTime;  \u00a0 List&lt;Interval> intervals;  \u00a0 factory Timeline.fromJson(Map&lt;String, dynamic> json) => Timeline(  \u00a0 \u00a0 \u00a0 \u00a0 timestep: json[\"timestep\"],  \u00a0 \u00a0 \u00a0 \u00a0 startTime: DateTime.parse(json[\"startTime\"]),  \u00a0 \u00a0 \u00a0 \u00a0 endTime: DateTime.parse(json[\"endTime\"]),  \u00a0 \u00a0 \u00a0 \u00a0 intervals: List&lt;Interval>.from(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 json[\"intervals\"].map((x) => Interval.fromJson(x))),  \u00a0 \u00a0 \u00a0 );  \u00a0 Map&lt;String, dynamic> toJson() => {  \u00a0 \u00a0 \u00a0 \u00a0 \"timestep\": timestep,  \u00a0 \u00a0 \u00a0 \u00a0 \"startTime\": startTime.toIso8601String(),  \u00a0 \u00a0 \u00a0 \u00a0 \"endTime\": endTime.toIso8601String(),  \u00a0 \u00a0 \u00a0 \u00a0 \"intervals\": List&lt;dynamic>.from(intervals.map((x) => x.toJson())),  \u00a0 \u00a0 \u00a0 };  }  class Interval {  \u00a0 Interval({  \u00a0 \u00a0 required this.startTime,  \u00a0 \u00a0 required this.values,  \u00a0 });  \u00a0 DateTime startTime;  \u00a0 Values values;  \u00a0 factory Interval.fromJson(Map&lt;String, dynamic> json) => Interval(  \u00a0 \u00a0 \u00a0 \u00a0 startTime: DateTime.parse(json[\"startTime\"]),  \u00a0 \u00a0 \u00a0 \u00a0 values: Values.fromJson(json[\"values\"]),  \u00a0 \u00a0 \u00a0 );  \u00a0 Map&lt;String, dynamic> toJson() => {  \u00a0 \u00a0 \u00a0 \u00a0 \"startTime\": startTime.toIso8601String(),  \u00a0 \u00a0 \u00a0 \u00a0 \"values\": values.toJson(),  \u00a0 \u00a0 \u00a0 };  }  class Values {  \u00a0 Values({  \u00a0 \u00a0 required this.windSpeed,  \u00a0 \u00a0 required this.windDirection,  \u00a0 \u00a0 required this.temperature,  \u00a0 \u00a0 required this.temperatureApparent,  \u00a0 \u00a0 required this.weatherCode,  \u00a0 \u00a0 required this.humidity,  \u00a0 \u00a0 required this.visibility,  \u00a0 \u00a0 required this.dewPoint,  \u00a0 \u00a0 required this.precipitationType,  \u00a0 \u00a0 required this.cloudCover,  \u00a0 });  \u00a0 double windSpeed;  \u00a0 double windDirection;  \u00a0 double temperature;  \u00a0 double temperatureApparent;  \u00a0 int weatherCode;  \u00a0 double humidity;  \u00a0 double visibility;  \u00a0 double dewPoint;  \u00a0 int precipitationType;  \u00a0 double cloudCover;  \u00a0 factory Values.fromJson(Map&lt;String, dynamic> json) => Values(  \u00a0 \u00a0 \u00a0 \u00a0 windSpeed: json[\"windSpeed\"].toDouble(),  \u00a0 \u00a0 \u00a0 \u00a0 windDirection: json[\"windDirection\"].toDouble(),  \u00a0 \u00a0 \u00a0 \u00a0 temperature: json[\"temperature\"].toDouble(),  \u00a0 \u00a0 \u00a0 \u00a0 temperatureApparent: json[\"temperatureApparent\"].toDouble(),  \u00a0 \u00a0 \u00a0 \u00a0 weatherCode: json[\"weatherCode\"],  \u00a0 \u00a0 \u00a0 \u00a0 humidity: json[\"humidity\"].toDouble(),  \u00a0 \u00a0 \u00a0 \u00a0 visibility: json[\"visibility\"].toDouble(),  \u00a0 \u00a0 \u00a0 \u00a0 dewPoint: json[\"dewPoint\"].toDouble(),  \u00a0 \u00a0 \u00a0 \u00a0 precipitationType: json[\"precipitationType\"],  \u00a0 \u00a0 \u00a0 \u00a0 cloudCover: json[\"cloudCover\"].toDouble(),  \u00a0 \u00a0 \u00a0 );  \u00a0 Map&lt;String, dynamic> toJson() => {  \u00a0 \u00a0 \u00a0 \u00a0 \"windSpeed\": windSpeed,  \u00a0 \u00a0 \u00a0 \u00a0 \"windDirection\": windDirection,  \u00a0 \u00a0 \u00a0 \u00a0 \"temperature\": temperature,  \u00a0 \u00a0 \u00a0 \u00a0 \"temperatureApparent\": temperatureApparent,  \u00a0 \u00a0 \u00a0 \u00a0 \"weatherCode\": weatherCode,  \u00a0 \u00a0 \u00a0 \u00a0 \"humidity\": humidity,  \u00a0 \u00a0 \u00a0 \u00a0 \"visibility\": visibility,  \u00a0 \u00a0 \u00a0 \u00a0 \"dewPoint\": dewPoint,  \u00a0 \u00a0 \u00a0 \u00a0 \"precipitationType\": precipitationType,  \u00a0 \u00a0 \u00a0 \u00a0 \"cloudCover\": cloudCover,  \u00a0 \u00a0 \u00a0 };  }  class Warning {  \u00a0 Warning({  \u00a0 \u00a0 required this.code,  \u00a0 \u00a0 required this.type,  \u00a0 \u00a0 required this.message,  \u00a0 });  \u00a0 int code;  \u00a0 String type;  \u00a0 String message;  \u00a0 factory Warning.fromJson(Map&lt;String, dynamic> json) => Warning(  \u00a0 \u00a0 \u00a0 \u00a0 code: json[\"code\"],  \u00a0 \u00a0 \u00a0 \u00a0 type: json[\"type\"],  \u00a0 \u00a0 \u00a0 \u00a0 message: json[\"message\"],  \u00a0 \u00a0 \u00a0 );  \u00a0 Map&lt;String, dynamic> toJson() => {  \u00a0 \u00a0 \u00a0 \u00a0 \"code\": code,  \u00a0 \u00a0 \u00a0 \u00a0 \"type\": type,  \u00a0 \u00a0 \u00a0 \u00a0 \"message\": message,  \u00a0 \u00a0 \u00a0 };  }  <\/code><\/pre>\n<p>The weather model is based on the structure of the JSON data we are to receive from the endpoint.<\/p>\n<p>Step 4: Setting up the ApiClient<\/p>\n<p>After creating the weather model class, we&#8217;ll need to construct our ApiClient class, which will handle network calls to the API endpoint.<\/p>\n<p>The code snippet below shows how we get the weather data of a specific location using the Dio package. First, we initialize the Dio package with our baseUrl. Then, we specify our query parameters apikey, location, fields, units, timesteps, startTime, and endTime. \u00a0<\/p>\n<p>You can access the Tomorrow.io documentation to see a preview of the <a href=\"https:\/\/docs.tomorrow.io\/reference\/get-timelines\" rel=\"noopener noreferrer nofollow\">query parameters<\/a> accepted by the Tomorrow API, and what data you can get from the fields attribute.<\/p>\n<p>Next, we create a method called <code>getWeather<\/code>, inside which we call the GET method on the Dio object and pass in the required parameters. Then, we await the response. If successful, we parse the JSON data and return it; otherwise, we produce an error message.<\/p>\n<pre><code>import 'package:dio\/dio.dart';  import 'package:tomorrow_weather\/model\/weather.dart';  class ApiClient {  \u00a0 final Dio _dio = Dio(BaseOptions(  \u00a0 \u00a0 baseUrl: \"https:\/\/api.tomorrow.io\/v4\",  \u00a0 ));  \u00a0 \/\/replace with your ApiKEY  \u00a0 \/\/get your key from app.tomorrow.io\/development\/keys  \u00a0 static const String apiKey = 'YOUR_API_KEY';  \u00a0 \/\/pick a location to get the weather  \u00a0 static const location = [40.758, -73.9855]; \/\/New York  \u00a0 \/\/\/\/ list the fields you want to get from the api  \u00a0 static const fields = [  \u00a0 \u00a0 \"windSpeed\",  \u00a0 \u00a0 \"windDirection\",  \u00a0 \u00a0 \"temperature\",  \u00a0 \u00a0 \"temperatureApparent\",  \u00a0 \u00a0 \"weatherCode\",  \u00a0 \u00a0 \"humidity\",  \u00a0 \u00a0 \"visibility\",  \u00a0 \u00a0 \"dewPoint\",  \u00a0 \u00a0 \"cloudCover\",  \u00a0 \u00a0 \"precipitationType\"  \u00a0 ];  \u00a0 \/\/ choose the unit system, either metric or imperial  \u00a0 static const units = \"imperial\";  \u00a0 \/\/ set the timesteps, like \"current\" and \"1d\"  \u00a0 static const timesteps = [\"current\", \"1d\"];  \u00a0 \/\/ configure the time frame up to view the current and 4-days weather forecast  \u00a0 String startTime =  \u00a0 \u00a0 \u00a0 DateTime.now().toUtc().add(const Duration(minutes: 0)).toIso8601String();  \u00a0 String endTime =  \u00a0 \u00a0 \u00a0 DateTime.now().toUtc().add(const Duration(days: 4)).toIso8601String();  \u00a0 \/\/method to get the weather data  \u00a0 Future&lt;Weather> getWeather() async {  \u00a0 \u00a0 try {  \u00a0 \u00a0 \u00a0 final response = await _dio.get('\/timelines', queryParameters: {  \u00a0 \u00a0 \u00a0 \u00a0 'location': location.join(','),  \u00a0 \u00a0 \u00a0 \u00a0 'apikey': apiKey,  \u00a0 \u00a0 \u00a0 \u00a0 'fields': fields,  \u00a0 \u00a0 \u00a0 \u00a0 'units': units,  \u00a0 \u00a0 \u00a0 \u00a0 'timesteps': timesteps,  \u00a0 \u00a0 \u00a0 \u00a0 'startTime': startTime,  \u00a0 \u00a0 \u00a0 \u00a0 'endTime': endTime  \u00a0 \u00a0 \u00a0 });  \u00a0 \u00a0 \u00a0 \/\/parse the JSON data, returns the Weather data\u00a0  \u00a0 \u00a0 \u00a0 return Weather.fromJson(response.data);  \u00a0 \u00a0 } on DioError catch (e) {  \u00a0 \u00a0 \u00a0 \/\/returns the error if any  \u00a0 \u00a0 \u00a0 return e.response!.data;  \u00a0 \u00a0 }  \u00a0 }  }  <\/code><\/pre>\n<p>That concludes the ApiClient class. Next, we&#8217;ll build the UI for our weather application and consume the API data.<\/p>\n<p>Step 5: Consuming the API Data<\/p>\n<p>We have a single screen that displays the current and 4-day forecast weather statistics.\u00a0<\/p>\n<p>The code snippet below shows how we used the FutureBuilder weather widget to display the result of our getWeather method. If it contains data, it returns the weather data; else, it renders an error on the screen. If none of the aforementioned apply, we display the CircularProgressIndicator widget to indicate that our data is loading.<\/p>\n<pre><code>\/\/...  \/\/...  class _HomePageState extends State&lt;HomePage> {  \u00a0 final ApiClient _apiClient = ApiClient(); \/\/initialize the ApiClient\u00a0  \u00a0   \u00a0 Widget build(BuildContext context) {  \u00a0 \u00a0 return Scaffold(  \u00a0 \u00a0 \u00a0 backgroundColor: AppColors.backgroundColor,  \u00a0 \u00a0 \u00a0 body: SafeArea(  \u00a0 \u00a0 \u00a0 \u00a0 child: FutureBuilder&lt;Weather>( \u00a0 \u00a0 \/\/&lt;&lt;-- FutureBuilder\u00a0  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 future: _apiClient.getWeather(),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 builder: (context, snapshot) {  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/loading state  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 if (snapshot.connectionState == ConnectionState.waiting) {  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 return const Center(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 child: CircularProgressIndicator(),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 );  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 } else {  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/when the future is complete and there is an error, display it.  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 if (snapshot.hasError) {  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 return Center(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 child: Text(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 'Error: ${snapshot.error}',  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 style: const TextStyle(color: Colors.red),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 );  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 } else {  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/when the future is complete and has no error, show the weather.  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/get weatherCode  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 int weatherCode = snapshot  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .data!.data.timelines[0].intervals[0].values.weatherCode;  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/get weatherName  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 String weatherCodeName =  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ApiClient.getWeatherCodeName(weatherCode);  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/get weatherIcon  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 String weatherCodeIcon =  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ApiClient.getWeatherIcon(weatherCodeName);  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 return Stack(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 children: [  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/...  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Padding(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 padding: const EdgeInsets.symmetric(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 vertical: 15,  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 horizontal: 15.0,  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 child: ListView(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 shrinkWrap: true,  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 children: [  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 const Text('Current Weather',  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 style: TextStyle(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 color: AppColors.greyShade1,  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 fontSize: 30.0,  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 fontWeight: FontWeight.w700,  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 )),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 const SizedBox(height: 30.0),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Image.asset(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 weatherCodeIcon, \u00a0 \/\/weatherIcon  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 width: 150,  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 height: 150,  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Center(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 child: RichText(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 text: TextSpan(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/accessing the temperature value from the snapshot  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 text: snapshot.data!.data.timelines[0]  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .intervals[0].values.temperature  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .toStringAsFixed(0)  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .toString(),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 style: const TextStyle(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 fontFamily: 'Raleway',  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 fontSize: 144,  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 color: AppColors.greyShade1,  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 fontWeight: FontWeight.w500),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 children: const [  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 TextSpan(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 text: '\u00b0F',  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 style: TextStyle(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 fontFamily: 'Raleway',  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 fontSize: 48,  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 color: AppColors.textBlueColor,  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 fontWeight: FontWeight.w500,  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 )  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ],  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ),  \/\/...  \/\/...<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/19c\/fca\/a9c\/19cfcaa9ccfadd9d2df9ea3bad4c15e5.jpeg\" width=\"996\" height=\"2160\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/19c\/fca\/a9c\/19cfcaa9ccfadd9d2df9ea3bad4c15e5.jpeg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<h3>Conclusion<\/h3>\n<p>In this tutorial, we&#8217;ve learned what Tomorrow.io is and how we can interact with its API to build a weather prediction application. The source code of the demo application is available on <a href=\"https:\/\/github.com\/iloveteajay\/tomorrow_weather_app\" rel=\"noopener noreferrer nofollow\">GitHub<\/a>. We\u2019ve done this using Flutter to enable integration into desktop and mobile applications, making it scalable for any use \u2013 private or public.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/656033\/\"> https:\/\/habr.com\/ru\/articles\/656033\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><figcaption>Photo by Wolfgang Hasselmann on Unsplash<\/figcaption><\/figure>\n<p>This article describes how to develop a real-time weather prediction app in Flutter using the Tomorrow.io Weather API. The app consists of a single screen that displays the current weather and a 4-day daily forecast for a specific location. We\u2019re creating this prediction app on Flutter because it is an open-source UI development kit, which means it can be integrated beautifully into desktop and mobile apps across various platforms, making it scalable.\u00a0<\/p>\n<h3>Prerequisites<\/h3>\n<p>If you wish to follow along with this tutorial, you must have the following set up:<\/p>\n<ul>\n<li>\n<p>A free <a href=\"https:\/\/app.tomorrow.io\/?planid=60d46beae90c3b3549a59ff3\" rel=\"noopener noreferrer nofollow\">Tomorrow.io<\/a> account to access their weather-related features and data<\/p>\n<\/li>\n<li>\n<p>Any IDE that has the Flutter SDK installed (i.e., Android Studio, VSCode)<\/p>\n<\/li>\n<li>\n<p>A basic understanding of Dart and Flutter<\/p>\n<\/li>\n<\/ul>\n<p>This tutorial was verified with <a href=\"https:\/\/flutter.dev\/\" rel=\"noopener noreferrer nofollow\">Flutter<\/a> v2.5.1 and Android Studio v3.5.\u00a0<\/p>\n<p>So, with everything out the way, let&#8217;s get started.<\/p>\n<h3>Getting started with Tomorrow.io<\/h3>\n<p><a href=\"http:\/\/Tomorrow.io\" rel=\"noopener noreferrer nofollow\">Tomorrow.io<\/a> is a SaaS weather intelligence platform that provides real-time weather forecasts. It offers one of the most reliable free-to-use weather APIs. They provide an all-in-one endpoint that makes it easy to retrieve weather data by making a single call to the endpoint.<\/p>\n<p>After creating an account with Tomorrow.io, we need to retrieve our apikey from the Development menu on the sidebar. \u00a0The image below shows the Development screen.\u00a0<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h3>Project Setup<\/h3>\n<p>Step 1: Creating a new Flutter project<\/p>\n<p>Create a new Flutter project by running the following command in your terminal.<\/p>\n<p><code>flutter create weather_app<\/code><\/p>\n<p>Step 2: Installing Dependencies\u00a0<\/p>\n<p>Next, we need to add the dio package as a dependency in our project. The dio package will be used for making network requests.\u00a0<\/p>\n<p>Run the following command to get the newest version of the dio package in your project.<\/p>\n<p><code>flutter pub add dio<\/code><\/p>\n<p>This command automatically adds the dio package in your pubspec.yaml file.<\/p>\n<p>Step 3 : Creating the Weather Model<\/p>\n<p>Let&#8217;s create a weather model class. The class should have instance variables, a constructor for initializing fields upon object creation, and methods for translating JSON data from the API to <a href=\"https:\/\/www.w3adda.com\/dart-tutorial\/dart-classes\" rel=\"noopener noreferrer nofollow\">Dart Classes<\/a>. It is recommended that we write a model class to make our code easier to read, understand, and maintain.<\/p>\n<p>Create a new dart file weather_model.dart and paste the following code in it. \u00a0<\/p>\n<pre><code>import 'dart:convert';  Weather weatherFromJson(String str) => Weather.fromJson(json.decode(str));  String weatherToJson(Weather data) => json.encode(data.toJson());  class Weather {  \u00a0 Weather({  \u00a0 \u00a0 required this.data,  \u00a0 \u00a0 required this.warnings,  \u00a0 });  \u00a0 Data data;  \u00a0 List&lt;Warning> warnings;  \u00a0 factory Weather.fromJson(Map&lt;String, dynamic> json) => Weather(  \u00a0 \u00a0 \u00a0 \u00a0 data: Data.fromJson(json[\"data\"]),  \u00a0 \u00a0 \u00a0 \u00a0 warnings: List&lt;Warning>.from(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 json[\"warnings\"].map((x) => Warning.fromJson(x))),  \u00a0 \u00a0 \u00a0 );  \u00a0 Map&lt;String, dynamic> toJson() => {  \u00a0 \u00a0 \u00a0 \u00a0 \"data\": data.toJson(),  \u00a0 \u00a0 \u00a0 \u00a0 \"warnings\": List&lt;dynamic>.from(warnings.map((x) => x.toJson())),  \u00a0 \u00a0 \u00a0 };  }  class Data {  \u00a0 Data({  \u00a0 \u00a0 required this.timelines,  \u00a0 });  \u00a0 List&lt;Timeline> timelines;  \u00a0 factory Data.fromJson(Map&lt;String, dynamic> json) => Data(  \u00a0 \u00a0 \u00a0 \u00a0 timelines: List&lt;Timeline>.from(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 json[\"timelines\"].map((x) => Timeline.fromJson(x))),  \u00a0 \u00a0 \u00a0 );  \u00a0 Map&lt;String, dynamic> toJson() => {  \u00a0 \u00a0 \u00a0 \u00a0 \"timelines\": List&lt;dynamic>.from(timelines.map((x) => x.toJson())),  \u00a0 \u00a0 \u00a0 };  }  class Timeline {  \u00a0 Timeline({  \u00a0 \u00a0 required this.timestep,  \u00a0 \u00a0 required this.startTime,  \u00a0 \u00a0 required this.endTime,  \u00a0 \u00a0 required this.intervals,  \u00a0 });  \u00a0 String timestep;  \u00a0 DateTime startTime;  \u00a0 DateTime endTime;  \u00a0 List&lt;Interval> intervals;  \u00a0 factory Timeline.fromJson(Map&lt;String, dynamic> json) => Timeline(  \u00a0 \u00a0 \u00a0 \u00a0 timestep: json[\"timestep\"],  \u00a0 \u00a0 \u00a0 \u00a0 startTime: DateTime.parse(json[\"startTime\"]),  \u00a0 \u00a0 \u00a0 \u00a0 endTime: DateTime.parse(json[\"endTime\"]),  \u00a0 \u00a0 \u00a0 \u00a0 intervals: List&lt;Interval>.from(  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 json[\"intervals\"].map((x) => Interval.fromJson(x))),  \u00a0 \u00a0 \u00a0 );  \u00a0 Map&lt;String, dynamic> toJson() => {  \u00a0 \u00a0 \u00a0 \u00a0 \"timestep\": timestep,  \u00a0 \u00a0 \u00a0 \u00a0 \"startTime\": startTime.toIso8601String(),  \u00a0 \u00a0 \u00a0 \u00a0 \"endTime\": endTime.toIso8601String(),  \u00a0 \u00a0 \u00a0 \u00a0 \"intervals\": List&lt;dynamic>.from(intervals.map((x) => x.toJson())),  \u00a0 \u00a0 \u00a0 };  }  class Interval {  \u00a0 Interval({  \u00a0 \u00a0 required this.startTime,  \u00a0 \u00a0 required this.values,  \u00a0 });  \u00a0 DateTime startTime;  \u00a0 Values values;  \u00a0 factory Interval.fromJson(Map&lt;String, dynamic> json) => Interval(  \u00a0 \u00a0 \u00a0 \u00a0 startTime: DateTime.parse(json[\"startTime\"]),  \u00a0 \u00a0 \u00a0 \u00a0 values: Values.fromJson(json[\"values\"]),  \u00a0 \u00a0 \u00a0 );  \u00a0 Map&lt;String, dynamic> toJson() => {  \u00a0 \u00a0 \u00a0 \u00a0 \"startTime\": startTime.toIso8601String(),  \u00a0 \u00a0 \u00a0 \u00a0 \"values\": values.toJson(),  \u00a0 \u00a0 \u00a0 };  }  class Values {  \u00a0 Values({  \u00a0 \u00a0 required this.windSpeed,  \u00a0 \u00a0 required this.windDirection,  \u00a0 \u00a0 required this.temperature,  \u00a0 \u00a0 required this.temperatureApparent,  \u00a0 \u00a0 required this.weatherCode,  \u00a0 \u00a0 required this.humidity,  \u00a0 \u00a0 required this.visibility,  \u00a0 \u00a0 required this.dewPoint,  \u00a0 \u00a0 required this.precipitationType,  \u00a0 \u00a0 required this.cloudCover,  \u00a0 });  \u00a0 double windSpeed;  \u00a0 double windDirection;  \u00a0 double temperature;  \u00a0 double temperatureApparent;  \u00a0 int weatherCode;  \u00a0 double humidity;  \u00a0 double visibility;  \u00a0 double dewPoint;  \u00a0 int precipitationType;  \u00a0 double cloudCover;  \u00a0 factory Values.fromJson(Map&lt;String, dynamic> json) => Values(  \u00a0 \u00a0 \u00a0 \u00a0 windSpeed: json[\"windSpeed\"].toDouble(),  \u00a0 \u00a0 \u00a0 \u00a0 windDirection: json[\"windDirection\"].toDouble(),  \u00a0 \u00a0 \u00a0 \u00a0 temperature: json[\"temperature\"].toDouble(),  \u00a0 \u00a0 \u00a0 \u00a0 temperatureApparent: json[\"temperatureApparent\"].toDouble(),  \u00a0 \u00a0 \u00a0 \u00a0 weatherCode: json[\"weatherCode\"],  \u00a0 \u00a0 \u00a0 \u00a0 humidity: json[\"humidity\"].toDouble(),  \u00a0 \u00a0 \u00a0 \u00a0 visibility: json[\"visibility\"].toDouble(),  \u00a0 \u00a0 \u00a0 \u00a0 dewPoint: json[\"dewPoint\"].toDouble(),  \u00a0 \u00a0 \u00a0 \u00a0 precipitationType: json[\"precipitationType\"],  \u00a0 \u00a0 \u00a0 \u00a0 cloudCover: json[\"cloudCover\"].toDouble(),  \u00a0 \u00a0 \u00a0 );  \u00a0 Map&lt;String, dynamic> toJson() => {  \u00a0 \u00a0 \u00a0 \u00a0 \"windSpeed\": windSpeed,  \u00a0 \u00a0 \u00a0 \u00a0 \"windDirection\": windDirection,  \u00a0 \u00a0 \u00a0 \u00a0 \"temperature\": temperature,  \u00a0 \u00a0 \u00a0 \u00a0 \"temperatureApparent\": temperatureApparent,  \u00a0 \u00a0 \u00a0 \u00a0 \"weatherCode\": weatherCode,  \u00a0 \u00a0 \u00a0 \u00a0 \"humidity\": humidity,  \u00a0 \u00a0 \u00a0 \u00a0 \"visibility\": visibility,  \u00a0 \u00a0 \u00a0 \u00a0 \"dewPoint\": dewPoint,  \u00a0 \u00a0 \u00a0 \u00a0 \"precipitationType\": precipitationType,  \u00a0 \u00a0 \u00a0 \u00a0 \"cloudCover\": cloudCover,  \u00a0 \u00a0 \u00a0 };  }  class Warning {  \u00a0 Warning({  \u00a0 \u00a0 required this.code,  \u00a0 \u00a0 required this.type,  \u00a0 \u00a0 required this.message,  \u00a0 });  \u00a0 int code;  \u00a0 String type;  \u00a0 String message;  \u00a0 factory Warning.fromJson(Map&lt;String, dynamic> json) => Warning(  \u00a0 \u00a0 \u00a0 \u00a0 code: json[\"code\"],  \u00a0 \u00a0 \u00a0 \u00a0 type: json[\"type\"],  \u00a0 \u00a0 \u00a0 \u00a0 message: json[\"message\"],  \u00a0 \u00a0 \u00a0 );  \u00a0 Map&lt;String, dynamic> toJson() => {  \u00a0 \u00a0 \u00a0 \u00a0 \"code\": code,  \u00a0 \u00a0 \u00a0 \u00a0 \"type\": type,  \u00a0 \u00a0 \u00a0 \u00a0 \"message\": message,  \u00a0 \u00a0 \u00a0 };  }  <\/code><\/pre>\n<p>The weather model is based on the structure of the JSON data we are to receive from the endpoint.<\/p>\n<p>Step 4: Setting up the ApiClient<\/p>\n<p>After creating the weather model class, we&#8217;ll need to construct our ApiClient class, which will handle network calls to the API endpoint.<\/p>\n<p>The code snippet below shows how we get the weather data of a specific location using the Dio package. First, we initialize the Dio package with our baseUrl. Then, we specify our query parameters apikey, location, fields, units, timesteps, startTime, and endTime. \u00a0<\/p>\n<p>You can access the Tomorrow.io documentation to see a preview of the <a href=\"https:\/\/docs.tomorrow.io\/reference\/get-timelines\" rel=\"noopener noreferrer nofollow\">query parameters<\/a> accepted by the Tomorrow API, and what data you can get from the fields attribute.<\/p>\n<p>Next, we create a method called <code>getWeather<\/code>, inside which we call the GET method on the Dio object and pass in the required parameters. Then, we await the response. If successful, we parse the JSON data and return it; otherwise, we produce an error message.<\/p>\n<pre><code>import 'package:dio\/dio.dart';  import 'package:tomorrow_weather\/model\/weather.dart';  class ApiClient {  \u00a0 final Dio _dio = Dio(BaseOptions(  \u00a0 \u00a0 baseUrl: \"https:\/\/api.tomorrow.io\/v4\",  \u00a0 ));  \u00a0 \/\/replace with your ApiKEY  \u00a0 \/\/get your key from app.tomorrow.io\/development\/keys  \u00a0 static const String apiKey = 'YOUR_API_KEY';  \u00a0 \/\/pick a location to get the weather  \u00a0 static const location = [40.758, -73.9855]; \/\/New York  \u00a0 \/\/\/\/ list the fields you want to get from the api  \u00a0 static const fields = [  \u00a0 \u00a0 \"windSpeed\",  \u00a0 \u00a0 \"windDirection\",  \u00a0 \u00a0 \"temperature\",  \u00a0 \u00a0 \"temperatureApparent\",  \u00a0 \u00a0 \"weatherCode\",  \u00a0 \u00a0 \"humidity\",  \u00a0 \u00a0 \"visibility\",  \u00a0 \u00a0 \"dewPoint\",  \u00a0 \u00a0 \"cloudCover\",  \u00a0 \u00a0 \"precipitationType\"  \u00a0 ];  \u00a0 \/\/ choose the unit system, either metric or imperial  \u00a0 static const units = \"imperial\";  \u00a0 \/\/ set the timesteps, like \"current\" and \"1d\"  \u00a0 static const timesteps = [\"current\", \"1d\"];  \u00a0 \/\/ configure the time frame up to view the current and 4-days weather forecast  \u00a0 String startTime =  \u00a0 \u00a0 \u00a0 DateTime.now().toUtc().add(const Duration(minutes: 0)).toIso8601String();  \u00a0 String endTime =  \u00a0 \u00a0 \u00a0 DateTime.now().toUtc().add(const Duration(days: 4)).toIso8601String();  \u00a0 \/\/method to get the weather data  \u00a0 Future&lt;Weather> getWeather() async {  \u00a0 \u00a0 try {  \u00a0 \u00a0 \u00a0 final response = await _dio.get('\/timelines', queryParameters: {  \u00a0 \u00a0 \u00a0 \u00a0 'location': location.join(','),  \u00a0 \u00a0 \u00a0 \u00a0 'apikey': apiKey,  \u00a0 \u00a0 \u00a0 \u00a0 'fields': fields,  \u00a0 \u00a0 \u00a0 \u00a0 'units': units,  \u00a0 \u00a0 \u00a0 \u00a0 'timesteps': timesteps,  \u00a0 \u00a0 \u00a0 \u00a0 'startTime': startTime,  \u00a0 \u00a0 \u00a0 \u00a0 'endTime': endTime  \u00a0 \u00a0 \u00a0 });  \u00a0 \u00a0 \u00a0 \/\/parse the JSON data, returns the Weather data\u00a0  \u00a0 \u00a0 \u00a0 return Weather.fromJson(response.data);  \u00a0 \u00a0 } on DioError catch (e) {  \u00a0 \u00a0 \u00a0 \/\/returns the error if any  \u00a0 \u00a0 \u00a0 return e.response!.data;  \u00a0 \u00a0 }  \u00a0 }  }  <\/code><\/pre>\n<p>That concludes the ApiClient class. Next, we&#8217;ll build the UI for our weather application and consume the API data.<\/p>\n<p>Step 5: Consuming the API Data<\/p>\n<p>We have a single screen that displays the current and 4-day forecast weather statistics.\u00a0<\/p>\n<p>The code snippet below shows how we used the FutureBuilder weather widget to display the result of our getWeather method. If it contains data, it returns the weather data; else, it renders an error on the screen. If none of the aforementioned apply, we<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-400437","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/400437","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=400437"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/400437\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=400437"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=400437"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=400437"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}