feat: Add settings provider for theme and time range management

- Implemented SettingsProvider to manage user preferences for theme options and time ranges for reminders.
- Added persistent reminder settings with configurable retry intervals and maximum attempts.
- Created UI for settings screen to allow users to customize their preferences.
- Integrated shared_preferences for persistent storage of user settings.

feat: Introduce Ingredient model

- Created Ingredient model to represent nutritional components with properties for id, name, amount, and unit.
- Added methods for serialization and deserialization of Ingredient objects.

feat: Develop Archived Supplements Screen

- Implemented ArchivedSupplementsScreen to display archived supplements with options to unarchive or delete.
- Added UI components for listing archived supplements and handling user interactions.

chore: Update dependencies in pubspec.yaml and pubspec.lock

- Updated shared_preferences dependency to the latest version.
- Removed flutter_datetime_picker_plus dependency and added file dependency.
- Updated Flutter SDK constraint to >=3.27.0.
This commit is contained in:
2025-08-26 17:19:54 +02:00
parent e6181add08
commit 2aec59ec35
18 changed files with 3756 additions and 376 deletions

View File

@@ -0,0 +1,64 @@
class Ingredient {
final int? id;
final String name; // e.g., "Vitamin K2", "Vitamin D3"
final double amount; // e.g., 75, 20
final String unit; // e.g., "mcg", "mg", "IU"
const Ingredient({
this.id,
required this.name,
required this.amount,
required this.unit,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'amount': amount,
'unit': unit,
};
}
factory Ingredient.fromMap(Map<String, dynamic> map) {
return Ingredient(
id: map['id'],
name: map['name'],
amount: map['amount']?.toDouble() ?? 0.0,
unit: map['unit'],
);
}
Ingredient copyWith({
int? id,
String? name,
double? amount,
String? unit,
}) {
return Ingredient(
id: id ?? this.id,
name: name ?? this.name,
amount: amount ?? this.amount,
unit: unit ?? this.unit,
);
}
@override
String toString() {
return '$amount$unit $name';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is Ingredient &&
other.name == name &&
other.amount == amount &&
other.unit == unit;
}
@override
int get hashCode {
return name.hashCode ^ amount.hashCode ^ unit.hashCode;
}
}

View File

@@ -1,9 +1,12 @@
import 'ingredient.dart';
import 'dart:convert';
class Supplement {
final int? id;
final String name;
final double dosageAmount; // Amount per unit (e.g., 187mg)
final String? brand;
final List<Ingredient> ingredients;
final int numberOfUnits; // Number of units to take (e.g., 2 capsules)
final String unit; // mg, g, ml, etc.
final String unitType; // capsules, tablets, ml, etc.
final int frequencyPerDay;
final List<String> reminderTimes; // e.g., ['08:00', '20:00']
@@ -14,9 +17,9 @@ class Supplement {
Supplement({
this.id,
required this.name,
required this.dosageAmount,
this.brand,
this.ingredients = const [],
required this.numberOfUnits,
required this.unit,
required this.unitType,
required this.frequencyPerDay,
required this.reminderTimes,
@@ -25,16 +28,38 @@ class Supplement {
this.isActive = true,
});
// Helper getter for total dosage per intake
double get totalDosagePerIntake => dosageAmount * numberOfUnits;
// Helper getters
double get totalDosagePerIntake {
// This concept doesn't apply well to multi-ingredient supplements
// Return 0 as it should be handled per ingredient
return 0.0;
}
// Get formatted ingredients string for display
String get ingredientsDisplay {
if (ingredients.isEmpty) {
return 'No ingredients specified';
}
return ingredients.map((ingredient) =>
'${ingredient.amount * numberOfUnits}${ingredient.unit} ${ingredient.name}'
).join(', ');
}
// Get ingredients per single unit
String get ingredientsPerUnit {
if (ingredients.isEmpty) {
return 'No ingredients specified';
}
return ingredients.map((ingredient) => ingredient.toString()).join(', ');
}
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'dosageAmount': dosageAmount,
'brand': brand,
'ingredients': jsonEncode(ingredients.map((ingredient) => ingredient.toMap()).toList()),
'numberOfUnits': numberOfUnits,
'unit': unit,
'unitType': unitType,
'frequencyPerDay': frequencyPerDay,
'reminderTimes': reminderTimes.join(','),
@@ -45,13 +70,29 @@ class Supplement {
}
factory Supplement.fromMap(Map<String, dynamic> map) {
List<Ingredient> ingredients = [];
// Try to parse ingredients if they exist
if (map['ingredients'] != null && map['ingredients'].isNotEmpty) {
try {
final ingredientsJson = map['ingredients'] as String;
final ingredientsList = jsonDecode(ingredientsJson) as List;
ingredients = ingredientsList
.map((ingredient) => Ingredient.fromMap(ingredient as Map<String, dynamic>))
.toList();
} catch (e) {
// If parsing fails, fall back to empty list
ingredients = [];
}
}
return Supplement(
id: map['id'],
name: map['name'],
dosageAmount: map['dosageAmount']?.toDouble() ?? map['dosage']?.toDouble() ?? 0.0, // Backwards compatibility
numberOfUnits: map['numberOfUnits'] ?? 1, // Default to 1 for backwards compatibility
unit: map['unit'],
unitType: map['unitType'] ?? 'units', // Default unit type for backwards compatibility
brand: map['brand'],
ingredients: ingredients,
numberOfUnits: map['numberOfUnits'] ?? 1,
unitType: map['unitType'] ?? 'units',
frequencyPerDay: map['frequencyPerDay'],
reminderTimes: map['reminderTimes'].split(','),
notes: map['notes'],
@@ -63,9 +104,9 @@ class Supplement {
Supplement copyWith({
int? id,
String? name,
double? dosageAmount,
String? brand,
List<Ingredient>? ingredients,
int? numberOfUnits,
String? unit,
String? unitType,
int? frequencyPerDay,
List<String>? reminderTimes,
@@ -76,9 +117,9 @@ class Supplement {
return Supplement(
id: id ?? this.id,
name: name ?? this.name,
dosageAmount: dosageAmount ?? this.dosageAmount,
brand: brand ?? this.brand,
ingredients: ingredients ?? this.ingredients,
numberOfUnits: numberOfUnits ?? this.numberOfUnits,
unit: unit ?? this.unit,
unitType: unitType ?? this.unitType,
frequencyPerDay: frequencyPerDay ?? this.frequencyPerDay,
reminderTimes: reminderTimes ?? this.reminderTimes,