How to Design Custom Border Shape for Messages in Flutter Chat Widget?
In this article, we will learn how to design custom border shapes for incoming and outgoing chat settings using the shape property in the SfChat widget. By following the steps outlined below, you can personalize the appearance of your Flutter Chat Widget interface, making it more visually appealing and user-friendly.
Step 1: Define the List of Chat Messages
Define a list of chat messages that will be displayed in the SfChat widget. Each message will include information such as the text, the time the message was sent, the author, and an optional image in the avatar. We will use the ChatMessage class to define these messages.
In the following code snippet, we define two initial messages: one from the outgoing user and another from the incoming user.
late List<ChatMessage> _messages;
@override
void initState() {
_messages = <ChatMessage>[
ChatMessage(
text: 'Hi! How are you?',
time: DateTime.now(),
author: const ChatAuthor(
id: '8ob3-b720-g9s6-25s8',
name: 'Outgoing user name',
),
),
ChatMessage(
text: 'Fine, how about you?',
time: DateTime.now(),
author: const ChatAuthor(
id: 'a2c4-56h8-9x01-2a3d',
name: 'Incoming user name',
avatar: AssetImage('images/People_Circle2.png'),
),
),
ChatMessage(
text:
'I’ve been doing well. I’ve started working on a new project recently.',
time: DateTime.now(),
author: const ChatAuthor(
id: '8ob3-b720-g9s6-25s8',
name: 'Outgoing user name',
),
),
ChatMessage(
text:
'That sounds great! I’ve been exploring a few new frameworks myself.',
time: DateTime.now(),
author: const ChatAuthor(
id: 'a2c4-56h8-9x01-2a3d',
name: 'Incoming user name',
avatar: AssetImage('images/People_Circle2.png'),
),
),
];
super.initState();
}
Step 2: Display Messages Using the SfChat Widget
Add the SfChat widget to display messages, specifying the outgoingUser and a composer for input.
SfChat(
messages: _messages,
outgoingUser: '8ob3-b720-g9s6-25s8',
composer: const ChatComposer(
decoration: InputDecoration(
hintText: 'Type a message',
),
),
),
Step 3: Adjust Message Padding and Background Color
Set the padding property in ChatMessageSettings to define appropriate space inside the Message. As our custom shape includes a tail extending below the Message, this padding ensures there is enough space to accommodate the tail within the Message’s boundaries. And also adjust the background color for the Message using backgroundColor and texStyle to enhance the overall visual design.
SfChat(
....
incomingMessageSettings: ChatMessageSettings(
textStyle: TextStyle(color: Colors.white),
padding: EdgeInsets.only(top: 15, bottom: 30, left: 28, right: 30),
backgroundColor: Colors.green[600],
),
outgoingMessageSettings: ChatMessageSettings(
textStyle: TextStyle(color: Colors.white),
padding: EdgeInsets.only(top: 15, bottom: 30, left: 28, right: 30),
backgroundColor: Colors.deepPurple[600],
),
...
),
Step 4: Create Custom Border Shapes for Chat Messages
Define the CustomBorderShape class, which extends ShapeBorder. This allows us to customize the chat message’s border design. Additionally, define three key properties in the extended class:
borderRadius: Controls how rounded the message’s corners are. Higher values make the corners more curved.
tailHeight: Determines the height of the “tail” at the bottom of the message.
isOutgoing: Indicates whether the message is for outgoing (right-aligned) or incoming (left-aligned) messages. This helps decide the alignment and shape of the tail.
class CustomBorderShape extends ShapeBorder {
final bool isOutgoing;
final double borderRadius;
final double tailHeight;
const CustomBorderShape({
required this.isOutgoing,
this.borderRadius = 30.0,
this.tailHeight = 15.0,
});
}
Prepare the Custom Border Shape
To customize the border, we override the getOuterPath method. In the method, we subtract tailHeight from rect.bottom to allocate space for the tail within the message’s boundaries. This ensures the tail seamlessly integrates with the message shape. And to ensure the corners of the message are proportionally rounded, we calculate an adjusted radius that is constrained within safe limits based on the rectangle’s dimensions.
class CustomBorderShape extends ShapeBorder {
....
@override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
final double bottom = rect.bottom - tailHeight;
final double adjustedRadius =
borderRadius.clamp(0, (rect.width / 3).clamp(0, rect.height / 3));
}
}
Using the Path class, we create the main message shape as a rounded rectangle with the adjusted radius. To visually connect the message to the sender, we draw a tail at the bottom. The placement depends on the isOutgoing property:
class CustomBorderShape extends ShapeBorder {
final bool isOutgoing;
final double borderRadius;
final double tailHeight;
const CustomBorderShape({
required this.isOutgoing,
this.borderRadius = 30.0,
this.tailHeight = 15.0,
});
@override
EdgeInsetsGeometry get dimensions => EdgeInsets.zero;
@override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
final double bottom = rect.bottom - tailHeight;
final double adjustedRadius =
borderRadius.clamp(0, (rect.width / 3).clamp(0, rect.height / 3));
final double left = rect.left;
final double right = rect.right;
final Path path = Path();
path.addRRect(
RRect.fromLTRBAndCorners(
left,
rect.top,
right,
bottom,
topLeft: Radius.circular(adjustedRadius),
bottomLeft: Radius.circular(adjustedRadius),
topRight: Radius.circular(adjustedRadius),
bottomRight: Radius.circular(adjustedRadius),
),
);
final double tailStartX =
isOutgoing ? right - adjustedRadius : left + adjustedRadius;
final double tailEndX = isOutgoing
? right - adjustedRadius * 1.4
: left + adjustedRadius * 1.5;
path.moveTo(tailStartX, bottom);
path.quadraticBezierTo(
isOutgoing
? right - adjustedRadius * 1.2
: left + adjustedRadius * 1.2,
bottom + tailHeight * 0.5,
tailStartX,
bottom + tailHeight,
);
path.lineTo(tailEndX, bottom);
path.close();
return path;
}
@override
void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {}
@override
ShapeBorder scale(double t) => this;
@override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
// This method is not used in this implementation.
return Path();
}
}
Step 5: Implement the Custom Border Shape in SfChat
Now that we have the CustomBorderShape class, we’ll use it in the SfChat widget by setting the shape property in the ChatMessageSettings class.
SfChat(
.....
incomingMessageSettings: ChatMessageSettings(
shape: CustomBorderShape(isOutgoing: false),
),
outgoingMessageSettings: ChatMessageSettings(
shape: CustomBorderShape(isOutgoing: true)),
),
),
By following the above-provided steps, you can design custom border shapes for chat message in the SfChat widget. The shape property enables you to implement your own border design, allowing for distinct styles for both incoming and outgoing messages.
Here is the output demo:
Conclusion
I hope you enjoyed learning about how to design custom border shape for messages in Flutter Chat Widget.
You can refer to our Flutter Chat feature tour page to know about its other groundbreaking feature representations and documentation and how to quickly get started for configuration specifications.
For current customers, you can check out our components from the License and Downloads page. If you are new to Syncfusion®, you can try our 30-day free trial to check out our other controls.
If you have any queries or require clarifications, please let us know in the comments section below. You can also contact us through our support forums, Direct-Trac, or feedback portal. We are always happy to assist you!