Integrating FHIR with Flutter — Part 1
A Guide to FHIR Introduction and Setting Up Your Project
Integrating FHIR with Flutter — Part 1
A Guide to FHIR Introduction and Setting Up Your Project

The image was generated using GPT-5.2
1. Introduction
In today’s healthcare sector, transfer and access of patient data remain frustratingly inconsistent across different systems, hospitals, and clinics. A patient who visits multiple healthcare providers often finds their medical history scattered across incompatible electronic health records (EHR) systems. Doctors can’t easily access critical information, leading to repeated tests, medication errors, and fragmented care. Also, software developers who wish to build on existing healthcare solutions find it extremely difficult when data is poorly structured, inconsistently labeled, or locked behind proprietary formats. Even when access is granted, the lack of a common data model makes integrating systems slow and expensive.
This is where FHIR (Fast Healthcare Interoperability Resources) comes in — a modern standard that promises to break down these data models and enable seamless healthcare information exchange.
My Journey
I’ve always been drawn to building mobile products that make a direct impact on human lives. In 2025, that drive led me to work with ArewaCare, a medical software company creating digital tools for pregnant women and the physicians who care for them. While working on healthcare workflows, we stumbled on a challenge that quietly underpins almost every medical system: Data fragmentation. “Data fragmentation is a term widely used in the field of data management and particularly in databases and file systems. It is a method of storing a single file or a single piece of data into multiple parts on the disk”. Patient records are often scattered across multiple locations, and systems struggle to communicate with one another, which also leads to degradation and increased security risks. I was introduced to the concept of interoperability of data as a solution, and for the first time, I heard the word “FHIR”. This led me to research and build a demo FHIR-compliant healthcare management application using Flutter — and in this two-part series, I’ll share everything I learned.
P.S: The very first mobile application I built in 2020 was a healthcare solution — Beata Medrek (An Electronic healthcare record software with an emergency feature that helps one locate the nearest hospital). A solution that was inspired by my first-hand experience with Nigeria’s public hospitals while I was taking care of my then-sick mother. She passed gloriously in 2021 🕊️🕊️(I pray St. Cordelia Nkpozi's soul rest in peace. Amen); and my solution, although completed then, died as well in my GitHub account 😂😂
What You’ll Learn in Part 1
- Understanding FHIR and why it matters in healthcare
- The problem FHIR solves in healthcare interoperability
- Overview of a complete healthcare app built with Flutter, key features, and use cases
- Domain models and entities structure
- Libraries and tools for FHIR development
- Foundation for Part 2’s implementation
What to Expect in Part 2
- Complete implementation with code examples
- Network layer and API integration
- State management with Riverpod
- Form handling and validation
- Testing strategies
- Deployment considerations
Target Audience
- Flutter developers curious about healthcare apps
- Healthcare IT professionals interested in mobile development
- Developers learning about FHIR integration
- Students exploring health tech
2. FHIR — Fast Healthcare Interoperability Resources
FHIR (Fast Healthcare Interoperability Resources) is a next-generation standards framework created by HL7 International for exchanging healthcare information electronically and consistently between systems. Think of it as the “REST API for healthcare data.”
Key Characteristics
RESTful Architecture
- Uses standard HTTP methods (GET, POST, PUT, DELETE)
- URL-based resource access
Resource-Based Model
FHIR organizes healthcare data into modular “Resources”:
- Patient: Demographics, contact information
- Observation: Vital signs, lab results
- MedicationRequest: Prescriptions
- Condition: Diagnoses
- Appointment: Scheduling information
- DiagnosticReport: Lab test results
- Practitioner: Healthcare providers
and many other resources; see a Swagger view of other possible resources: HAPI FHIR Test/Demo Server R4 Endpoint
Each resource is a self-contained unit of healthcare information with a standardized structure.
Modern Data Formats
- JSON: Primary format (developer-friendly)
- XML: Alternative format
- Easy to parse and generate
Interoperability
- Enables different healthcare systems to communicate
- Reduces integration complexity
- Supports mobile and cloud applications
- Industry-wide adoption
Problem Solved: Both patients, doctors, and hospitals can access health data on any device.
Use Cases:
- View medical records on smartphones
- Share data with new doctors
- Track vital signs over time
- Medication reminders
So a patient can visit ABC hospital and is eventually transferred to XYZ medical center; the latter can easily access all tests, diagnoses, vital signs, history, etc., just by querying the FHIR server of the former. Amazing right? 🥳. In case of an emergency, this tool would be helpful for doctors to make informed decisions.
3. Project Overview & Features
What We’re Building
A demo FHIR-compliant healthcare management application built with Flutter that demonstrates real-world integration with public FHIR servers (HAPI, KODJIN, FIREFLY). The application supports patient registration and record management, diagnosis tracking, prescription and medication orders, vital signs and clinical observations, appointment scheduling, and laboratory results reporting.
From a Flutter perspective, the focus wasn’t on UI complexity but on modeling healthcare data as domain objects, mapping them cleanly to FHIR resources, and building predictable data flows for creating, reading, and updating clinical records.
Links to the public servers: HAPI FHIR (https://hapi.fhir.org/baseR4), Kodjin (https://demo.kodjin.com), Firefly (https://server.fire.ly)
Architecture Overview
The project follows Clean Architecture principles with two main layers:
Domain Layer (Core Business Logic && Implementation)
↓
Presentation Layer (UI)
Don’t ask about the data layer 😔. I found it too cumbersome for this small project, so all implementation and use cases happen in the domain repository.
In this section, we’ll focus on the Domain Layer, which contains our business entities and models.
Core Entities Explained
1. ApiResponse Entity
Purpose: Standardizes all API responses for consistent error handling
class ApiResponse<T> {
final bool isSuccess;
final T? data;
final String? error;
final int? statusCode;
const ApiResponse({
required this.isSuccess,
this.data,
this.error,
this.statusCode,
});
// Factory constructors for success and error cases
factory ApiResponse.success(T data) {
return ApiResponse(
isSuccess: true,
data: data,
);
}
factory ApiResponse.error(String error, {int? statusCode}) {
return ApiResponse(
isSuccess: false,
error: error,
statusCode: statusCode,
);
}
// Safe data access
T get safeData => data as T;
}
Why This Matters:
- Type-safe response handling
- Consistent error management
2. Other project FHIR-related Entities (Patient, Observation, MedicationRequest, Condition, Appointment, DiagnosticReport, Practitioner)
There are models from the fhir_r4 plugin (an Official FHIR R4 implementation for Dart on pub.dev (fhir_r4)), but they can be very complex to work with; therefore, I created an entity that complements the plugin’s models.
For Patient Entity:
class ProjectPatientEntity {
final String id;
final String firstName;
final String lastName;
final DateTime dateOfBirth;
final String? gender;
final String phoneNumber;
final String? email;
final String? address;
final String? emergencyContactNo;
ProjectPatientEntity({
required this.id,
required this.firstName,
required this.lastName,
required this.dateOfBirth,
this.gender,
required this.phoneNumber,
this.email,
this.address,
this.emergencyContactNo,
});
In the app, the parameters of ProjectPatientEntity are intentionally designed to mirror the input fields in the Register Patient screen. This keeps the Flutter form layer simple and predictable, allowing user input to be captured as a plain domain object without any direct dependency on FHIR.
Before persisting data, this domain entity is then mapped into a FHIR Patient resource. At this boundary, application-specific fields — such as names, contact details, gender, and date of birth — are translated into their standardized FHIR equivalents. This extra mapping step is deliberate: it decouples the Flutter UI and business logic from the FHIR schema while ensuring that any data sent to the server conforms strictly to interoperability standards.
import 'package:fhir_r4/fhir_r4.dart' as fhir;
MapStringDynamic addPatient({fhir.Patient? existingPatient}) {
final body = fhir.Patient(
id: fhir.FhirString(id),
active: fhir.FhirBoolean(true),
name: [
HumanName(family: lastName.toFhirString, given [firstName.toFhirString]),
],
identifier: [fhir.Identifier(value: ApiConstants.projectIdentifier.toFhirString)],
birthDate: dateOfBirth.toFhirDate,
gender: gender != null ? fhir.AdministrativeGender(gender?.toLowerCase()) : null,
telecom: [
fhir.ContactPoint(system: fhir.ContactPointSystem.email, value: email?.toFhirString),
fhir.ContactPoint(system: fhir.ContactPointSystem.phone, value: phoneNumber.toFhirString),
fhir.ContactPoint(system: fhir.ContactPointSystem.other, value: emergencyContactNo?.toFhirString),
],
address: address != null ? [fhir.Address(text: address?.toFhirString)] : null,
);
// I use the .copyWith() when i am editing an already existing patient data
if (existingPatient != null) {
final updatedPatient = existingPatient.copyWith(
name: body.name,
birthDate: body.birthDate,
gender: body.gender,
telecom: body.telecom,
address: body.address,
active: body.active,
);
return updatedPatient.toJson();
}
return body.toJson();
}
For Prescription Entity:
class ProjectPrescriptionEntity {
final String patientId;
final String medication;
final int dosage;
final String route;
final int frequency;
final int duration;
final DateTime startDate;
final String prescribingDoctor;
final String? instructions;
ProjectPrescriptionEntity({
required this.patientId,
required this.medication,
required this.dosage,
required this.route,
required this.frequency,
required this.duration,
required this.startDate,
required this.prescribingDoctor,
this.instructions,
});
// Convert to FHIR MedicationRequest
Map<String, dynamic> addPrescription() {
return MedicationRequest(
status: MedicationrequestStatus.active,
intent: MedicationrequestIntent.order,
subject: Reference(
reference: 'Patient/$patientId'.toFhirString,
),
medicationCodeableConcept: CodeableConcept(
text: medication.toFhirString,
),
authoredOn: startDate.toFhirDateTime,
requester: Reference(
display: prescribingDoctor.toFhirString,
),
dosageInstruction: [
Dosage(
text: instructions?.toFhirString,
timing: Timing(
repeat: TimingRepeat(
frequency: frequency.toFhirPositiveInt,
period: 1.toFhirDecimal,
periodUnit: 'd'.toFhirString,
duration: duration.toFhirDecimal,
durationUnit: 'd'.toFhirString,
),
),
route: CodeableConcept(text: route.toFhirString),
doseAndRate: [
DosageDoseAndRate(
doseQuantity: Quantity(
value: dosage.toFhirDecimal,
unit: 'mg'.toFhirString,
),
),
],
),
],
).toJson();
}
}
The same method goes for all the other entities in the project. Check the Entities folder in the project: lib/src/domain/entities - Source Code (Github)
4. Libraries and tools for FHIR development
Core Dependencies
1. **fhir_r4:** A Dart implementation of the FHIR (Fast Healthcare Interoperability Resources) R4 specification
dependencies:
fhir_r4: ^0.4.3
Features:
- Complete FHIR R4 resource models
- Type-safe resource creation
- JSON/YAML serialization
- Validation support
- Extension methods
2. **flutter_riverpod:** A reactive caching and data-binding framework
dependencies:
flutter_riverpod: ^2.6.1
Key Features for Our App:
AutoDisposeNotifier: Automatic controller cleanupProvider: Dependency injection- Type-safe state access
P.S: I am not a big fan of being loyal to a state management solution; you can use any that works! 😅😅
3. **Dio:** Powerful HTTP client for Dart
dependencies:
dio: ^5.9.0
Key Features:
- Base URL configuration
- Custom headers (FHIR requires
application/fhir+json) - Retry mechanism
- Progress callbacks
4. **dio_smart_retry:** Automatic retry for failed requests
dependencies:
dio_smart_retry: ^7.0.1
Features:
- Exponential backoff
- Configurable retry count
- Specific error type retry
Configuration:
dio.interceptors.add(
RetryInterceptor(
dio: dio,
retries: 3,
retryDelays: [
Duration(seconds: 1),
Duration(seconds: 3),
Duration(seconds: 5),
],
),
);
What’s Coming in Part 2: Implementation
Part 2 will dive deep into the actual implementation:
1. Project Setup & Configuration
- Installing dependencies
- Platform-specific configuration
2. Network Layer Implementation
- Dio configuration and setup
- FHIR server integration
- Interceptors (logging, retry, auth)
- Dynamic server switching
- Error handling
3. Repository Pattern
- Abstract repositories
- Implementation with dependency injection
- CRUD operations
- API response handling
4. State Management with Riverpod
- Controller setup
- Form state management
- Loading states
- Error handling
- Provider dependency injection
5. Building FHIR Forms
Complete implementation of:
- Patient registration form
- Prescription form with validation
- Observations form
- Converting form data to FHIR resources
- Submitting to the FHIR server
- Handling responses
Prepare for Part 2
Recommended Actions:
- Clone the Repository:
git clone https://github.com/Captured-Heart/fhir_demo.git
cd fhir_demo
2. Explore FHIR Documentation:
3. Try HAPI FHIR Server:
- Visit: https://hapi.fhir.org/baseR4
- Explore existing resources
- Try creating a Patient via Swagger UI
4. Review Flutter Concepts:
- Form handling
- State management basics
- HTTP requests
Resources
FHIR Documentation
- HL7 FHIR Official
- FHIR R4 Spec
- Resource Examples
- FHIR-FLI
- Medblocks — FHIR Fundamentals Course
- KODJIN FHIR Server Documentation
- HL7 FHIR DevDays YouTube Playlist
FHIR Servers
- HAPI FHIR Test Server
- HAPI Swagger UI
- KODJIN Demo Server
- FIREFLY Server
- Confluence — Public test servers list
Flutter Resources
Project Links
- GitHub Repository
- Part 2 (Coming Soon!)
메타데이터
- post_id
- ff63ab3f7df3
- slug
- integrating-fhir-with-flutter-part-1-ff63ab3f7df3
- url
- https://medium.com/@captured_heart/integrating-fhir-with-flutter-part-1-ff63ab3f7df3
- canonical_url
- https://medium.com/@captured_heart/integrating-fhir-with-flutter-part-1-ff63ab3f7df3
- author_url
- https://medium.com/@captured_heart
- status
- ok
- fetched_at
- 2026-06-20 20:29:01