Building a Vibrant Weather App with Flutter and Integrating Native Splash Screen


Introduction

In this tutorial, we will build a weather app using Flutter. The app will feature a vibrant user interface, a native splash screen, and the ability to fetch weather data based on the user's location or a searched city name.


Project Setup

1. Create a New Flutter Project

First, create a new Flutter project:

flutter create weather_app cd weather_app

2. Add Dependencies

Update your pubspec.yaml file to include the necessary dependencies:

dependencies: flutter: sdk: flutter animations: ^2.0.5 flutter_blurhash: ^0.7.0 flutter_svg: ^2.0.10 geolocator: ^8.0.0 http: ^0.13.4 dev_dependencies: flutter_native_splash: ^2.0.5

Run the following command to install the packages:

flutter pub get


3. Configure Flutter Native Splash

Add the following configuration to your pubspec.yaml file for the native splash screen:

flutter_native_splash: color: "#42a5f5" image: assets/splash.png android: true ios: true web: false android_gravity: fill ios_content_mode: scaleAspectFill


4. Add Splash Screen Image

Place your splash screen image in the assets directory. Ensure your pubspec.yaml includes the assets section:

flutter: assets: - assets/splash.png

Run the following command to generate the splash screen configuration:

flutter pub run flutter_native_splash:create


Building the Weather App

1. Weather Service

Create a weather_service.dart file in a services directory to fetch weather data from OpenWeatherMap:


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

class WeatherService {
  static const String apiKey = '35e48f639d31fb3f53ac30e814bb6bb5';
  static const String apiUrl =
      'https://api.openweathermap.org/data/2.5/weather';

  Future<Map<String, dynamic>> getWeather(double lat, double lon) async {
    final response = await http.get(
      Uri.parse(
          "https://api.openweathermap.org/data/2.5/weather?lat=$lat&lon=$lon&units=metric&appid=$apiKey"),
    );

    if (response.statusCode == 200) {
      return json.decode(response.body);
    } else {
      throw Exception('Failed to load weather data');
    }
  }

  Future<Map<String, dynamic>> getWeatherByCity(String cityName) async {
    final response = await http
        .get(Uri.parse('$apiUrl?q=$cityName&units=metric&appid=$apiKey'));

    if (response.statusCode == 200) {
      return json.decode(response.body);
    } else {
      throw Exception('Failed to load weather data');
    }
  }
}


2. Home Screen

Create home_screen.dart in the screens directory and update it as follows:


import 'package:animations/animations.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blurhash/flutter_blurhash.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:geolocator/geolocator.dart';
import '../Services/weather_service.dart';

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  WeatherService _weatherService = WeatherService();
  String _temperature = '';
  String _description = '';
  String _icon = '';
  bool _loading = true;
  final TextEditingController _searchController = TextEditingController();

  @override
  void initState() {
    super.initState();
    _fetchWeather();
  }

  Future<void> _fetchWeather({String? cityName}) async {
    try {
      var weatherData;
      if (cityName != null && cityName.isNotEmpty) {
        weatherData = await _weatherService.getWeatherByCity(cityName);
      } else {
        Position position = await _getCurrentLocation();
        weatherData = await _weatherService.getWeather(
            position.latitude, position.longitude);
      }
      setState(() {
        _temperature = '${weatherData['main']['temp']}°C';
        _description = weatherData['weather'][0]['description'];
        _icon = getWeatherIcon(weatherData['weather'][0]['main']);
        _loading = false;
      });
    } catch (e) {
      print('Error fetching weather: $e');
      setState(() {
        _loading = false;
      });
    }
  }

  Future<Position> _getCurrentLocation() async {
    bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      return Future.error('Location services are disabled.');
    }

    LocationPermission permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        return Future.error('Location permissions are denied');
      }
    }

    if (permission == LocationPermission.deniedForever) {
      return Future.error(
          'Location permissions are permanently denied, we cannot request permissions.');
    }

    return await Geolocator.getCurrentPosition();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Weather App'),
        actions: [
          IconButton(
            icon: const Icon(Icons.search),
            onPressed: () {
              showSearchDialog(context);
            },
          ),
        ],
      ),
      body: _loading
          ? const Center(child: CircularProgressIndicator())
          : PageTransitionSwitcher(
              duration: const Duration(milliseconds: 300),
              transitionBuilder: (child, animation, secondaryAnimation) =>
                  SharedAxisTransition(
                child: child,
                animation: animation,
                secondaryAnimation: secondaryAnimation,
                transitionType: SharedAxisTransitionType.horizontal,
              ),
              child: Column(
                key: const ValueKey<int>(0),
                children: [
                  Expanded(
                    child: Stack(
                      children: [
                        const BlurHash(
                          hash: 'LEHV6nWB2yk8pyo0adR*.7kCMdnj',
                          imageFit: BoxFit.cover,
                        ),
                        Center(
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              SvgPicture.asset(
                                _icon,
                                height: 150,
                              ),
                              const SizedBox(height: 20),
                              Text(
                                _temperature,
                                style: const TextStyle(
                                  fontSize: 50,
                                  fontWeight: FontWeight.bold,
                                  color: Colors.blue,
                                ),
                              ),
                              Text(
                                _description,
                                style: const TextStyle(
                                  fontSize: 30,
                                  color: Colors.amber,
                                ),
                              ),
                            ],
                          ),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
    );
  }

  void showSearchDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: const Text('Search City'),
          content: TextField(
            controller: _searchController,
            decoration: const InputDecoration(hintText: 'Enter city name'),
          ),
          actions: [
            TextButton(
              child: const Text('Cancel'),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
            TextButton(
              child: const Text('Search'),
              onPressed: () {
                Navigator.of(context).pop();
                _fetchWeather(cityName: _searchController.text);
              },
            ),
          ],
        );
      },
    );
  }
}

String getWeatherIcon(String condition) {
  switch (condition) {
    case 'Clear':
      return 'lib/assets/sunny-day.svg';
    case 'Clouds':
      return 'lib/assets/cloud-view.svg';
    case 'Rain':
      return 'lib/assets/icon-rain.svg';
    case 'Drizzle':
      return 'lib/assets/rainbow.svg';
    case 'Thunderstorm':
      return 'lib/assets/reshot-icon-weather-YRQ52EZUTW.svg';
    case 'Snow':
      return 'lib/assets/snowflake.svg';
    case 'Mist':
    case 'Smoke':
    case 'Haze':
    case 'Dust':
    case 'Fog':
    case 'Sand':
    case 'Ash':
    case 'Squall':
    case 'Tornado':
      return 'lib/assets/sunny-and-windy.svg';
    default:
      return 'lib/assets/weather.svg';
  }
}



3. Ensure Assets are Included in pubspec.yaml

Ensure all your SVG files are included in the pubspec.yaml file:

flutter: assets: - lib/assets/cloud-view.svg - lib/assets/hot-weather.svg - lib/assets/icon-rain.svg - lib/assets/rainbow.svg - lib/assets/reshot-icon-weather-YRQ52EZUTW.svg - lib/assets/snowflake.svg - lib/assets/sunny-and-windy.svg - lib/assets/sunny-day.svg - lib/assets/sunset.svg - lib/assets/weather.svg

4. Main Dart File


Ensure your main.dart file initializes the app correctly:


import 'package:flutter/material.dart';





Link to Repository:WeatherApp




Comments

Popular posts from this blog

Building a Vibrant ToDo App with Flutter

Implemtation of a CSV File

Project Introduction