Skip to main content
This page documents all data models and structures used in Vitu’s local Hive database.

Core models

User

The User class stores account information and personal details for health calculations. Defined in: main.dart:56-125
nombre
string
required
User’s first name
apellido
string
required
User’s last name
genero
string
required
Gender: "masculino" or "femenino" (used for hydration goal calculation)
edad
int
required
Age in years (used for hydration goal adjustments)
altura
double
required
Height in centimeters
peso
double
required
Weight in kilograms (used for hydration goal calculation: weight × 35ml)
correo
string
required
Email address (used as unique identifier/primary key)
contrasena
string
required
Password (stored in plain text - not recommended for production)
brightness
string
Theme brightness: "light" or "dark". Defaults to system preference if null.
seedColor
int
ARGB color value for Material 3 theme. Example: 0xFF8BC34A for lime green.
fontFamily
string
Font family name: null (system default) or "serif"
followLocation
bool
Whether to use GPS for exercise tracking. Defaults to false.
Storage: Example:
final user = User(
  nombre: 'Juan',
  apellido: 'Pérez',
  genero: 'masculino',
  edad: 28,
  altura: 175.0,
  peso: 70.0,
  correo: '[email protected]',
  contrasena: 'securepassword123',
  brightness: 'dark',
  seedColor: 0xFF8BC34A,
  fontFamily: null,
  followLocation: true,
);

await _usersBox.put('user:${user.correo}', user.toMap());

UserSettings

The UserSettings class stores user preferences separate from the User model. Defined in: main.dart:161-200
userId
string
required
User email (foreign key to User)
brightness
string
Theme brightness: "light" or "dark"
seedColor
int
ARGB color value for theming
fontFamily
string
Font family: null or "serif"
followLocation
bool
GPS tracking enabled for exercise
metaHydratationMl
double
Custom daily hydration goal in milliliters. If null, auto-calculated from weight/age/gender.
Storage: Example:
final settings = UserSettings(
  userId: '[email protected]',
  brightness: 'dark',
  seedColor: 0xFF4CAF50,
  fontFamily: 'serif',
  followLocation: true,
  metaHydratationMl: 2500.0,
);

await _userSettingsBox.put('settings:${settings.userId}', settings.toMap());

Daily data models

All daily data uses composite keys: {userId}_{YYYY-MM-DD}

Exercise data

Box: daily_exercise
Key pattern: {email}_{date}
Example key: [email protected]_2026-03-04
steps
int
required
Total steps counted for the day
distance_km
double
Distance traveled in kilometers (if GPS enabled)
calories
double
Estimated calories burned
activity_type
string
Current activity: "stationary", "walking", "running", or "vehicle"
last_updated
string
ISO 8601 timestamp of last step count update
Example:
final today = DateTime.now();
final key = '[email protected]_${today.year}-${today.month.toString().padLeft(2, '0')}-${today.day.toString().padLeft(2, '0')}';

await _dailyExerciseBox.put(key, {
  'steps': 8543,
  'distance_km': 6.2,
  'calories': 320.5,
  'activity_type': 'walking',
  'last_updated': DateTime.now().toIso8601String(),
});

Hydration logs

Box: hydration_logs
Key pattern: {email}_{date}_{timestamp}
Example key: [email protected]_2026-03-04_1709553600
Individual water intake events (e.g., +250ml at 14:30).
userId
string
required
User email
ml
int
required
Milliliters consumed in this event (100, 250, or 500)
timestamp
string
required
ISO 8601 timestamp of when water was logged
Example:
final now = DateTime.now();
final key = '[email protected]_${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}_${now.millisecondsSinceEpoch}';

await _hydrationLogsBox.put(key, {
  'userId': '[email protected]',
  'ml': 250,
  'timestamp': now.toIso8601String(),
});

Hydration summary

Box: daily_hydration_summary
Key pattern: {email}_{date}
Example key: [email protected]_2026-03-04
Daily total of water consumed.
total_ml
double
required
Total milliliters consumed for the day
goal_ml
double
required
Daily hydration goal (from user settings or auto-calculated)
percentage
double
Progress percentage: (total_ml / goal_ml) * 100
Example:
final key = '[email protected]_2026-03-04';

await _hydrationSummaryBox.put(key, {
  'total_ml': 1850.0,
  'goal_ml': 2450.0,
  'percentage': 75.5,
});

Sleep data

Box: daily_sleep
Key pattern: {email}_{date}
Example key: [email protected]_2026-03-04
Sleep session recorded for a given night.
hora_inicio
string
required
ISO 8601 timestamp of sleep start time (when screen turned off)
hora_fin
string
required
ISO 8601 timestamp of wake time (when screen turned on)
duration_h
double
required
Sleep duration in hours (clipped to 19:00-07:00 window)
quality
int
required
Sleep quality rating (1-5 stars). Auto-assigned based on duration:
  • Less than 6h: 2 stars
  • 6-8h: 4 stars
  • More than 8h: 5 stars
User can edit this value.
Example:
final key = '[email protected]_2026-03-04';

await Hive.box('daily_sleep').put(key, {
  'hora_inicio': '2026-03-03T23:15:00.000Z',
  'hora_fin': '2026-03-04T07:30:00.000Z',
  'duration_h': 7.5,
  'quality': 4,
});

Data relationships

Key conventions

Foreign keys

All daily data boxes use userId (the user’s email) as a foreign key to relate data back to the user.

Composite keys

Daily records use the pattern {userId}_{YYYY-MM-DD} to ensure:
  • Unique keys per user per day
  • O(1) lookups for specific dates
  • No data collisions between users
Example composite key generation:
String getDailyKey(String userId, DateTime date) {
  return '${userId}_${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
}

final key = getDailyKey('[email protected]', DateTime.now());
// Result: "[email protected]_2026-03-04"

Date formatting

Always use ISO 8601 format for timestamps:
final timestamp = DateTime.now().toIso8601String();
// Result: "2026-03-04T14:30:00.000Z"

Type conversions

Hive stores data as maps with dynamic values. Always validate types when reading:
final rawData = _dailyExerciseBox.get(key);
if (rawData is Map) {
  final steps = (rawData['steps'] is int) 
      ? rawData['steps'] 
      : int.tryParse('${rawData['steps'] ?? 0}') ?? 0;
}

Hydration goal calculation

The hydration goal is calculated automatically if metaHydratationMl is not set:
double calcularMetaHidratacion(User u) {
  final base = (u.peso > 0 ? u.peso : 70.0) * 35.0;
  var adj = base;
  if (u.edad < 14) adj = base * 0.90;
  if (u.edad >= 65) adj = base * 0.95;
  if (u.genero.toLowerCase() == 'masculino') adj += 200;
  return adj.clamp(1200.0, 4500.0);
}
Formula:
  • Base: weight × 35ml
  • Age less than 14: -10%
  • Age 65+: -5%
  • Male: +200ml
  • Clamped: 1200-4500ml

Build docs developers (and LLMs) love