đź”— Github

Introduction

In today’s digital age, interaction and engagement are at the heart of any successful mobile application. Whether you’re developing a social media platform, a blog reader, or an e-commerce app, providing users with a seamless and visually appealing comment section is a crucial element.

Flutter, with its rich set of widgets and powerful customization options, is the perfect tool to elevate your app’s user experience by creating an exceptional comment section. In this blog post, we’ll embark on a journey into the world of Flutter, where we’ll explore the art of designing a comment section UI that not only looks fantastic but also enhances user interaction.

By the end of this guide, you’ll be equipped with the knowledge and skills to implement a captivating comment section in your Flutter mobile app. So, whether you’re a seasoned Flutter developer or just starting your Flutter adventure, let’s dive in and transform your app’s comment section into a vibrant and engaging community hub!

Prerequisite

I’m using flutter_svg package for the profile picture on the comments. The profile pictures will be from DiceBear.

flutter_svg: ^2.0.6

Code Snippet

To implement a robust comment system in Flutter, we often need a well-structured data model to manage comment data effectively. In this blog post, we’ll explore two essential classes, Comments and Reply, designed to do just that.

The Comments Class

class Comments {
  final String userName;
  final String imageURL;
  final String content;
  final String type;
  final String id;
  final List<Reply> replies;

  Comments(
    this.replies, {
    required this.userName,
    required this.imageURL,
    required this.content,
    required this.type,
    required this.id,
  });
}

The Comments class represents top-level comments. It includes properties such as userName (the commenter’s name), imageURL (the commenter’s avatar URL), content (the comment text or content URL), type (indicating whether it’s text or a GIF), and more. Crucially, it has a List of Reply objects, enabling nested comments.

The Reply Class

class Reply {
  final String userName;
  final String imageURL;
  final String content;
  final String type;
  final String id;

  Reply({
    required this.userName,
    required this.imageURL,
    required this.content,
    required this.type,
    required this.id,

  });
}

The Reply class represents individual replies to comments, and it shares similar properties with Comments. This structured approach allows for consistency and easy management of comment data, including usernames, content and more.

The ParentComment Widget

class ParentComment extends StatelessWidget {
  final Comment comment;

  ParentComment({required this.comment});

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Expanded(
          flex: 1,
          child: CircleAvatar(
            child: SvgPicture.network(
              comment.imageURL,
            ),
          ),
        ),
        Expanded(
          flex: 8,
          child: ListTile(
            trailing: IconButton(
                onPressed: () {}, icon: Icon(Icons.favorite_border_rounded)),
            dense: true,
            title: Text(
              comment.userName,
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            subtitle: comment.type == "text"
                ? Text(comment.content)
                : ClipRRect(
                    borderRadius: BorderRadius.circular(12),
                    child: Image.network(comment.content),
                  ),
          ),
        ),
      ],
    );
  }
}

The ParentComment widget is designed to display top-level comments. It takes a Comment object as a parameter and constructs a visual representation of that comment. Key features of this widget include:

  • Displaying the commenter’s avatar using a CircleAvatar.
  • Showing the username and content of the comment.
  • Providing a like button (currently non-functional).

The ReplyComment Widget

class ReplyComment extends StatelessWidget {
  final Reply reply;

  ReplyComment({required this.reply});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Expanded(
              flex: 1,
              child: CircleAvatar(
                child: SvgPicture.network(
                  reply.imageURL,
                ),
              ),
            ),
            Expanded(
              flex: 8,
              child: ListTile(
                trailing: IconButton(
                    onPressed: () {},
                    icon: Icon(Icons.favorite_border_rounded)),
                dense: true,
                title: Text(
                  reply.userName,
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
                subtitle: reply.type == "text"
                    ? Text(reply.content)
                    : ClipRRect(
                        borderRadius: BorderRadius.circular(12),
                        child: Image.network(reply.content),
                      ),
              ),
            ),
          ],
        ),
      ],
    );
  }
}

The ReplyComment widget is similar to ParentComment but is designed for displaying replies to comments. It takes a Reply object as a parameter and constructs a visual representation of that reply. Key features include:

  • Displaying the replier’s avatar.
  • Showing the username and content of the reply.
  • Providing a like button (also currently non-functional).

The CommentItem Class

class CommentItem extends StatefulWidget {
  final Comment comment;
  const CommentItem({Key? key, required this.comment});

  @override
  State<CommentItem> createState() => _CommentItemState();
}

class _CommentItemState extends State<CommentItem> {
  bool showReply = false;

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: [
          ParentComment(comment: widget.comment),
          if (!showReply)
            if (widget.comment.replies.isNotEmpty)
              TextButton(
                onPressed: () {
                  setState(() {
                    showReply = true;
                  });
                },
                child: Text("Show ${widget.comment.replies.length} Replies"),
              ),
          if (showReply)
            ...widget.comment.replies.map((reply) {
              return Column(
                children: [
                  Row(
                    children: [
                      Expanded(
                        child: Container(),
                        flex: 1,
                      ),
                      Expanded(
                        child: ReplyComment(reply: reply),
                        flex: 8,
                      ),
                    ],
                  ),
                ],
              );
            }).toList(),
          Divider(),
        ],
      ),
    );
  }
}

How Nested Comments Work

  • The CommentItem widget maintains a state (showReply) to control the display of replies.
  • When a user taps the “Show X Replies” button, it toggles the showReply state, revealing the replies.
  • Replies are iterated through and displayed using the ReplyComment widget within a Column.

Example Data

Comment(
      userName: "Jhanarthananraja",
      imageURL:
          "https://api.dicebear.com/7.x/big-smile/svg?seed=Jhanarthananraja",
      content:
          "This is a test comments! This is a test comments! This is a test comments! This is a test comments!",
      type: "text",
      id: "msg-001",
      replies: [
        Reply(
          userName: "John Doe",
          imageURL:
              "https://api.dicebear.com/7.x/big-smile/svg?seed=JohnDoe",
          content: "https://media.giphy.com/media/TmOpUpxHEtW7ZtdvUc/giphy.gif",
          type: "gif",
          id: "msg-002",
        ),
        Reply(
          userName: "Mary Jane",
          imageURL:
              "https://api.dicebear.com/7.x/big-smile/svg?seed=MaryJane",
          content:
              "This is a test reply",
          type: "text",
          id: "msg-003",
        ),
      ],
    ),

Screenshot

Screenshot

Full Code

The full code is available on my GitHub