Skip to main content

How to Add your own Media Upload Service over LikeMinds Feed SDK?

Introduction

This guide explains how to implement media uploads and post creation as separate operations using the LikeMinds Feed SDK. Breaking these operations into distinct steps provides better control over the upload process and enables features like progress tracking and error handling.

Prerequisites

  • LikeMinds Feed SDK initialized in your project, guide here
  • Basic understanding of Dart/Flutter asynchronous programming
  • Access to LMFeedPostBloc and related models

Implementation Steps

Step 1: Create your own Media Upload Service

Create your own function of uploading media to your preferred destination. This could be an AWS bucket, Firebase Storage, or more file storage options. Our goal is to upload the media straight to the destination, and then use the link of that media while creating the post. So, create a function that uploads your chosen media, and then parses the data into LMAttachmentViewData to be sent in post creation.

Future<List<LMAttachmentViewData>> uploadMedia(){
/// create a function that uploads your media to your preferred destination
/// parse the returned data into the type of LMAttachmentViewData
}

Alternatively, you can handle the upload of media files to the LikeMinds server. The implementation includes progress tracking and proper error handling using a broadcast StreamController. The uploadMedia function returns a Future containing a list of uploaded media attachments.

Future<List<LMAttachmentViewData>> uploadMedia({
required List<LMAttachmentViewData> mediaFiles,
required LMUserViewData user,
Function(double)? onProgressUpdate,
}) async {
try {
// Create progress controller
StreamController<double> progressController = StreamController<double>.broadcast();

// Subscribe to progress updates if needed
if (onProgressUpdate != null) {
progressController.stream.listen(onProgressUpdate);
}

// Create a completer to wait for the upload result
Completer<List<LMAttachmentViewData>> uploadCompleter = Completer();

// Subscribe to bloc states
final subscription = LMFeedPostBloc.instance.stream.listen((state) {
if (state is LMFeedMediaUploadedState) {
// Media upload completed successfully
uploadCompleter.complete(state.mediaViewData);
} else if (state is LMFeedPostErrorState) {
// Upload failed
uploadCompleter.completeError(state.errorMessage ?? 'Upload failed');
}
});

// Add upload media event to bloc
LMFeedPostBloc.instance.add(
LMFeedUploadMediaEvent(
postMedia: mediaFiles,
user: user,
progressController: progressController,
),
);

// Wait for the result
final result = await uploadCompleter.future;

// Clean up subscription
subscription.cancel();

return result;
} catch (e) {
print('Error uploading media: $e');
throw e;
}
}

Step 2: Create a Post using LMFeedPostBloc

After media upload completes, this step handles the actual post creation. The function accepts various parameters including the uploaded media attachments, post text, topics, and optional heading. It returns a Future containing the created post data.

note

The mediaAttachments parameter accepts a List<LMAttachmentViewData> based on which if there are files in those attachments, it uploads them to the media server.

Future<LMPostViewData> createPost({
required LMUserViewData user,
required String postText,
required List<LMTopicViewData> topics,
List<LMAttachmentViewData>? mediaAttachments,
String? heading,
int? feedroomId,
}) async {
try {
// Create a completer to wait for the post creation result
Completer<LMPostViewData> postCompleter = Completer();

// Subscribe to bloc states
final subscription = LMFeedPostBloc.instance.stream.listen((state) {
if (state is LMFeedNewPostUploadedState) {
// Post created successfully
postCompleter.complete(state.postData);
} else if (state is LMFeedNewPostErrorState) {
// Post creation failed
postCompleter.completeError(state.errorMessage ?? 'Post creation failed');
}
});

// Add create post event to bloc
LMFeedPostBloc.instance.add(
LMFeedCreateNewPostEvent(
user: user,
postText: postText,
heading: heading,
postMedia: mediaAttachments,
selectedTopics: topics,
feedroomId: feedroomId,
),
);

// Wait for the result
final result = await postCompleter.future;

// Clean up subscription
subscription.cancel();

return result;
} catch (e) {
print('Error creating post: $e');
throw e;
}
}

Step 3: Combining Media upload and Post Creation

This step demonstrates how to combine both functions in a practical scenario, showing the complete flow from media upload to post creation with proper error handling.

Future<void> createPostWithMedia() async {
try {
// Get your user data and topics
LMUserViewData currentUser = getCurrentUser();
List<LMTopicViewData> selectedTopics = getSelectedTopics();

// Prepare media files
List<LMAttachmentViewData> mediaFiles = [
// Your media files here
];

// Step 1: Upload media first
final uploadedMedia = await uploadMedia(
mediaFiles: mediaFiles,
user: currentUser,
onProgressUpdate: (progress) {
print('Upload progress: ${progress * 100}%');
},
);

// Step 2: Create post with uploaded media
final createdPost = await createPost(
user: currentUser,
postText: "My post text",
topics: selectedTopics,
mediaAttachments: uploadedMedia,
heading: "Optional heading",
);

print('Post created successfully with ID: ${createdPost.id}');

} catch (e) {
print('Error in post creation process: $e');
}
}

Step 4: Customize the Compose Screen (Optional)

To integrate your custom post creation function with the compose screen, you can modify the LMFeedComposeScreen widget. This is done by providing a custom composeAppbarBuilder parameter.

The composeAppbarBuilder is a function that returns an AppBar widget for the compose screen. By default, this AppBar contains a button that handles post creation. You can override this button's functionality to use your createPostWithMedia() function while still leveraging the built-in post validation using the validateUser paramter in the builder.

Conclusion

This implementation provides a robust solution for handling media uploads and post creation separately in your application. The separation of concerns allows for better error handling and progress tracking during the upload process. The modular approach makes it easier to maintain and modify the code as needed.