How to Develop a Weather App with Flutter using Location Kit and OpenWeatherMap API?

Search This thread

shikkerimath

Senior Member
Nov 30, 2020
65
21
📖Introduction

In this post, I am going to show you how to develop a weather application with Flutter using HMS Location Kit and OpenWeatherMap API. It will be a very basic application with two screens, splash, and main page. We will retrieve the user’s location with Location Kit and get the weather based on it from OpenWeatherMap API. Finally, we will display temperature and some illustrations, icons according to weather information.

1615789977823.png

✨ What are the Location Kit and OpenWeatherMap API?

🌡️ OpenWeatherMap API is a simple and easy-to-use API with lots of basic and advanced features. It offers us both free and paid choices. Since we are developing a basic application, we will only ask for current weather data and it is free to use. We only need to register for it.

📍 Huawei Location Kit combines the GPS, Wi-Fi, and base station locations to help you quickly obtain precise user locations, build up global positioning capabilities, and reach a wide range of users around the globe. Currently, it provides the four main capabilities: fused location, location semantics, activity identification, and geofence.

🚧 Development Process

Let’s create a Flutter project and start integrating necessities. Since we will be using HMS we need to create a project on AGC and integrate HMS Core and Location Kit. You can use these guides to complete the integration of HMS Core. Plugin document and Developer Huawei document.

Also, to use OpenWeatherMap we should complete our sign-up process and have at least one API key. For to do that you can look at this documentation. OpenWeatherMap document

We are one step away from completing our pre-coding process. Since we will be using different packages we need to add them to our “pubspec.yaml” file. You can check out my YAML file. Versions may be different. I will be using the http package for HTTP requests, flutter_spinkit package for loading animations, huawei_location package for user’s location, logger package to log errors or messages, and flutter_svg package to display SVG files. You can find all these packages in the pub.dev. Also, we need to add our assets’ location to our YAML file. My images will under the “images” folder so I simply put them in my file as “images/”.

Code:
name: hms_weather
description: A weather app with HMS Location and OpenWeatherMap

version: 1.0.0+1

environment:
  sdk: ">=2.7.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.3
  http: ^0.12.2
  flutter_spinkit: ^4.1.2
  huawei_location: ^5.0.0+301
  logger: ^0.9.4
  flutter_svg: ^0.19.1

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:

  uses-material-design: true

  assets:
    - images/

After completing this, don’t forget to click “Pub get” and “Get dependencies”.

⌨️ Coding

Let’s start coding by creating our services. We basically need three services, network, position, and weather.

🔗 Networking

First, we will create our networking service. This service will handle our HTTP requests. We will call OpenWeatherMap API by this service class.

Code:
import 'package:http/http.dart' as http;
import 'package:logger/logger.dart';
import 'dart:convert';

class NetworkHelper {
  NetworkHelper({this.url});

  final String url;

  Future getData() async {
    http.Response response = await http.get(url);

    if (response.statusCode == 200) {
      String data = response.body;
      return jsonDecode(data);
    } else {
      var logger = Logger();
      logger.e('Response Code: ${response.statusCode}');
    }
  }
}

When API returns us a response with the response code 200, we will decode its body and return it. Otherwise, we will simply log it. Since this should be done asynchronously to prevent blocking the application. We will add async and await to our function also should return a future.

📍 Position

Like I mentioned in the beginning we will use the Huawei Location kit to get the user’s location. In our position service, we will check permissions first. If users gave their permission for us to get their location we will check if they have the last location. Otherwise, we will ask for their permission.

Code:
class Position {
  double latitude;
  double longitude;
  FusedLocationProviderClient locationProviderClient = FusedLocationProviderClient();
  PermissionHandler _permissionHandler = PermissionHandler();
  Location location;


  Future<void> getCurrentLocation() async {
    bool hasLocationPermission = await _permissionHandler.hasLocationPermission();
    if (hasLocationPermission != true) {
      _permissionHandler.requestLocationPermission();
    }


    try {
      location = await locationProviderClient.getLastLocation();
      if (location == null) {
        LocationRequest locationRequest = LocationRequest();
        // PRIORITY_LOW_POWER: 104 - Used to request the city-level location.
        locationRequest.priority = 104;
        try {
          int requestCode = await locationProviderClient.requestLocationUpdates(locationRequest);
          StreamSubscription<Location> streamSubscription =
              locationProviderClient.onLocationData.listen((event) {
            print(event);
          });
          location = await locationProviderClient.getLastLocation();
          longitude = location.longitude;
          latitude = location.latitude;
          await locationProviderClient.removeLocationUpdates(requestCode);
          streamSubscription.cancel();
        } catch (e) {
          print("Error: $e");
        }
      } else {
        location = await locationProviderClient.getLastLocation();
        longitude = location.longitude;
        latitude = location.latitude;
      }
    } catch (e) {
      var logger = Logger();
      logger.e(e.toString());
    }
  }
}

If the user has no last location we will simply listen for the current location update and try to get it. This process is also should be done asynchronously to prevent blocking the application. We will add async and await to our function.

☀️ Weather

For our last service, we will create a class to get information from OpenWeatherMap API using position and networking services.

Code:
const apiKey = 'YOUR_API_KEY';
const openWeatherMapURL = 'https://api.openweathermap.org/data/2.5/weather';


Future<dynamic> getLocationWeather() async {
  Position position = Position();
  await position.getCurrentLocation();


  NetworkHelper networkHelper = NetworkHelper(
      url:
          '$openWeatherMapURL?lat=${position.latitude}&lon=${position.longitude}&appid=$apiKey&units=metric');
  var weatherData = await networkHelper.getData();
  return weatherData;
}

After getting our weather data we will change our background colors, images, and city name according to it on our main page. We will check the condition value of weather data for changes. My control functions are elementary, but you can try to improve it yourself for a better codebase.

Code:
String getWeatherSVGNetwork(int condition) {
  if (condition < 300) return 'https://www.flaticon.com/svg/static/icons/svg/3026/3026385.svg';
  else if (condition < 400) return 'https://www.flaticon.com/svg/static/icons/svg/899/899693.svg';
  else if (condition < 600) return 'https://www.flaticon.com/svg/static/icons/svg/2921/2921803.svg';
  else if (condition < 700) return 'https://www.flaticon.com/svg/static/icons/svg/2834/2834554.svg';
  else if (condition < 800) return 'https://www.flaticon.com/svg/static/icons/svg/2446/2446001.svg';
  else if (condition == 800) return 'https://www.flaticon.com/svg/static/icons/svg/146/146199.svg';
  else if (condition <= 804) return 'https://www.flaticon.com/svg/static/icons/svg/899/899681.svg';
  else return 'https://www.flaticon.com/svg/static/icons/svg/2471/2471920.svg';
}


SvgPicture getBackground(int condition, int temp) {
  if (condition < 300) return SvgPicture.asset('images/storm_bg.svg');
  else if (condition < 400) return SvgPicture.asset('images/storm_bg.svg');
  else if (condition < 600) return SvgPicture.asset('images/umbrella_bg.svg');
  else if (condition < 700) return SvgPicture.asset('images/snowman_bg.svg');
  else if (condition < 800) return SvgPicture.asset('images/cloudy_bg.svg');
  else if (condition == 800 && temp >= 20) return SvgPicture.asset('images/very_hot_bg.svg');
  else if (condition == 800 && temp < 20) return SvgPicture.asset('images/sunny_bg.svg');
  else if (condition <= 804) return SvgPicture.asset('images/cloudy_bg.svg');
  else return SvgPicture.asset('images/sunny_bg.svg');
}


List<Color> getBackgroundColor(int condition) {
  if (condition < 300) return [Colors.pink.shade600, Colors.orange.shade400];
  else if (condition < 400) return [Colors.green.shade300, Colors.purple.shade400];
  else if (condition < 600) return [Colors.green.shade300, Colors.purple.shade400];
  else if (condition < 700) return [Colors.red.shade400, Colors.tealAccent.shade400];
  else if (condition < 800) return [Colors.green.shade300, Colors.purple.shade400];
  else if (condition == 800) return [Colors.blue.shade600, Colors.yellow.shade400];
  else if (condition <= 804) return [Colors.green.shade300, Colors.purple.shade400];
  else return [Colors.green.shade300, Colors.purple.shade400];
}

We will call these functions from our main page. Since our services are ready we can start our splash screen.

🚪 Splash Screen

In our splash, we will display and weather icon and a loading spinner. Also, we are going to call our getLocationWeather() method to get our weather data. When we retrieve our data we will navigate to our main page.

Code:
class _LoadingScreenState extends State<LoadingScreen> {
  @override
  void initState() {
    super.initState();
    getLocationData();
  }


  void getLocationData() async {
    var weatherData = await WeatherModel().getLocationWeather();


    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) {
          return HomeScreen( locationWeather: weatherData, );
        },
      ),
    );
  }
}

1615790344096.png


🏠 Main Page

When we are on the main page, we should check our weather data. If it is null we should say something like “There is something wrong.”. Otherwise, we should change the background, weather icon, and fill city name and degree.

Code:
WeatherModel weather = WeatherModel();
int temperature;
String cityName;
String weatherMessage;
double newHeight;
String weatherSVG = '';
SvgPicture svgBG;
List<Color> backgroundColors = [Colors.white, Colors.blueGrey];

void updateUI(dynamic weatherData) {
  setState(() {
    if (weatherData == null) {
      temperature = 0;
      weatherSVG = 'https://www.flaticon.com/svg/static/icons/svg/2471/2471920.svg';
      cityName = 'There is an error.';
      svgBG = SvgPicture.asset('images/404-error.svg');
      return;
    }
    var temp = weatherData['main']['temp'];
    temperature = temp.toInt();
    weatherMessage = weather.getMessage(temperature);
    var condition = weatherData['weather'][0]['id'];
    weatherSVG = weather.getWeatherSVGNetwork(condition);
    svgBG = weather.getBackground(condition, temperature);
    backgroundColors = weather.getBackgroundColor(condition);
    cityName = weatherData['name'];
  });
}

1615790372599.png



After we set our weather data to our UI, we should have something like this. Now we only add refresh function for the near me icon. When clicked it should check the weather from OpenWeatherMap API with our location.

Code:
Expanded(
  child: Container(
    alignment: Alignment.centerLeft,
    child: FlatButton(
      onPressed: () async {
        var weatherData = await weather.getLocationWeather();
        updateUI(weatherData);
      },
      child: Icon(
        Icons.near_me,
        size: 50.0,
      ),
    ),
  ),
),

And we completed our development process. We have a simple weather application that can get our location and shows that location’s weather.

⚠️ Tips & Tricks
  • Don't forget to check your dependencies added correctly.
  • Don’t forget to click “Pub get” and “Get dependencies”.
  • Don't forget to add agconnect-service.json file to your android/app directory.
  • Don't forget to add your images folder to your “pubspec.yaml” if you use one.

💡 Conclusion

As you can see Huawei provides us solutions to help our development even in Flutter. The location kit is a really easy to use and very useful tool. Also, OpenWeatherMap API is a good option for developers as you can see. This was a small step for humankind but at least everyone will know should they get an umbrella or not when they are going out 🌂. You can find this project's GitHub repository in references. I hope it will help you to understand the UI side as well.

👇 References


Github Repo

Pub.Dev

Huawei Document

AppBrewery Course

Checkout in forum
 
Our Apps
Get our official app!
The best way to access XDA on your phone
Nav Gestures
Add swipe gestures to any Android
One Handed Mode
Eases uses one hand with your phone