Introduction
In today’s digital age, managing medical records efficiently and securely is a significant concern. Traditional systems have been hindered by issues like data breaches, lack of interoperability, and the inability to manage records in real-time. However, blockchain technology provides an effective solution to these challenges. Through this tutorial, we will leverage the Celo blockchain to build a Medical Records Management System.
Celo, with its robust features and Ethereum compatibility, makes it an excellent choice for developing decentralized applications. We will be using Flutter, a popular cross-platform framework, to build our app, focusing on permissioned access control for ensuring data privacy and security.
By the end of this tutorial, we will create a secure, decentralized, user-friendly system for managing medical records.
Prerequisites
Before we dive into building our Celo-Based Medical Records Management System, you need to be familiar with a few things:
- Basic understanding of blockchain technology, specifically the Celo platform. If you’re new to Celo, I recommend going through the “Getting Started with Celo” tutorial first.
- Proficiency in Flutter and Dart. This tutorial assumes you can create a basic Flutter app.
- Some familiarity with smart contracts, as we will use them to store and retrieve medical data on the blockchain.
Requirements
- Flutter SDK: Make sure that you have the latest Flutter SDK installed on your system. If not, you can get it from here.
- Dart: Dart is the programming language used with Flutter. It should come bundled with the Flutter SDK, but you can also install it separately from here.
- Solidity: We will use Solidity for writing smart contracts.
- A Celo Wallet: Make sure to set up a wallet on Celo’s Alfajores Testnet. You can do this by following the steps from the
Setting up a Celo Wallet
section in this tutorial.
Writing the Smart Contract
First, let’s write the Smart Contract. We’ll use Solidity, a programming language for implementing Smart Contracts on Ethereum-based blockchains such as Celo.
Create a new file named MedicalRecords.sol
and paste the following code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MedicalRecords {
address private owner;
// Define a struct to represent a patient's medical record
struct Record {
string patientName;
string DOB;
string bloodType;
}
struct AdditionalRecord {
string knownAllergies;
string medicalConditions;
}
// Define two mappings that links a patient ID to their Record and AdditionalRecord
mapping (string => Record) private records;
mapping (string => AdditionalRecord) private additionalRecords;
// Define a modifier that only allows the owner of the contract to execute the function
modifier onlyOwner {
require(msg.sender == owner, "Only the contract owner can perform this operation");
_;
}
// The constructor sets the owner of the contract to the sender of the transaction
constructor() {
owner = msg.sender;
}
// Define a function to add a new Record to the mapping
function addRecord(
string calldata patientID,
string calldata patientName,
string calldata DOB,
string calldata bloodType
) public onlyOwner {
// Initialize a new Record
Record memory newRecord = Record({
patientName: patientName,
DOB: DOB,
bloodType: bloodType
});
// Store the new Record in the records mapping
records[patientID] = newRecord;
}
function addAdditionalRecord(
string calldata patientID,
string calldata knownAllergies,
string calldata medicalConditions
) public onlyOwner {
// Initialize a new AdditionalRecord
AdditionalRecord memory newAdditionalRecord = AdditionalRecord({
knownAllergies: knownAllergies,
medicalConditions: medicalConditions
});
// Store the new AdditionalRecord in the additionalRecords mapping
additionalRecords[patientID] = newAdditionalRecord;
}
// Define a function to retrieve a Record and an AdditionalRecord given a patient ID
function getRecords(string calldata patientID) public view returns (Record memory, AdditionalRecord memory) {
// Retrieve the Record from the records mapping
Record memory record = records[patientID];
AdditionalRecord memory additionalRecord = additionalRecords[patientID];
// If the Record doesn't exist, revert the transaction
if (bytes(record.patientName).length == 0) {
revert("No record found");
}
// Return the Record and AdditionalRecord
return (record, additionalRecord);
}
}
This smart contract named MedicalRecords
is designed to handle medical records on the blockchain. Here’s a detailed breakdown of its functionality:
- The contract declares two data structures,
Record
andAdditionalRecord
. These structs hold the necessary details about a patient’s medical record. TheRecord
struct holds the patient’s name, date of birth, and blood type, while theAdditionalRecord
holds the patient’s known allergies and medical conditions. - The contract uses two mappings,
records
andadditionalRecords
, to map a patient’s ID to their correspondingRecord
andAdditionalRecord
respectively. - The contract also defines a state variable
owner
of typeaddress
to keep track of the owner of the contract. The owner is initially set to be the address that deploys the contract, as defined in theconstructor
function. - There is a modifier function
onlyOwner
which restricts certain functions to be executable only by the owner of the contract. This function uses therequire
statement to enforce the constraint. - Two functions,
addRecord
andaddAdditionalRecord
allow adding a newRecord
andAdditionalRecord
respectively to the mapping. These functions take in patient details as parameters and construct a newRecord
orAdditionalRecord
, which is then added to the respective mapping. Both these functions are guarded by theonlyOwner
modifier, ensuring that only the contract owner can add new records. - Finally, the function
getRecords
allows retrieving both theRecord
and theAdditionalRecord
associated with a given patient ID. This function first checks if the requested record exists in the mapping, and if not, it reverts the transaction. If the record exists, it returns the correspondingRecord
andAdditionalRecord
.
After writing and testing the smart contract, you can deploy it to the Celo network using a deployment tools like Remix, Truffle, or Hardhat or you can check out this tutorial to learn how to deploy a smart contract. You will get a contract address and ABI as a result of the deployment. Keep these handy, as they’ll be necessary for interacting with the contract from the Flutter app.
Building the Flutter UI and Connecting to the Contract
First, we need to clone the flutter code repo by running this command in our terminal:
git clone https://github.com/Qtech11/flutter_celo_medical_records.git
Open the cloned project in your preferred IDE. You should have the following files in it:
In your terminal, run the command below to install all dependencies required for the project:
flutter pub get
-
In your
main.dart
file, you have the bottom navigation bar which allows you to navigate to two screens(Workout screen
andWeight screen
). -
The
add_patient_screen.dart
file contains theAdd Patient Screen
. The add patient screen is expected to display a form field where you can input the patient details and create a patient record. -
The
get_record_screen.dart
file contains theGet Record Screen
. The screen has a singletextfield
(where you input thepatient id
) and abutton
used to fetch the patient record using theid
you inputed . -
The
web3_provider.dart
file serves as an intermediary between the logic and UI using theflutter riverpod
package for managing the state of the app. -
The
web3helper.dart
file contains the code below:
import 'dart:convert';
import 'package:http/http.dart';
import 'package:web3dart/web3dart.dart';
class Web3Helper {
late Web3Client _client;
Web3Helper() {
Client httpClient = Client();
_client =
Web3Client('https://alfajores-forno.celo-testnet.org', httpClient);
}
final _contractAddress = EthereumAddress.fromHex('<your-contract-address>');
final credentials = EthPrivateKey.fromHex(
"<your-private-key>"); // replace with your celo wallet private key
static const abi = '<your-contract-abi>'; // Replace these with your actual contract ABI
// Replace these with your actual contract ABI
final contractABI = json.encode(abi);
Future<String> addRecord(
String patientId,
String patientName,
String dob,
String bloodType,
) async {
final contract = DeployedContract(
ContractAbi.fromJson(contractABI, "MedicalRecords"), _contractAddress);
final function = contract.function('addRecord');
final response = await _client.sendTransaction(
credentials,
Transaction.callContract(
contract: contract,
function: function,
parameters: [patientId, patientName, dob, bloodType],
),
chainId: 44787,
);
while (true) {
final receipt = await _client.getTransactionReceipt(response);
if (receipt != null) {
print('Transaction successful');
print(receipt);
break;
}
// Wait for a while before polling again
await Future.delayed(const Duration(seconds: 2));
}
return response;
}
Future<List> getRecords(String id) async {
try {
final contract = DeployedContract(
ContractAbi.fromJson(contractABI, "MedicalRecords"),
_contractAddress);
final function = contract.function('getRecords');
final response = await _client.call(
contract: contract,
function: function,
params: [id],
);
return response[0];
} catch (e) {
rethrow;
}
}
}
This Dart class named Web3Helper
sets up a connection to the Celo Alfajores Testnet and interacts with the MedicalRecords
smart contract that we defined previously.
import
statements: These are the libraries that our class relies on. We are using thehttp
library to make network requests andweb3dart
to interact with the Ethereum network (Celo is compatible with Ethereum).class Web3Helper
: This is our main class for interacting with the Celo network and our contract.late Web3Client _client
: Here we declare a late-initializedWeb3Client
instance. This object will allow us to connect to the Celo network.Web3Helper()
: This is the constructor for our class. We create an HTTP client and then initialize the_client
with the URL of the Celo Alfajores Testnet._contractAddress
andcredentials
: Here we declare the address of our deployed contract and our wallet credentials. We use these to tell theWeb3Client
where our contract is and to sign transactions.abi
andcontractABI
: These variables hold the Application Binary Interface (ABI) of our smart contract. The ABI is a JSON string that describes the contract’s methods and variables. It’s generated when the contract is compiled.addRecord
method: This asynchronous function interacts with theaddRecord
function in our smart contract. It sends a transaction to the contract with the provided parameters (patientId
,patientName
,dob
,bloodType
). After sending the transaction, it waits for the transaction receipt to ensure the transaction was successful.getRecords
method: This function calls thegetRecords
function in our smart contract. It returns the patient’s records for the givenid
.
This code provides a practical example of how to interact with a smart contract on the Celo network. The addRecord
and getRecords
methods demonstrate how to call contract functions, which can be extrapolated to any function in a smart contract.
Remember to handle any potential errors and avoid hardcoding sensitive information like private keys in a real-world production application.
When you debug the app to your Emulator or your device, you should have something of this form:
-
Add Patient Screen:
-
Get Record Screen:
-
A presentation of how it works:
Conclusion
In this tutorial, we successfully created a Celo-Based Medical Records Management System using Flutter and Solidity. We explored essential aspects of blockchain technology, including smart contracts, and permissioned access control to ensure data privacy and security. By integrating these concepts with Flutter, we managed to build a user-friendly application for patients and healthcare providers to manage medical records seamlessly.
Next Steps
Now that you’ve mastered the basics of integrating Celo with Flutter to manage medical records, you can further enhance this system. Some potential improvements might include:
- Adding more complex querying capabilities to fetch patient records based on various parameters.
- Implementing multi-signature wallets to allow multiple stakeholders (like doctors and pharmacists) to validate changes to a patient’s records.
Keep learning and exploring the limitless opportunities blockchain technology offers!
About the Author
Qudus Olanrewaju is a proficient Mobile developer and technical writer who has a strong interest in blockchain technology and web3. He enjoys building web3 projects and leveraging his distinctive viewpoint to create engaging and insightful content. You can connect with me on Linkedin and check out my latest projects on Github