Leveraging Flutter Starter to build a Weather App
Tags: opensource, flutter, dart
Published: 2021-01-06 07:47:54

Hi there! Welcome to the third tutorial for building production-level apps with Flutter Starter. [Flutter](https://flutter.dev) has been extremely popular with the dev community since its launch but you might have some trouble building production-level apps. 

Flutter gives you a simple counter app upon installation which works if you are just starting out. However, in order to build a production-level app, you need State Management, API calls, Styleguide and common widgets. This code is frequently repeated in every project. Our idea was to write all the code and create the necessary files for all your projects. No more rewrites!

Check out our solution- **[Flutter Starter](https://flutter-starter.github.io/?utm_source=Flutter%20Starter&utm_medium=dev&utm_campaign=3rd%20Tutorial%20Article):**

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/d55mka5g3l70tlgdtp45.gif)

To learn more about this example, visit the [Weather App](https://github.com/GeekyAnts/flutter-starter/tree/master/examples/weather-app?utm_source=Flutter%20Starter&utm_medium=dev&utm_campaign=3rd%20Tutorial%20Article) page.

You can find the API URL in `api-constants.dart`:

```dart
Map<String, String> apiConstants = {
  "openweather": "https://api.openweathermap.org/data/2.5",
  "auth": "https://reqres.in/api"
};

```

We will use an open source [Weather API](https://openweathermap.org/current).

In *main.dart*, we need a function *getWeatherforCity.* This will take the city name and fetch the weather details for that city.

This is a *get* method so we will call *get* from restapihandler as shown below: 

```dart
static getWeatherforCity(String city) async {
    final response = await RestApiHandlerData.getData(
        '${apiConstants["openweather"]}/weather?q=$city&appid=$appId');
    return response;
  }
```

The API SDK is now a black box from which we will just call the method *getWeatherforCity* and it will return a JSON response.

Now that we have the data, it needs to be called, parsed and stored. Proceed to the *shared* folder which contains *blocs*, *models* and *resources*.

**Resources:**

The following code snippet in *weather_* shows how API SDK functions will be called:

```dart
class WeatherApiRepository {
  Future fetchWeather(String city) async {
    final response = await ApiSdk.getWeatherforCity(city);
    return response;
  }
}
```

The *weather_model.dart* file in the *models* folder will store the data from the response:

```dart
class WeatherModel {
  dynamic lon;
  dynamic lat;
  String mainWeather;
  String description;
  String temp;
  String minTemp;
  String maxTemp;
  dynamic windSpeed;
  String countryCode;
  String cityName;
  String icon;

  WeatherModel.fromJson(Map<String, dynamic> parsedJson)
      : lon = parsedJson['coord']['lon'],
        lat = parsedJson['coord']['lat'],
        mainWeather = parsedJson['weather'][0]['main'],
        description = parsedJson['weather'][0]['description'],
				// Converted temperature to celcius, you can do this in **a*pp*** also.
        temp = (parsedJson['main']['temp'] - 273.15).toStringAsFixed(2),
        minTemp = (parsedJson['main']['temp_min'] - 273.15).toStringAsFixed(2),
        maxTemp = (parsedJson['main']['temp_max'] - 273.15).toStringAsFixed(2),
        windSpeed = parsedJson['wind']['speed'],
        countryCode = parsedJson['sys']['country'],
        cityName = parsedJson['name'],
        icon = parsedJson["weather"][0]["icon"];
}
```

In *weather_bloc.dart* file, we have used the resources to access the data and catch any errors:

```dart
final response = await weatherApiRepository.fetchWeather(event.city);
        if (response["cod"] != 200) {
          yield FailureState(
            message: response['message'],
            cod: response['cod'],
          );
        } else {
          WeatherModel weatherModel = WeatherModel.fromJson(response);
          yield SearchSuccessState(weatherModel: weatherModel);
        }
      } catch (e) {
        FailureState(message: e);
      }
```

In the main app, the WeatherPage widget needs to be wrapped with the Blocprovider widget:

```dart
body: BlocProvider(
	create: (context) => WeatherBloc(), 
	child: WeatherPage(),
),
```

Finally, use BlocBuilder in *weather_page.dart* to build the required widget according to the states:

```dart
BlocBuilder<WeatherBloc, WeatherStates>(builder: (context, state) {
            if (state is SearchSuccessState)
              return BuildWeather(
                weatherModel: state.weatherModel,
              );
            else if (state is InitialSuccessState)
              return Container();
            else if (state is LoadingState)
              return _buildLoadingScreen();
            else if (state is FailureState)
              return _buildErrorPage(state.message, state.cod);
            else
              return Container();
          }),
```

**Your app should look like this:**

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5ak97dnffrna1r1ng6ie.gif)

Congrats! You just successfully built a production-ready Weather App. Interested in more? Take a look at our guide to building a [Hacker News app](https://techblog.geekyants.com/flutter-starter-guide-to-building-a-hacker-news-app?utm_source=Flutter%20Starter&utm_medium=dev&utm_campaign=3rd%20Tutorial%20Article). If you would like to learn more about Flutter Starter, please visit our [official documentation](https://flutter-starter.github.io/?utm_source=Flutter%20Starter&utm_medium=dev&utm_campaign=3rd%20Tutorial%20Article).

This article was written by [Sumant Raj](https://twitter.com/sumantraj2611?lang=en) and [Madhav B](https://twitter.com/madhavb23). Edited by Kavya V.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qu308uugr0y9262s5zxr.png)