Upload and Update Images in ImageKit with Flutter BLoC
Profile pictures are one of the most personal parts of any app—but handling uploads, updates, and deletions efficiently?
That’s a whole different story.
Today, we’re integrating ImageKit.io to make profile picture management seamless in Connect.
Instead of just storing an image URL, we’re now tracking image IDs—making it easier to update and delete images without leaving orphan files behind.
GitHub Link: https://github.com/maitry4/Connect/
Upload and Update Images in ImageKit with Flutter BLoC
Contents
Step 1: Add File ID In The User Profile
As we are working with imagekit and we can only delete images using their ids via APIs.
(This can be done the other way without holding this inside the database. But as of now let’s keep it this way only.)
We will need to add a file id in the Profile User
lib/features/profile/domain/entities/profile_user.dart
➡️Click To View The Modified Code
import 'package:connect/features/auth/domain/entities/app_user.dart';
class CProfileUser extends CAppUser {
final String bio;
final String profileImageUrl;
final String? profileImageId; // Added fileId
CProfileUser({
...
this.profileImageId,
});
// Method to update profile user
CProfileUser copyWith({String? newBio, String? newProfileImageUrl, String? newProfileImageId}) {
return CProfileUser(
...
profileImageId: newProfileImageId ?? profileImageId,
);
}
// Convert profile user -> json
@override
Map<String, dynamic> toJson() {
...
'profileImageId': profileImageId,
};
}
factory CProfileUser.fromJson(Map<String, dynamic> jsonUser) {
return CProfileUser(
...
profileImageId: jsonUser['profileImageId'],
);
}
}
Step 2: Create an ImageKit Repository
Private API Key
Create lib/config/api_keys.dart
In here create a string variable image_kit_private_api
add your private api key here.
In the gitignore file add this line at bottom:
lib/config/api_keys.dart
Upload and Update Images in ImageKit with Flutter BLoC Upload and Update Images in ImageKit with Flutter BLoC Upload and Update Images in ImageKit with Flutter BLoC Upload and Update Images in ImageKit with Flutter BLoC Upload and Update Images in ImageKit with Flutter BLoC Upload and Update Images in ImageKit with Flutter BLoC
Repository For ImageKit Data Storage
Go to the lib/features/profile/data/
And create imagekit_repo.dart
➡️Click To View imagekit_repo.dart
Code
import 'dart:convert';
import 'dart:io';
import 'package:connect/config/api_keys.dart';
import 'package:dio/dio.dart';
class ImageKitRepo {
final Dio _dio = Dio();
final String _uploadUrl = "https://upload.imagekit.io/api/v1/files/upload";
final String _privateApiKey = image_kit_private_api;
Future<Map<String, String>?> uploadImage(File file) async {
try {
FormData formData = FormData.fromMap({
"file": await MultipartFile.fromFile(file.path),
"fileName": file.path.split('/').last,
"folder": "/user_profiles",
});
Response response = await _dio.post(
_uploadUrl,
data: formData,
options: Options(
headers: {
"Authorization": "Basic ${base64Encode(utf8.encode("$_privateApiKey:"))}"
},
),
);
if (response.statusCode == 200) {
return {
"url": response.data["url"],
"fileId": response.data["fileId"],
};
}
} catch (e) {
print("ImageKit Upload Error: $e");
}
return null;
}
Future<void> deleteImage(String fileId) async {
try {
Response response = await _dio.delete(
"https://api.imagekit.io/v1/files/$fileId",
options: Options(
headers: {
"Authorization": "Basic ${base64Encode(utf8.encode("$_privateApiKey:"))}"
},
),
);
if (response.statusCode == 204) {
print("Image deleted successfully.");
} else {
throw Exception("Failed to delete image.");
}
} catch (e) {
print("ImageKit Delete Error: $e");
}
}
Future<String?> getFileId(String imageUrl) async {
try {
Response response = await _dio.get(
"https://api.imagekit.io/v1/files/",
queryParameters: {"searchQuery": imageUrl},
options: Options(
headers: {
"Authorization": "Basic ${base64Encode(utf8.encode("$_privateApiKey:"))}"
},
),
);
if (response.statusCode == 200) {
List files = response.data;
for (var file in files) {
if (file["url"] == imageUrl) {
return file["fileId"];
}
}
}
} catch (e) {
print("Error fetching file ID: $e");
}
return null;
}
}
➡️Click To View This Code’s Explanation
1. The Three Variables:
Dio _dio = Dio();
- This initializes an instance of
Dio
, which is a powerful HTTP client in Dart used for making API requests.
- This initializes an instance of
String _uploadUrl = "https://upload.imagekit.io/api/v1/files/upload";
- This holds the API endpoint for uploading images to ImageKit.
String _privateApiKey = image_kit_private_api;
- This stores the private API key (imported from
api_keys.dart
) used for authentication when making requests to ImageKit.
- This stores the private API key (imported from
2. uploadImage
Method:
- This method takes a
File
object as input and uploads it to ImageKit. - Steps:
- Creates
FormData
with the file, filename, and target folder (/user_profiles
). - Sends a
POST
request to_uploadUrl
with authentication usingBasic
authorization. - If successful (HTTP 200), it returns a map containing the image URL and file ID.
- If an error occurs, it prints an error message and returns
null
.
- Creates
3. deleteImage
Method:
- This method deletes an image from ImageKit using its
fileId
. - Steps:
- Sends a
DELETE
request to the ImageKit API using the file ID. - If successful (HTTP 204), it prints a success message.
- If unsuccessful, it throws an exception with a failure message.
- If an error occurs during the process, it prints an error message.
- Sends a
4. getFileId
Method:
If no match is found or an error occurs, it prints an error message and returns null
.
This method retrieves the fileId
of an image using its URL.
Steps:
Sends a GET
request to ImageKit with a search query for the image URL.
If successful (HTTP 200), it loops through the response list to find a matching URL.
Returns the corresponding fileId
if found.
Step 3: Modify Profile Repo
We will need to add two new methods to the:
lib/features/profile/domain/repos/profile_repo.dart
import 'dart:io';
import 'package:connect/features/profile/domain/entities/profile_user.dart';
abstract class CProfileRepo {
Future<CProfileUser?> fetchUserProfile(String uid);
Future<void> updateProfile(CProfileUser updatedProfile);
Future<String?> uploadProfileImage(File file, String uid);
Future<void> deleteProfileImage(String imageUrl, String? imageId);
}
Upload and Update Images in ImageKit with Flutter BLoC Upload and Update Images in ImageKit with Flutter BLoC Upload and Update Images in ImageKit with Flutter BLoC Upload and Update Images in ImageKit with Flutter BLoC Upload and Update Images in ImageKit with Flutter BLoC Upload and Update Images in ImageKit with Flutter BLoC
Step 4: Modify Firebase Profile Repository
The modified code can be viewed at: https://github.com/maitry4/Connect/blob/main/lib/features/profile/data/firebase_profile_repo.dart
The modifications improve how profile images are handled in the system.
Previously, the profile only stored a profile image URL.
Now, the system also keeps track of an image ID, making it easier to manage images—especially for updates and deletions.
Additionally, new methods allow uploading and deleting profile images using ImageKit, an external image storage service.
➡️Click To View The Detailed Explanation
Changes in fetchUserProfile
and updateProfile
What’s Added?
profileImageId
is now fetched and stored.- Added error logging (
print("Error fetching user profile: $e")
).
Why?
profileImageId
is necessary for managing images (deletion, updates, etc.).- Error logging helps diagnose problems during profile retrieval.
New Methods
uploadProfileImage(File file, String uid)
What it does:
- Uploads an image to ImageKit.
- Stores the uploaded image’s URL and ID in Firestore.
- Returns the image URL.
Why?
- Allows users to upload profile images efficiently while keeping track of them for later management.
deleteProfileImage(String imageUrl, String? imageId)
What it does:
- Deletes an image from ImageKit using its ID.
- Logs an error if no ID is found.
Why?
- Ensures users can remove old profile pictures without leaving unused images stored.
Step 5: Modify The Profile cubit
The modified code can be viewed at: https://github.com/maitry4/Connect/blob/main/lib/features/profile/presentation/cubits/profile_cubit.dart
The overall purpose of the modifications in profile_cubit.dart
is to enhance profile updates by adding image upload functionality, improving efficiency with image compression, and refining error handling.
Key Enhancements:
- Profile Picture Support 📸
- Users can now update their profile image along with their bio.
- New images are uploaded, and old images are deleted to free up storage.
- Image Compression for Efficiency 🚀
- Converts images to WebP format to reduce file size before uploading.
- Helps in faster uploads and better app performance.
- Better Error Handling & Validation 🛠️
- Prevents redundant profile updates if no changes are made.
- More detailed error messages make debugging easier.
➡️Click To View The Detailed Explanation
Profile Image Upload & Update Logic
- The
updateProfile
method now acceptsFile? newProfileImage
, allowing users to update their profile picture along with their bio. - If a new image is provided, it gets compressed and uploaded using
profileRepo.uploadProfileImage
. - If the upload is successful, the
profileImageUrl
is updated; otherwise, an error is emitted.
Image Compression Before Upload
- Added
_convertToWebP(File imageFile)
method to compress images before uploading. - Uses
FlutterImageCompress
to reduce image size while maintaining quality (format: WebP, quality: 80). - Saves compressed images in a temporary directory for efficient handling.
Delete Previous Profile Image (if applicable)
- If a new image is uploaded, the old image is deleted using
profileRepo.deleteProfileImage
, preventing unnecessary storage usage.
Error Handling Improvements
Prevents unnecessary profile updates when no changes are made by checking if newBio
and newProfileImage
are both null
.
More specific error messages, such as "Failed to compress image"
or "Error updating profile: $e"
, make debugging easier.