feat: Use dynamic gradient for chat bubbles

This commit is contained in:
Krille 2025-02-05 15:28:27 +01:00
commit 480513c708
No known key found for this signature in database
GPG key ID: E067ECD60F1A0652
2 changed files with 191 additions and 103 deletions

View file

@ -152,6 +152,7 @@ class ChatEventList extends StatelessWidget {
nextEvent: i + 1 < events.length ? events[i + 1] : null, nextEvent: i + 1 < events.length ? events[i + 1] : null,
previousEvent: i > 0 ? events[i - 1] : null, previousEvent: i > 0 ? events[i - 1] : null,
wallpaperMode: hasWallpaper, wallpaperMode: hasWallpaper,
scrollController: controller.scrollController,
), ),
); );
}, },

View file

@ -1,3 +1,5 @@
import 'dart:ui' as ui;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -36,6 +38,7 @@ class Message extends StatelessWidget {
final bool animateIn; final bool animateIn;
final void Function()? resetAnimateIn; final void Function()? resetAnimateIn;
final bool wallpaperMode; final bool wallpaperMode;
final ScrollController scrollController;
const Message( const Message(
this.event, { this.event, {
@ -54,6 +57,7 @@ class Message extends StatelessWidget {
this.animateIn = false, this.animateIn = false,
this.resetAnimateIn, this.resetAnimateIn,
this.wallpaperMode = false, this.wallpaperMode = false,
required this.scrollController,
super.key, super.key,
}); });
@ -323,6 +327,16 @@ class Message extends StatelessWidget {
borderRadius: borderRadius, borderRadius: borderRadius,
), ),
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: BubbleBackground(
colors: [
theme.brightness == Brightness.light
? theme.colorScheme.tertiary
: theme.colorScheme
.tertiaryContainer,
color,
],
ignore: noBubble || !ownMessage,
scrollController: scrollController,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
@ -360,7 +374,8 @@ class Message extends StatelessWidget {
eventId: event eventId: event
.relationshipEventId!, .relationshipEventId!,
content: { content: {
'msgtype': 'm.text', 'msgtype':
'm.text',
'body': '...', 'body': '...',
}, },
senderId: senderId:
@ -368,8 +383,8 @@ class Message extends StatelessWidget {
type: type:
'm.room.message', 'm.room.message',
room: event.room, room: event.room,
status: status: EventStatus
EventStatus.sent, .sent,
originServerTs: originServerTs:
DateTime.now(), DateTime.now(),
); );
@ -379,7 +394,8 @@ class Message extends StatelessWidget {
bottom: 4.0, bottom: 4.0,
), ),
child: InkWell( child: InkWell(
borderRadius: ReplyContent borderRadius:
ReplyContent
.borderRadius, .borderRadius,
onTap: () => onTap: () =>
scrollToEventId( scrollToEventId(
@ -410,7 +426,8 @@ class Message extends StatelessWidget {
RelationshipTypes.edit, RelationshipTypes.edit,
)) ))
Padding( Padding(
padding: const EdgeInsets.only( padding:
const EdgeInsets.only(
top: 4.0, top: 4.0,
), ),
child: Row( child: Row(
@ -441,6 +458,7 @@ class Message extends StatelessWidget {
), ),
), ),
), ),
),
], ],
), ),
), ),
@ -574,3 +592,72 @@ class Message extends StatelessWidget {
); );
} }
} }
class BubbleBackground extends StatelessWidget {
const BubbleBackground({
super.key,
required this.scrollController,
required this.colors,
required this.ignore,
required this.child,
});
final ScrollController scrollController;
final List<Color> colors;
final bool ignore;
final Widget child;
@override
Widget build(BuildContext context) {
if (ignore) return child;
return CustomPaint(
painter: BubblePainter(
repaint: scrollController,
colors: colors,
context: context,
),
child: child,
);
}
}
class BubblePainter extends CustomPainter {
BubblePainter({
required this.context,
required this.colors,
required super.repaint,
});
final BuildContext context;
final List<Color> colors;
ScrollableState? _scrollable;
@override
void paint(Canvas canvas, Size size) {
final scrollable = _scrollable ??= Scrollable.of(context);
final scrollableBox = scrollable.context.findRenderObject() as RenderBox;
final scrollableRect = Offset.zero & scrollableBox.size;
final bubbleBox = context.findRenderObject() as RenderBox;
final origin =
bubbleBox.localToGlobal(Offset.zero, ancestor: scrollableBox);
final paint = Paint()
..shader = ui.Gradient.linear(
scrollableRect.topCenter,
scrollableRect.bottomCenter,
colors,
[0.0, 1.0],
TileMode.clamp,
Matrix4.translationValues(-origin.dx, -origin.dy, 0.0).storage,
);
canvas.drawRect(Offset.zero & size, paint);
}
@override
bool shouldRepaint(BubblePainter oldDelegate) {
final scrollable = Scrollable.of(context);
final oldScrollable = _scrollable;
_scrollable = scrollable;
return scrollable.position != oldScrollable?.position;
}
}