Skip to content
CARP Docs
Ask AISystemLightDark
On this page

Blood pressure and oxygen

Write blood pressure readings with writeBloodPressure and blood oxygen saturation with writeBloodOxygen.

Copy MarkdownOpen inOpen in GitHubView as MarkdownOpen in Scira AIOpen in ChatGPTOpen in ClaudeOpen in Cursor

Blood pressure and blood oxygen have dedicated write methods. Write heart rate and other numeric vitals with writeHealthData.

Blood pressure

writeBloodPressure writes systolic and diastolic readings together.

Future<bool> writeBloodPressure({
  required double systolic,
  required double diastolic,
  required DateTime startTime,
  DateTime? endTime,
})
ParameterTypeRequiredDescription
systolicdoubleYesSystolic pressure (mmHg)
diastolicdoubleYesDiastolic pressure (mmHg)
startTimeDateTimeYesMeasurement time
endTimeDateTimeNoEnd time (optional)

Write a reading

// Normal blood pressure reading
await health.writeBloodPressure(
  systolic: 120.0,
  diastolic: 80.0,
  startTime: DateTime.now(),
);

// Elevated reading
await health.writeBloodPressure(
  systolic: 135.0,
  diastolic: 85.0,
  startTime: DateTime.now(),
);

Categorize readings

enum BPCategory {
  normal,        // <120 and <80
  elevated,      // 120-129 and <80
  hypertension1, // 130-139 or 80-89
  hypertension2, // 140-179 or 90-119
  crisis,        // ≥180 or ≥120
}

BPCategory categorizeBloodPressure(double systolic, double diastolic) {
  if (systolic >= 180 || diastolic >= 120) {
    return BPCategory.crisis;
  } else if (systolic >= 140 || diastolic >= 90) {
    return BPCategory.hypertension2;
  } else if (systolic >= 130 || diastolic >= 80) {
    return BPCategory.hypertension1;
  } else if (systolic >= 120) {
    return BPCategory.elevated;
  }
  return BPCategory.normal;
}

// Usage with categorization
Future<void> logBloodPressure(double sys, double dia) async {
  final category = categorizeBloodPressure(sys, dia);
  print('BP: $sys/$dia - ${category.name}');
  
  await health.writeBloodPressure(
    systolic: sys,
    diastolic: dia,
    startTime: DateTime.now(),
  );
}

Validate readings

class BloodPressureTracker {
  final Health health;
  
  BloodPressureTracker(this.health);
  
  Future<bool> logReading({
    required double systolic,
    required double diastolic,
    DateTime? measurementTime,
  }) async {
    // Validate readings
    if (!_isValidReading(systolic, diastolic)) {
      print('Invalid BP reading');
      return false;
    }
    
    return await health.writeBloodPressure(
      systolic: systolic,
      diastolic: diastolic,
      startTime: measurementTime ?? DateTime.now(),
    );
  }
  
  bool _isValidReading(double systolic, double diastolic) {
    return systolic >= 70 && systolic <= 250 &&
           diastolic >= 40 && diastolic <= 150 &&
           systolic > diastolic;
  }
}

// Usage
final tracker = BloodPressureTracker(health);
await tracker.logReading(systolic: 120, diastolic: 80);

Blood oxygen

writeBloodOxygen writes blood oxygen saturation (SpO2).

Future<bool> writeBloodOxygen({
  required double saturation,
  required DateTime startTime,
  DateTime? endTime,
  double? flowRate,
})
ParameterTypeRequiredDescription
saturationdoubleYesSpO2 percentage (0-100)
startTimeDateTimeYesMeasurement time
endTimeDateTimeNoEnd time (optional)
flowRatedoubleNoOxygen flow rate (L/min)

Write a reading

// Normal oxygen saturation
await health.writeBloodOxygen(
  saturation: 98.0,
  startTime: DateTime.now(),
);

// Lower saturation with oxygen therapy
await health.writeBloodOxygen(
  saturation: 92.0,
  flowRate: 2.0, // 2 L/min supplemental oxygen
  startTime: DateTime.now(),
);

Interpret oxygen levels

String interpretSpO2(double saturation) {
  if (saturation >= 95) {
    return 'Normal';
  } else if (saturation >= 90) {
    return 'Mildly Low';
  } else if (saturation >= 85) {
    return 'Moderately Low';
  } else {
    return 'Severely Low';
  }
}

// Usage
Future<void> logOxygenLevel(double saturation) async {
  final interpretation = interpretSpO2(saturation);
  print('SpO2: $saturation% - $interpretation');
  
  if (saturation < 90) {
    print('⚠️ Low oxygen - consider medical attention');
  }
  
  await health.writeBloodOxygen(
    saturation: saturation,
    startTime: DateTime.now(),
  );
}

Validate readings

class BloodOxygenTracker {
  final Health health;
  
  BloodOxygenTracker(this.health);
  
  Future<bool> logReading({
    required double saturation,
    double? flowRate,
    DateTime? measurementTime,
  }) async {
    // Validate saturation
    if (saturation < 0 || saturation > 100) {
      print('Invalid SpO2: must be between 0-100%');
      return false;
    }
    
    // Validate flow rate if provided
    if (flowRate != null && (flowRate < 0 || flowRate > 15)) {
      print('Invalid flow rate');
      return false;
    }
    
    return await health.writeBloodOxygen(
      saturation: saturation,
      flowRate: flowRate,
      startTime: measurementTime ?? DateTime.now(),
    );
  }
}

// Usage
final tracker = BloodOxygenTracker(health);
await tracker.logReading(saturation: 98.0);

Write a full set of vital signs

This example writes blood pressure, heart rate and blood oxygen with the same timestamp:

class CardiovascularMonitor {
  final Health health;
  
  CardiovascularMonitor(this.health);
  
  Future<void> logVitalSigns({
    required double systolic,
    required double diastolic,
    required double heartRate,
    required double spO2,
  }) async {
    final now = DateTime.now();
    
    // Blood pressure
    await health.writeBloodPressure(
      systolic: systolic,
      diastolic: diastolic,
      startTime: now,
    );
    
    // Heart rate
    await health.writeHealthData(
      value: heartRate,
      type: HealthDataType.HEART_RATE,
      startTime: now,
    );
    
    // Blood oxygen
    await health.writeBloodOxygen(
      saturation: spO2,
      startTime: now,
    );
    
    print('Vital signs logged:');
    print('  BP: $systolic/$diastolic mmHg');
    print('  HR: $heartRate bpm');
    print('  SpO2: $spO2%');
  }
}

// Usage
final monitor = CardiovascularMonitor(health);
await monitor.logVitalSigns(
  systolic: 120,
  diastolic: 80,
  heartRate: 72,
  spO2: 98,
);

Permissions

Request WRITE permission for every type you write. Blood pressure uses two data types, BLOOD_PRESSURE_SYSTOLIC and BLOOD_PRESSURE_DIASTOLIC, so request both:

// Request cardiovascular permissions
final types = [
  HealthDataType.BLOOD_PRESSURE_SYSTOLIC,
  HealthDataType.BLOOD_PRESSURE_DIASTOLIC,
  HealthDataType.BLOOD_OXYGEN,
  HealthDataType.HEART_RATE,
];

final granted = await health.requestAuthorization(
  types,
  permissions: List.filled(types.length, HealthDataAccess.WRITE),
);

Platform differences

AndroidiOS
  • Blood pressure is stored as a combined record.
  • Requires the Health Connect SDK.
  • Flow rate support may vary.
  • Compatible with connected health apps.
  • Blood pressure is stored as separate systolic and diastolic entries, linked together automatically.
  • Supports flow rate for blood oxygen.
  • Data appears in the Apple Health app.

Quick links

Page · DocumentationCARP DocumentationPage · DocumentationIntroductionPage · DocumentationQuickstartPage · DocumentationTerminologyPage · DocumentationArchitecture