WeatherDrobe's purpose is to help in everyday situations in which we often don't have time to dress properly or check upcoming weather news. Did you ever have problem with time missing? This application has been created to sort it out. The Main Algorithm of WeatherDrobe will prepare for you few proposals of dressing, based on actual weather informations, whereby you will never again forget to take an umbrella ;)
All screenshots presented below do not represent the final state of the product. Appearance may change in the future.
On the homepage, the user will be able to receive information such as suggested clothing, the current weather forecast, and by pressing the top banner, the hourly weather forecast. In addition, in the left column there are suggestions such as umbrella, wristwatch, etc. Do you like the look of this app? Check Graphic Attribution!
In the virtual wardrobe, users can browse their own collection, which is automatically downloaded from the Firebase server (Check Data Fetching). When you click on a piece of clothing, informations about it appears on the screen. In the future we are also planning a function of editing and deleting the created clothes.
The following images represent the appearance of the Outfit Creator. The user can easily create new clothing by setting four attributes - template, color, temperature and additional weather conditions. After pressing the 'Save' button, the information of the newly created outfit is sent to the Firebase server. Check FireBase Data Sending for more detailed informations.
WeatherDrobe's weather informations are based on Open Weather Map API which allows to use a powerfull One Call API option with the following feedback data:
- Current weather
- Minute forecast for 1 hour
- Hourly forecast for 48 hours
- Daily forecast for 7 days
- National weather alerts
- Historical weather data for the previous 5 days
- Current and forecast weat
Currently WeatherDrobe is using following data:
CurrentData({this.iconId, this.temperature, this.description, this.time});
factory CurrentData.fromJson(dynamic json) {
return CurrentData(
iconId: json["weather"][0]["icon"],
temperature: json["temp"].toDouble(),
description: json["weather"][0]["description"],
time: json["dt"]);
}
HourlyForecast({this.iconId,this.temperature,this.description,this.propabilityOfPrecipitation,this.time});
factory HourlyForecast.fromJson(Map<String, dynamic> json) {
return HourlyForecast(
iconId: json["weather"][0]["icon"],
temperature: json["temp"].toDouble(),
description: json["weather"][0]["description"],
propabilityOfPrecipitation: json["pop"].toDouble(),
time: json["dt"]);
}
WeatherDrobe is using Firebase Could Firestore databese to allows users multi-device experience. All garments created on one device will be available in another ones. (Provided that user will be on the same account)
On the first run of application User is asked to create an account or Sign-in to an existing one. The following code is responsible for all authentication operations with Firebase server:
//Logging in
Future<void> signIn(String email, String password) async {
try {
userCredential = await _auth.signInWithEmailAndPassword(
email: email, password: password);
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
print('No user found for that email.');
} else if (e.code == 'wrong-password') {
print('Wrong password provided for that user.');
}
}
}
Checking changes in authentication states during the application run (Depending on status state, relevant data will be showed on User's screen):
Future<void> _onAuthStateChanged(User firebaseUser) async {
if (firebaseUser == null) {
status = Status.Unauthenticated;
} else {
_user = firebaseUser;
status = Status.Authenticated;
}
notifyListeners();
}
//Registration
Future<void> register(String email, String password) async {
try {
userCredential = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {
print('The password provided is too weak.');
} else if (e.code == 'email-already-in-use') {
print('The account already exists for that email.');
}
} catch (e) {
print(e);
}
}
//Logging out
Future<void> signOut() async {
await _auth.signOut();
}
}
In order to maintain clarity and speed of movement through the database, data should be entered in an appropriate manner.
When the User000 creates a garment with the following characteristics:
- Red hat
- Best for 26°C — 29°C
- Suitable for strong sun
The data would be stored in the following way:
Since it would be highly inefficient to store two variables corresponding to minimum and maximum temperature, the following assignment of ranges to corresponding words and numbers was used in the process of storing the temperature data:
0: -∞ — -10° = Freezing
1: -9° — 0° = Cold
2: 1° — 4° = Chilly
3: 5° — 8° = Brisk
4: 9° — 13° = Cool
5: 14° — 18° = Mild
6: 19° — 25° = Perfect
7: 26° — 29° = Warm
8: 30° — 33° = Hot
9: 34° — ∞° = Scorching
The above assignment of temperature to appropriate terms was borrowed from u/_eurostep's post on Reddit (Last access 21.02.2020).
In WeatherDrobe, the user has the ability to add new garments to their Virtual Wardrobe, based on which the algorithm will create a clothing composition.
Clothes data to be transmitted should include following informations:
- The temperature range in which the garment performs best.
- Name of the garment template.
- The color of the garment.
- Additional weather conditions the garment can handle.
- What part of the body this garment is designed for.
The code responsible for sending data to Firestore server:
Future<void> saveCloth(FirebaseAuth auth) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
ClothType clothType = ClothType.values.elementAt(type);
//users.add({'userID': userID});
users
.doc(auth.currentUser.uid)
.set({'email': auth.currentUser.email, 'userID': auth.currentUser.uid});
users.doc(auth.currentUser.uid).collection(clothTypeName).add({
'temperature': temperatureRating,
'snow': snow,
'rain': rain,
'sun': sun,
'wind': wind,
'dir': dir,
'color': color
});
}
On the server, the data is stored in four separated collections that correspond to head, upper body, lower body, and foot garment types. All of these collections are children of the parent document that is assigned to our account. Unfortunately, Firebase does not allow us to access the sub-collections from within the document (DocumentSnapshot), so we had to perform the operation four times while retrieving the data.
void getGarments(FirebaseAuth auth) async {
List<QueryDocumentSnapshot> temp = [];
await users
.doc(auth.currentUser.uid)
.collection('head')
.get()
.then((QuerySnapshot querySnapshot) => {
if (querySnapshot.size > 0)
{
querySnapshot.docs.forEach((doc) {
temp.add(doc);
})
}
});
headwear = temp;
temp = [];
await users
.doc(auth.currentUser.uid)
.collection('top')
.get()
.then((QuerySnapshot querySnapshot) => {
if (querySnapshot.size > 0)
{
querySnapshot.docs.forEach((doc) {
temp.add(doc);
})
}
});
top = temp;
temp = [];
await users
.doc(auth.currentUser.uid)
.collection('legs')
.get()
.then((QuerySnapshot querySnapshot) => {
if (querySnapshot.size > 0)
{
querySnapshot.docs.forEach((doc) {
temp.add(doc);
})
}
});
legs = temp;
temp = [];
await users
.doc(auth.currentUser.uid)
.collection('feet')
.get()
.then((QuerySnapshot querySnapshot) => {
if (querySnapshot.size > 0)
{
querySnapshot.docs.forEach((doc) {
temp.add(doc);
})
}
});
feet = temp;
temp = [];
userCollections = await users.doc(auth.currentUser.uid).get();
notifyListeners();
}
In the app, the most important factor is the algorithm that suggests clothes based on weather conditions. Below is a simplified flowchart of the clothing suggestion and display. In the diagram, the current temperature represents the median temperature for the next X hours (for now it's 10 hours).
When creating a new Clothing object, the program checks what weather conditions will occur in the next X hours. To do this, the weather ID that OpeWeatherMap passes to us via its API is checked. The weather ID is a three-digit code that describes the exact weather phenomenon. There are seven main groups of weather conditions, which are subdivided into minor, more accurate weather descriptions. Below We present the main weather groups Full description of Weather Groups
Weather Group | Description |
---|---|
2XX | Thunderstorm |
3XX | Drizzle |
5XX | Rain |
6XX | Snow |
7XX | Atmosphere |
800 | Clear Sky |
80X | Clouds |
Weatherdrobe details four types of weather phenomena: Rain, Snow, Strong Wind and Strong Sun, so the application analyses the received data from the OpenWeatherMap server as follows:
Weather Condition | ID |
---|---|
Rain | [200 - 531] |
Snow | [600 - 622] |
Wind | 771, 781 |
Sun | 800 |
When creating a model, the algorithm will prioritise those garments that had compatibility with a given weather condition ticked during creation. In this way, on a rainy day, the algorithm will suggest a jacket first and clothes that are only suitable because of the temperature will appear in the next models.
In the application, apart from using the icons available as standard in flutter, and the usual emoji - I also used icons made available on the internet for free use. Below I present all the information about the authors of the graphics I used for this project.
- Umbrella Icon used in App Logo
- Clothing templates made by 'Freepik' from Flaticon
- Face Detection Icon used in Character Model made by 'Freepik' from Flaticon
- Model Graphic used as Character model, made by xiayamoon from Vecteezy
- Scarf Icon made by 'Freepik' from Flaticon
- Gloves Icon made by 'dreamicons' from Flaticon
- Umbrella Icon made by 'dreamicons' from Flaticon
- Mannequin Graphic made by 'macrovector' from Freepik
- Wardrobe Background Image made by 'macrovector' from Freepik