diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index a1d5093..ee6ddc4 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -33,8 +33,12 @@
-
-
+
+
diff --git a/lib/models/supplement_intake.dart b/lib/models/supplement_intake.dart
index cfce351..3330085 100644
--- a/lib/models/supplement_intake.dart
+++ b/lib/models/supplement_intake.dart
@@ -3,7 +3,7 @@ class SupplementIntake {
final int supplementId;
final DateTime takenAt;
final double dosageTaken; // Total dosage amount taken
- final int unitsTaken; // Number of units taken
+ final double unitsTaken; // Number of units taken (can be fractional)
final String? notes;
SupplementIntake({
@@ -32,7 +32,7 @@ class SupplementIntake {
supplementId: map['supplementId'],
takenAt: DateTime.parse(map['takenAt']),
dosageTaken: map['dosageTaken'],
- unitsTaken: map['unitsTaken'] ?? 1, // Default for backwards compatibility
+ unitsTaken: (map['unitsTaken'] ?? 1).toDouble(), // Default for backwards compatibility
notes: map['notes'],
);
}
@@ -42,7 +42,7 @@ class SupplementIntake {
int? supplementId,
DateTime? takenAt,
double? dosageTaken,
- int? unitsTaken,
+ double? unitsTaken,
String? notes,
}) {
return SupplementIntake(
diff --git a/lib/providers/supplement_provider.dart b/lib/providers/supplement_provider.dart
index acbdd10..c03eb69 100644
--- a/lib/providers/supplement_provider.dart
+++ b/lib/providers/supplement_provider.dart
@@ -103,13 +103,13 @@ class SupplementProvider with ChangeNotifier {
}
}
- Future recordIntake(int supplementId, double dosage, {int? unitsTaken, String? notes}) async {
+ Future recordIntake(int supplementId, double dosage, {double? unitsTaken, String? notes}) async {
try {
final intake = SupplementIntake(
supplementId: supplementId,
takenAt: DateTime.now(),
dosageTaken: dosage,
- unitsTaken: unitsTaken ?? 1,
+ unitsTaken: unitsTaken ?? 1.0,
notes: notes,
);
@@ -118,7 +118,7 @@ class SupplementProvider with ChangeNotifier {
// Show confirmation notification
final supplement = _supplements.firstWhere((s) => s.id == supplementId);
- final unitsText = unitsTaken != null && unitsTaken > 1 ? '$unitsTaken ${supplement.unitType}' : '';
+ final unitsText = unitsTaken != null && unitsTaken != 1 ? '${unitsTaken.toStringAsFixed(unitsTaken % 1 == 0 ? 0 : 1)} ${supplement.unitType}' : '';
await _notificationService.showInstantNotification(
'Supplement Taken',
'Recorded ${supplement.name}${unitsText.isNotEmpty ? ' - $unitsText' : ''} ($dosage ${supplement.unit})',
diff --git a/lib/screens/history_screen.dart b/lib/screens/history_screen.dart
index 8b53924..0aefa0a 100644
--- a/lib/screens/history_screen.dart
+++ b/lib/screens/history_screen.dart
@@ -120,62 +120,98 @@ class _HistoryScreenState extends State with SingleTickerProvider
}
final intakes = snapshot.data!;
+
+ // Group intakes by supplement
+ final Map>> groupedIntakes = {};
+ for (final intake in intakes) {
+ final supplementName = intake['supplementName'] as String;
+ groupedIntakes.putIfAbsent(supplementName, () => []);
+ groupedIntakes[supplementName]!.add(intake);
+ }
+
return ListView.builder(
padding: const EdgeInsets.all(16),
- itemCount: intakes.length,
+ itemCount: groupedIntakes.length,
itemBuilder: (context, index) {
- final intake = intakes[index];
- final takenAt = DateTime.parse(intake['takenAt']);
+ final supplementName = groupedIntakes.keys.elementAt(index);
+ final supplementIntakes = groupedIntakes[supplementName]!;
+
+ // Calculate totals
+ double totalDosage = 0;
+ double totalUnits = 0;
+ final firstIntake = supplementIntakes.first;
+
+ for (final intake in supplementIntakes) {
+ totalDosage += intake['dosageTaken'] as double;
+ totalUnits += (intake['unitsTaken'] as num?)?.toDouble() ?? 1.0;
+ }
return Card(
- margin: const EdgeInsets.only(bottom: 8),
- child: ListTile(
+ margin: const EdgeInsets.only(bottom: 12),
+ child: ExpansionTile(
leading: CircleAvatar(
backgroundColor: Theme.of(context).colorScheme.primary,
child: Icon(Icons.medication, color: Theme.of(context).colorScheme.onPrimary),
),
- title: Text(intake['supplementName']),
+ title: Text(
+ supplementName,
+ style: const TextStyle(fontWeight: FontWeight.w600),
+ ),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Text('${intake['dosageTaken']} ${intake['supplementUnit']}'),
Text(
- 'Taken at ${DateFormat('HH:mm').format(takenAt)}',
+ '${totalDosage.toStringAsFixed(totalDosage % 1 == 0 ? 0 : 1)} ${firstIntake['supplementUnit']} total',
+ style: TextStyle(
+ fontWeight: FontWeight.w500,
+ color: Theme.of(context).colorScheme.primary,
+ ),
+ ),
+ Text(
+ '${totalUnits.toStringAsFixed(totalUnits % 1 == 0 ? 0 : 1)} ${firstIntake['supplementUnitType'] ?? 'units'} • ${supplementIntakes.length} intake${supplementIntakes.length > 1 ? 's' : ''}',
style: TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
- if (intake['notes'] != null && intake['notes'].toString().isNotEmpty)
- Text(
- intake['notes'],
- style: TextStyle(
- fontSize: 12,
- color: Theme.of(context).colorScheme.onSurfaceVariant,
- fontStyle: FontStyle.italic,
- ),
- ),
],
),
- trailing: PopupMenuButton(
- onSelected: (value) {
- if (value == 'delete') {
- _deleteIntake(context, intake['id'], intake['supplementName']);
- }
- },
- itemBuilder: (context) => [
- const PopupMenuItem(
- value: 'delete',
- child: Row(
- children: [
- Icon(Icons.delete, color: Colors.red),
- SizedBox(width: 8),
- Text('Delete', style: TextStyle(color: Colors.red)),
- ],
- ),
+ children: supplementIntakes.map((intake) {
+ final takenAt = DateTime.parse(intake['takenAt']);
+ final units = (intake['unitsTaken'] as num?)?.toDouble() ?? 1.0;
+
+ return ListTile(
+ contentPadding: const EdgeInsets.only(left: 72, right: 16),
+ title: Text(
+ '${(intake['dosageTaken'] as double).toStringAsFixed((intake['dosageTaken'] as double) % 1 == 0 ? 0 : 1)} ${intake['supplementUnit']}',
+ style: const TextStyle(fontSize: 14),
),
- ],
- ),
+ subtitle: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ '${units.toStringAsFixed(units % 1 == 0 ? 0 : 1)} ${intake['supplementUnitType'] ?? 'units'} at ${DateFormat('HH:mm').format(takenAt)}',
+ style: TextStyle(
+ fontSize: 12,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ ),
+ if (intake['notes'] != null && intake['notes'].toString().isNotEmpty)
+ Padding(
+ padding: const EdgeInsets.only(top: 4),
+ child: Text(
+ intake['notes'],
+ style: TextStyle(
+ fontSize: 12,
+ fontStyle: FontStyle.italic,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }).toList(),
),
);
},
diff --git a/lib/screens/supplements_list_screen.dart b/lib/screens/supplements_list_screen.dart
index 0b08ba5..d7623b7 100644
--- a/lib/screens/supplements_list_screen.dart
+++ b/lib/screens/supplements_list_screen.dart
@@ -133,7 +133,7 @@ class SupplementsListScreen extends StatelessWidget {
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setState) {
- final units = int.tryParse(unitsController.text) ?? supplement.numberOfUnits;
+ final units = double.tryParse(unitsController.text) ?? supplement.numberOfUnits.toDouble();
final totalDosage = supplement.dosageAmount * units;
return AlertDialog(
@@ -146,7 +146,7 @@ class SupplementsListScreen extends StatelessWidget {
Expanded(
child: TextField(
controller: unitsController,
- keyboardType: TextInputType.number,
+ keyboardType: const TextInputType.numberWithOptions(decimal: true),
decoration: InputDecoration(
labelText: 'Number of ${supplement.unitType}',
border: const OutlineInputBorder(),
@@ -175,7 +175,7 @@ class SupplementsListScreen extends StatelessWidget {
),
),
Text(
- '${totalDosage.toStringAsFixed(1)} ${supplement.unit}',
+ '${totalDosage.toStringAsFixed(totalDosage % 1 == 0 ? 0 : 1)} ${supplement.unit}',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
@@ -203,7 +203,7 @@ class SupplementsListScreen extends StatelessWidget {
),
ElevatedButton(
onPressed: () {
- final unitsTaken = int.tryParse(unitsController.text) ?? supplement.numberOfUnits;
+ final unitsTaken = double.tryParse(unitsController.text) ?? supplement.numberOfUnits.toDouble();
final totalDosageTaken = supplement.dosageAmount * unitsTaken;
context.read().recordIntake(
supplement.id!,
diff --git a/lib/services/database_helper.dart b/lib/services/database_helper.dart
index cb39e6b..4f70a29 100644
--- a/lib/services/database_helper.dart
+++ b/lib/services/database_helper.dart
@@ -68,7 +68,7 @@ class DatabaseHelper {
supplementId INTEGER NOT NULL,
takenAt TEXT NOT NULL,
dosageTaken REAL NOT NULL,
- unitsTaken INTEGER NOT NULL DEFAULT 1,
+ unitsTaken REAL NOT NULL DEFAULT 1,
notes TEXT,
FOREIGN KEY (supplementId) REFERENCES $supplementsTable (id)
)
@@ -77,20 +77,49 @@ class DatabaseHelper {
Future _onUpgrade(Database db, int oldVersion, int newVersion) async {
if (oldVersion < 2) {
- // Add new columns for version 2
+ // First, add new columns
await db.execute('ALTER TABLE $supplementsTable ADD COLUMN dosageAmount REAL DEFAULT 0');
await db.execute('ALTER TABLE $supplementsTable ADD COLUMN numberOfUnits INTEGER DEFAULT 1');
await db.execute('ALTER TABLE $supplementsTable ADD COLUMN unitType TEXT DEFAULT "units"');
- await db.execute('ALTER TABLE $intakesTable ADD COLUMN unitsTaken INTEGER DEFAULT 1');
+ await db.execute('ALTER TABLE $intakesTable ADD COLUMN unitsTaken REAL DEFAULT 1');
- // Migrate existing data
+ // Migrate existing data from old dosage column to new dosageAmount column
await db.execute('''
UPDATE $supplementsTable
- SET dosageAmount = dosage,
+ SET dosageAmount = COALESCE(dosage, 0),
numberOfUnits = 1,
unitType = 'units'
WHERE dosageAmount = 0
''');
+
+ // Create new table with correct schema
+ await db.execute('''
+ CREATE TABLE ${supplementsTable}_new (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT NOT NULL,
+ dosageAmount REAL NOT NULL,
+ numberOfUnits INTEGER NOT NULL DEFAULT 1,
+ unit TEXT NOT NULL,
+ unitType TEXT NOT NULL DEFAULT 'units',
+ frequencyPerDay INTEGER NOT NULL,
+ reminderTimes TEXT NOT NULL,
+ notes TEXT,
+ createdAt TEXT NOT NULL,
+ isActive INTEGER NOT NULL DEFAULT 1
+ )
+ ''');
+
+ // Copy data to new table
+ await db.execute('''
+ INSERT INTO ${supplementsTable}_new
+ (id, name, dosageAmount, numberOfUnits, unit, unitType, frequencyPerDay, reminderTimes, notes, createdAt, isActive)
+ SELECT id, name, dosageAmount, numberOfUnits, unit, unitType, frequencyPerDay, reminderTimes, notes, createdAt, isActive
+ FROM $supplementsTable
+ ''');
+
+ // Drop old table and rename new table
+ await db.execute('DROP TABLE $supplementsTable');
+ await db.execute('ALTER TABLE ${supplementsTable}_new RENAME TO $supplementsTable');
}
}
@@ -184,7 +213,7 @@ class DatabaseHelper {
String endDate = DateTime(date.year, date.month, date.day, 23, 59, 59).toIso8601String();
List