A Personal Weather Dashboard built with Flutter & Provider
Real-time weather data Β· Favorites management Β· Celsius / Fahrenheit toggle
- Features
- Architecture & Approach
- Folder Structure
- Prerequisites
- Getting the API Key
- Clone & Run
- State Management β Provider
- Key Design Decisions
- Dependencies
- Author
| Feature | Description |
|---|---|
| ποΈ Major Cities | Horizontally scrollable cards for 8 pre-defined global cities |
| β€οΈ Favourites | Add / remove cities; persisted in app state via Provider |
| π Search | Search any city worldwide with loading, error, and success states |
| π‘οΈ Unit Toggle | Switch between Celsius and Fahrenheit globally from Settings |
| π Pull-to-Refresh | Swipe down on Home to re-fetch all city data |
| π± Material 3 UI | Clean gradient AppBar, cards, bottom nav, and consistent theming |
The project follows a feature-first folder layout with a clean separation of concerns:
Presentation (UI) βββ Provider (State) βββ Service (Network) βββ OpenWeatherMap API
- Model (
weather_model.dart) β pure Dart; parses JSON from OpenWeatherMap. - Service (
weather_service.dart) β responsible only for HTTP calls. - Provider (
weather_provider.dart) β holds all app state, exposes methods, callsnotifyListeners(). - Screens / Widgets β consume Provider via
Consumer<WeatherProvider>; zero business logic lives here.
Provider is the officially recommended lightweight state management solution for Flutter. It uses InheritedWidget under the hood, which means:
- No boilerplate (unlike BLoC/Redux).
- Widget rebuilds are surgical β only
Consumersubtrees re-render. - Easy to unit-test (just inject a mock provider).
lib/
βββ main.dart # App entry-point, ChangeNotifierProvider setup
β
βββ core/
β βββ constants/
β β βββ api_openweathermap.dart # Base URL & key reference
β β βββ secrets.dart # β οΈ GITIGNORED β your real API key goes here
β β βββ secrets.dart.example # Template β copy β secrets.dart
β βββ utils/ # (helper utilities, extendable)
β
βββ features/
βββ weather/
βββ data/
β βββ weather_service.dart # HTTP layer (http package)
β βββ weather_provider.dart # ChangeNotifier β all app state
β
βββ domain/
β βββ weather_model.dart # Weather data model + fromJson factory
β
βββ ui/
βββ screens/
β βββ home_screen.dart # Major cities + favourites
β βββ search_screen.dart # City search + result display
β βββ settings_screen.dart # Unit toggle (Β°C / Β°F)
βββ widgets/
βββ city_weather_card.dart # Horizontal city card
βββ favorite_city_tile.dart # Favourite list tile
| Tool | Minimum Version | Check command |
|---|---|---|
| Flutter SDK | 3.10.0 | flutter --version |
| Dart SDK | 3.0.0 | dart --version |
| Android Studio / VS Code | Latest | β |
| Android emulator or physical device | API 21+ | flutter devices |
β Install Flutter: https://docs.flutter.dev/get-started/install
WeatherWise uses the free tier of OpenWeatherMap.
- Sign up at https://openweathermap.org/ β click Sign In β Create an Account.
- Confirm your email address by clicking the link in the verification email.
- Log in and go to your dashboard.
- Click your username (top-right) β My API keys.
- Copy the Default key that is already generated, or click Generate to create a new named key.
β οΈ New keys can take up to 2 hours to activate.
The API will return401 Invalid API keyuntil then β this is completely normal.
# 1. Copy the example template
cp lib/core/constants/secrets.dart.example lib/core/constants/secrets.dart
# 2. Open secrets.dart and replace the placeholder:
# static const String openWeatherMapApiKey = 'YOUR_API_KEY_HERE';
# with your real key, e.g.:
# static const String openWeatherMapApiKey = 'abc123def456...';
secrets.dartis listed in.gitignoreβ it will never be committed accidentally.
# 1. Clone the repository
git clone https://github.com/<your-username>/WeatherWise-Flutter.git
cd WeatherWise-Flutter
# 2. Set up your API key (see section above)
cp lib/core/constants/secrets.dart.example lib/core/constants/secrets.dart
# β Edit secrets.dart and paste your OpenWeatherMap key
# 3. Install Flutter dependencies
flutter pub get
# 4. Verify no issues
flutter analyze
# 5. Run the app (debug mode)
flutter run
# ββ Optional commands ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# List connected devices
flutter devices
# Run on a specific device
flutter run -d <device_id>
# Build a release APK
flutter build apk --release
# Build a release App Bundle (for Play Store)
flutter build appbundle --release// main.dart
ChangeNotifierProvider(
create: (_) => WeatherProvider(),
child: MaterialApp( ... ),
)Consumer<WeatherProvider>(
builder: (context, provider, child) {
return Text(provider.formatTemperature(weather.temperature));
},
)final provider = Provider.of<WeatherProvider>(context, listen: false);
provider.fetchWeather('London');| Field | Type | Purpose |
|---|---|---|
_cityWeatherData |
Map<String, Weather> |
Cache of fetched weather keyed by lowercase city name |
_favoriteCities |
List<String> |
User-selected favourite city names |
_isCelsius |
bool |
Current unit preference (true = Β°C) |
_isLoading |
bool |
Drives CircularProgressIndicator in UI |
_errorMessage |
String? |
Displayed as red error text in UI |
_searchedWeather |
Weather? |
Latest Search screen result |
| Method | Description |
|---|---|
fetchWeather(city) |
Calls API, caches result, updates search result, notifies UI |
fetchAllPredefinedCities() |
Bulk-fetches all Home screen cities on first load |
refreshAllWeather() |
Re-fetches all cached cities (used after unit toggle) |
toggleUnit(isCelsius) |
Switches Β°C/Β°F and triggers refreshAllWeather() |
addToFavorites(city) |
Appends city to favourites list |
removeFromFavorites(city) |
Removes city from favourites list |
isFavorite(city) |
Returns bool β used to disable "Add to Favourites" button |
getFavoriteWeathers() |
Returns List<Weather> for all favourite cities |
formatTemperature(temp) |
Returns e.g. "25Β°C" or "77Β°F" based on current setting |
clearSearch() |
Resets search result (called when Search screen opens) |
API keys are never hard-coded in source. They live in secrets.dart (excluded by .gitignore). A secrets.dart.example serves as a safe, committed template for all contributors.
All cache look-ups use cityName.toLowerCase(), so "london" and "London" always resolve to the same cached entry.
When the user toggles Β°C β Β°F, refreshAllWeather() is invoked automatically. OpenWeatherMap returns values already in the chosen unit (metric / imperial), so no client-side conversion math is required.
| Scenario | Behaviour |
|---|---|
| City not found (404) | User-friendly "City not found" message |
| Network failure | "Check your internet connection" message |
| Empty search | SnackBar warning, no API call made |
| Icon load failure | Icons.cloud fallback widget |
RefreshIndicator wraps the Home ListView so users can swipe-down to reload all city data at any time.
- Home β Search and Home β Settings use
Navigator.push(MaterialPageRoute(...)). - The back arrow and device back button automatically call
Navigator.pop(), returning to Home. - Provider state persists across navigation because the
ChangeNotifierProvidersits aboveMaterialApp.
| Package | Version | Purpose |
|---|---|---|
| flutter | SDK | UI framework |
| provider | ^6.1.2 | State management (ChangeNotifier) |
| http | ^1.2.0 | HTTP GET requests to OpenWeatherMap |
| cupertino_icons | ^1.0.8 | iOS-style icon assets |
Muhammad Hunain
Roll Number: 22K-5037
BS Software Engineering
Assignment 1 β Smart Mobile Development
WeatherWise β Personal Weather Dashboard
This project is open-source and available under the MIT License.
Made with β€οΈ and Flutter