How to render Custom Post UI with custom JSON?
Introduction
The LikeMinds Feed SDK allows you to enhance your feed posts with custom widgets, enabling you to display unique content types or complex data structures within your feed. This guide will walk you through the process of implementing and using custom widgets in your feed posts.
Prerequisites
Before you begin, ensure the following:
- LikeMinds Feed Flutter SDK: The SDK must be properly installed and initialized in your Flutter project. Refer to the installation guide if needed.
- Feed Enabled: Ensure that Feed is enabled on the dashboard for your project.
- Basic Understanding of Flutter Widgets: Familiarity with Flutter widgets and layout concepts.
- Knowledge of Builder Pattern: Understanding of the builder pattern in Dart, as it is used to customize and create widgets dynamically.
Steps to Implement Custom Widgets
Step 1: Understanding LM Custom Widget for a Post
A custom widget is a JSON map of custom data that you can send in a post while creation in its attachment list. It is sent as an object of LMAttachmentViewData
under the meta key in the post data.
While rendering the feed, every LMPostViewData
object has a List<LMAttachmentViewData>
which you can use to parse your custom data and associate a custom view with it. Then you can use it to further render it in the feed using the steps mentioned further in this document.
Step 2: Understanding LMAttachmentViewData
The LMAttachmentViewData
class represents media attachments for a post. It has multiple factory constructors for different scenarios. Here's a brief overview:
class LMAttachmentViewData {
final LMMediaType attachmentType;
final LMAttachmentMetaViewData attachmentMeta;
// Factory constructors
factory LMAttachmentViewData.fromAttachmentMeta({...});
factory LMAttachmentViewData.fromMediaUrl({...});
factory LMAttachmentViewData.fromMediaBytes({...});
factory LMAttachmentViewData.fromMediaPath({...});
// Method to map media type to integer
int mapMediaTypeToInt() {...}
}
The class uses different factory constructors based on how you want to create the attachment:
fromAttachmentMeta
: When you already have an LMAttachmentMetaViewData object.fromMediaUrl
: When you have a URL for the media.fromMediaBytes
: When you have the media as bytes (Uint8List).fromMediaPath
: When you have a local file path for the media.
The LMMediaType
enum provides various media types you can use:
enum LMMediaType {
none,
image, // int value 1
video, // int value 2
document, // int value 3
link, // int value 4
widget, // int value 5
repost, // int value 8 or 9
poll, // int value 6
}
When creating an attachment, make sure to specify the correct media type. To do this you can also use, but always prefer using the LMMediaType
enum.
LMAttachmentViewData attachment; //image type attachment
/// Function to map the LMMediaType enum to its int value
int attachmentType = attachment.mapMediaTypeToInt(); // 1
/// Function to map the attachment int value to its LMMediaType enum
LMMediaType type = mapIntToMediaType(attachmentType); // LMMediaType.image
Step 3: Defining Your Custom Data Class
This part is optional as you can send a generic Map<String, dynamic>
to represent your custom JSON. Otherwise, create a custom data class that represents the structure of your widget data. For example, if you're creating a health vital tracking widget, you might define a CustomHealthData
:
class CustomHealthData {
final String id;
final String name;
final int measurement;
final String description;
CustomHealthData({
required this.id,
required this.name,
required this.measurement,
required this.description,
});
toMap() => _customHealthDataToMap();
/// conversion function
factory CustomHealthData.fromLMPostViewData(LMPostViewData post){...}
}
Step 4: Writing toMap()
to start sending as attachment
Next, create a function that will convert your custom data class to a Map<String, dynamic>
as this ensures that you get back the data however you want it. Here’s how:
Map<String, dynamic> _customHealthDataToMap(){
return {
'id': this.id,
'name': this.name,
'measurement': this.measurement,
'description': this.description,
};
}
The conversion and parsing to JSON to send it to the API is handled internally.
Step 5: Writing conversion function for attachment data to your custom data class
Next, create a function that converts the LMPostViewData
to your custom data class. This function will extract the relevant data from the post's attachments and widgets:
factory CustomHealthData.fromLMPostViewData(LMPostViewData post){
// Map of widgets in the current post
Map<String, LMWidgetViewData> widgets = post.widgets ?? {};
// Checking every attachment for its type and converting
for (LMAttachmentViewData attachment in post.attachments ?? []) {
// Convert only if the attachment is of type widget
if (attachment.attachmentType == LMMediaType.widget) {
// Use the entity id in attachmentMeta to pick the widget
// object from the widgets map in the post data
final entityId = attachment.attachmentMeta.meta?['entity_id'];
if (widgets.containsKey(entityId)) {
return CustomHealthData(
id: widgets[entityId]!.id
name: widgets[entityId]!.metadata['name'],
measurement: widgets[entityId]!.metadata['measurement'],
description: widgets[entityId]!.metadata['description'],
);
}
}
}
}
Steps to Render a Custom Widget in Feed
Step 1: Create Your Custom Widget
Now, create a Flutter widget that will display your custom data. For example:
class HealthMetricWidget extends StatelessWidget {
final CustomHealthData metric;
const HealthMetricWidget({Key? key, required this.metric}) : super(key: key);
Widget build(BuildContext context) {
return Card(
child: Column(
children: [
Text(metric.name, style: Theme.of(context).textTheme.headline6),
Text(metric.description),
Row(
children: [
_getMetricIcon(metric.id),
Text(metric.measurement)
]
)
],
),
);
}
}
Step 2: Implement the LMFeedPostWidgetBuilder
Function
The LMFeedPostWidgetBuilder
allows you to customize how each post is rendered in the feed. It is a typedef of the type Widget Function(BuildContext, LMFeedPostWidget, LMPostViewData)
.
Here's how you can implement it to include your custom widget:
Widget postWithMetricWidgetBuilder(
BuildContext context,
LMFeedPostWidget postWidget,
LMPostViewData postData,
) {
final healthData = CustomHealthData.fromLMPostViewData(post)
return postWidget.copyWith(
headerBuilder: (context, headerWidget, headerData) {
return headerWidget.copyWith(
subText: LMFeedText(text: "${healthData.name} metric post"),
);
},
contentBuilder: (context, contentwidget, content) {
return HealthMetricWidget(metric: healthData);
},
);
}
Step 3: Use the Builder Function in Your Feed
Finally, use your created postWithMetricWidgetBuilder function when navigating to the feed screen after initialisation. Here is how:
MaterialPageRoute route = MaterialPageRoute(
builder: (context) => LMFeedScreen(
...
postBuilder: postWithMetricWidgetBuilder,
...
)
);
Navigator.of(context).push(route);