A comprehensive Flutter package for managing comments with support for replies, likes, reporting, and user blocking. Built with a clean, extensible architecture that follows Flutter best practices and requires zero external dependencies (except provider).
Features
- ๐ฌ Threaded Comments: Full support for nested replies and comment threads
- ๐ Like System: Like/unlike functionality with count display
- ๐ก๏ธ Content Validation: Built-in validation for bad words, dangerous content, and length limits
- ๐จ Reporting System: Report comments and users with customizable reasons
- ๐ซ User Blocking: Block users functionality with optional time-based restrictions
- ๐ Internationalization: Support for multiple languages (English, Italian) with extensible localization
- ๐จ Highly Customizable: Optional theme, text styles, and app actions customization
- ๐ง Feature Flags: Flexible feature and insight system for dynamic configuration
- ๐ฆ Clean Architecture: Service-based architecture with dependency injection support
- ๐งช Well Tested: Comprehensive unit tests included
Installation
Add cdx_comments to your pubspec.yaml:
dependencies:
cdx_comments: ^0.0.1
provider: ^6.1.5+1
flutter_localizations:
sdk: flutter
Then run:
flutter pub get
Quick Start
1. Setup Localization
Add the localization delegates to your MaterialApp:
import 'package:cdx_comments/cdx_comments.dart';
MaterialApp(
localizationsDelegates: [
...CdxCommentsLocalizations.localizationsDelegates,
// Add your other delegates here
],
supportedLocales: CdxCommentsLocalizations.supportedLocales,
// ... rest of your app
)
2. Implement CommentService
Implement the CommentService interface to connect with your backend:
class MyCommentService implements CommentService {
@override
Future<List<Comment>> fetchComments(
String entityId,
CommentConfig config,
int page,
) async {
// Fetch comments from your API
final response = await http.get('/api/comments/$entityId?page=$page');
// Parse and return comments
return parseComments(response.body);
}
@override
Future<Comment?> postComment(Comment comment, CommentConfig config) async {
// Post comment to your API
final response = await http.post('/api/comments', body: comment.toJson());
return Comment.fromJson(response.body);
}
@override
Future<Comment?> postReply(Comment comment) async {
// Post reply to your API
final response = await http.post('/api/replies', body: comment.toJson());
return Comment.fromJson(response.body);
}
@override
Future<Comment?> toggleLikeComment(String commentId) async {
// Toggle like on your API
final response = await http.post('/api/comments/$commentId/like');
return Comment.fromJson(response.body);
}
@override
Future<void> deleteComment(String commentId) async {
// Delete comment on your API
await http.delete('/api/comments/$commentId');
}
@override
Future<List<Comment>> fetchReplies(String commentId, int page) async {
// Fetch replies from your API
final response = await http.get('/api/comments/$commentId/replies?page=$page');
return parseComments(response.body);
}
@override
Future<void> sendReportComment(String commentId, String reasonId) async {
await http.post('/api/comments/$commentId/report', body: {'reason': reasonId});
}
@override
Future<void> sendReportUser(String userId) async {
await http.post('/api/users/$userId/report');
}
@override
Future<void> sendBlockUser(String userId) async {
await http.post('/api/users/$userId/block');
}
}
3. Implement FeatureChecker
Control which features and insights are enabled:
class MyFeatureChecker implements FeatureChecker {
final bool commentsEnabled;
final bool likesEnabled;
MyFeatureChecker({
this.commentsEnabled = true,
this.likesEnabled = true,
});
@override
bool commentHasFeature(ModuleFeature feature) {
switch (feature) {
case ModuleFeature.comment:
return commentsEnabled;
case ModuleFeature.like:
return likesEnabled;
}
}
@override
bool commentHasInsight(ModuleInsight insight) {
switch (insight) {
case ModuleInsight.likeCount:
return likesEnabled; // Show count only if likes are enabled
}
}
}
4. Setup and Use
import 'package:cdx_comments/cdx_comments.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CommentsPage extends StatefulWidget {
final String postId;
const CommentsPage({super.key, required this.postId});
@override
State<CommentsPage> createState() => _CommentsPageState();
}
class _CommentsPageState extends State<CommentsPage> {
late final CommentService _service;
late final CommentConfig _config;
late final UserInfo _user;
late final CommentController _controller;
late final CommentValidator _validator;
late final FeatureChecker _featureChecker;
@override
void initState() {
super.initState();
// Setup services - in a real app, these might come from dependency injection
_service = MyCommentService();
_config = CommentConfig(
badWords: 'bad\nword\nlist', // Your bad words list
);
_user = UserInfo(
uuid: 'current-user-id',
name: 'Current User',
);
_controller = CommentController(
service: _service,
config: _config,
user: _user,
);
_validator = CommentValidator(badWordsData: _config.badWords);
_featureChecker = MyFeatureChecker();
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => CommentProvider(
controller: _controller,
postId: widget.postId,
validator: _validator,
),
child: Scaffold(
appBar: AppBar(title: const Text('Comments')),
body: Builder(
builder: (context) => ElevatedButton(
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (_) => CommentBottomSheet(
userBlockedUntil: null, // Set if user is blocked
featureChecker: _featureChecker,
service: _service,
user: _user,
),
);
},
child: const Text('Show Comments'),
),
),
),
);
}
}
Alternative using Dependency Injection (recommended for larger apps):
// Using a DI container (e.g., get_it, riverpod, etc.)
class CommentsPage extends StatelessWidget {
final String postId;
const CommentsPage({super.key, required this.postId});
@override
Widget build(BuildContext context) {
// Get services from DI container
final service = GetIt.instance<CommentService>();
final config = GetIt.instance<CommentConfig>();
final user = GetIt.instance<UserInfo>();
final controller = GetIt.instance<CommentController>();
final validator = GetIt.instance<CommentValidator>();
final featureChecker = GetIt.instance<FeatureChecker>();
return ChangeNotifierProvider(
create: (_) => CommentProvider(
controller: controller,
postId: postId,
validator: validator,
),
child: Scaffold(
// ... rest of the widget
),
);
}
}
Customization
Theme Customization
Customize colors and styling through the CommentsTheme interface:
class MyCustomTheme implements CommentsTheme {
@override
Color get primary => Colors.blue;
@override
Color get mainText => Colors.white;
@override
Color get mainBackground => Colors.black;
@override
Color get error => Colors.red;
@override
Color get minorText => Colors.grey;
@override
BorderRadius get cardRadius => BorderRadius.circular(20);
}
// Use it in CommentBottomSheet
CommentBottomSheet(
// ... other parameters
theme: MyCustomTheme(),
)
If not provided, the package uses DefaultCommentsTheme which automatically extracts colors from Theme.of(context).
App Actions Customization
Customize snackbars and dialogs:
class MyCustomAppActions implements CommentsAppActions {
@override
void showErrorSnackbar(BuildContext context, String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.red,
action: SnackBarAction(
label: 'OK',
onPressed: () {},
),
),
);
}
@override
void showInfoSnackbar(BuildContext context, String message) {
// Your custom implementation
}
@override
void showConfirmationDialog({
required BuildContext context,
required String title,
required String message,
required String confirmText,
required String cancelText,
required void Function(bool) onConfirm,
}) {
// Your custom dialog implementation
}
}
// Use it in CommentBottomSheet
CommentBottomSheet(
// ... other parameters
appActions: MyCustomAppActions(),
)
Text Style Customization
Customize text styles:
class MyCustomTextStyle implements CommentsTextStyle {
final BuildContext context;
const MyCustomTextStyle(this.context);
@override
TextStyle bold18({Color? color, TextAlign? align}) {
return TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: color ?? Theme.of(context).colorScheme.onSurface,
fontFamily: 'CustomFont',
);
}
// Implement other methods: normal14, normal15, normal12
}
// Use it in CommentBottomSheet
CommentBottomSheet(
// ... other parameters
textStyle: MyCustomTextStyle(context),
)
Architecture
The package follows clean architecture principles:
- Models: Immutable data classes (
Comment,UserInfo,CommentConfig,CommentsTheme, etc.) - Services: Abstract interfaces (
CommentService,FeatureChecker) for dependency inversion - Controllers: Business logic layer (
CommentController) - Providers: State management (
CommentProvider,CommentReportProvider) using Provider pattern - Validators: Input validation (
CommentValidator) - Widgets: Reusable UI components (
CommentBottomSheet,CommentTile,ReportCommentBottomSheet)
API Reference
Core Models
Comment
Represents a comment with all its properties:
id: Unique identifiercontent: Comment textuserId: Author user IDusername: Author usernamedate: Creation dateparentId: Parent comment ID (null for root comments)entityId: ID of the entity being commented onreplies: List of reply commentsreplyCount: Total number of repliesisLiked: Whether current user liked itlikeCount: Number of likesisMine: Whether comment belongs to current user
UserInfo
Current user information:
uuid: User unique identifiername: User display nameinitials: Generated initials from name
CommentConfig
Configuration for the comments module:
badWords: String containing bad words list (one per line)
Services
CommentService
Abstract interface for comment operations. You must implement:
fetchComments(): Fetch comments for an entitypostComment(): Post a new commentpostReply(): Post a replytoggleLikeComment(): Toggle like statusdeleteComment(): Delete a commentfetchReplies(): Fetch replies for a commentsendReportComment(): Report a commentsendReportUser(): Report a usersendBlockUser(): Block a user
FeatureChecker
Interface for feature/insight checking:
commentHasFeature(): Check if a feature is enabledcommentHasInsight(): Check if an insight should be shown
Widgets
CommentBottomSheet
Main comments UI widget. Parameters:
userBlockedUntil: Optional DateTime if user is blockedfeatureChecker: Feature checker implementationservice: Comment service implementationuser: Current user informationtheme: Optional custom themeappActions: Optional custom app actionstextStyle: Optional custom text styles
CommentTile
Individual comment widget. Displays comment content, actions, and replies.
ReportCommentBottomSheet
Bottom sheet for reporting comments and users.
Adding Custom Translations
To add support for additional languages:
- Create a new ARB file in
lib/l10n/(e.g.,app_fr.arb) - Copy the structure from
app_en.arb - Translate all the strings
- Regenerate localizations:
flutter gen-l10n
The delegate is extensible and can be combined with other localization delegates in your app.
Requirements
- Flutter >= 1.17.0
- Dart >= 3.10.1
providerpackage (^6.1.5+1)
Example
See the example directory for a complete working example.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Links
Libraries
- cdx_comments
- A Flutter package for managing comments with support for replies, likes, and reporting.
- controller
- l10n/app_localizations
- l10n/app_localizations_en
- l10n/app_localizations_it
- models/comment
- models/comment_config
- models/comments_app_actions
- models/comments_text_style
- models/comments_theme
- models/module_features
- models/user_info
- provider
- report/provider
- report/report
- report/sheet
- services/comment_service
- utils/date_formatter
- validator
- widgets/comment_tile
- widgets/line_divider
- widgets/sheet