refactor: Performance boost for avatar widget

pull/1462/head
Krille 1 year ago
parent 9906668f1c
commit 58577bb9e8
No known key found for this signature in database
GPG Key ID: E067ECD60F1A0652

@ -19,7 +19,7 @@ class Avatar extends StatelessWidget {
final IconData? icon; final IconData? icon;
final BorderSide? border; final BorderSide? border;
const Avatar({ Avatar({
this.mxContent, this.mxContent,
this.name, this.name,
this.size = defaultSize, this.size = defaultSize,
@ -31,122 +31,129 @@ class Avatar extends StatelessWidget {
this.border, this.border,
this.icon, this.icon,
super.key, super.key,
}); }) : fallbackLetters = name?.firstTwoCharsOrFallback ?? '@',
textColor = name?.lightColorAvatar,
noPic = mxContent == null ||
mxContent.toString().isEmpty ||
mxContent.toString() == 'null';
final String fallbackLetters;
final Color? textColor;
final bool noPic;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
var fallbackLetters = '@';
final name = this.name;
if (name != null) {
if (name.runes.length >= 2) {
fallbackLetters = String.fromCharCodes(name.runes, 0, 2);
} else if (name.runes.length == 1) {
fallbackLetters = name;
}
}
final noPic = mxContent == null ||
mxContent.toString().isEmpty ||
mxContent.toString() == 'null';
final textColor = name?.lightColorAvatar;
final textWidget = Container(
color: textColor,
alignment: Alignment.center,
child: Text(
fallbackLetters,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: (size / 3).roundToDouble(),
),
),
);
final borderRadius = this.borderRadius ?? BorderRadius.circular(size / 2); final borderRadius = this.borderRadius ?? BorderRadius.circular(size / 2);
final presenceUserId = this.presenceUserId; final presenceUserId = this.presenceUserId;
final container = Stack(
children: [ return InkWell(
SizedBox( onTap: onTap,
width: size, borderRadius: borderRadius,
height: size, child: Stack(
child: Material( children: [
color: theme.brightness == Brightness.light SizedBox(
? Colors.white width: size,
: Colors.black, height: size,
shape: RoundedRectangleBorder( child: Material(
borderRadius: borderRadius, color: theme.brightness == Brightness.light
side: border ?? BorderSide.none, ? Colors.white
), : Colors.black,
clipBehavior: Clip.hardEdge, shape: RoundedRectangleBorder(
child: noPic borderRadius: borderRadius,
? textWidget side: border ?? BorderSide.none,
: MxcImage( ),
client: client, clipBehavior: Clip.hardEdge,
key: ValueKey(mxContent.toString()), child: noPic
cacheKey: '${mxContent}_$size', ? Container(
uri: mxContent, color: textColor,
fit: BoxFit.cover, alignment: Alignment.center,
width: size, child: Text(
height: size, fallbackLetters,
placeholder: (_) => Center( style: TextStyle(
child: Icon( color: Colors.white,
Icons.person_2, fontWeight: FontWeight.bold,
color: theme.colorScheme.tertiary, fontSize: (size / 3).roundToDouble(),
size: size / 1.5, ),
),
)
: MxcImage(
client: client,
key: ValueKey(mxContent.toString()),
cacheKey: '${mxContent}_$size',
uri: mxContent,
fit: BoxFit.cover,
width: size,
height: size,
placeholder: (_) => Center(
child: Icon(
Icons.person_2,
color: theme.colorScheme.tertiary,
size: size / 1.5,
),
), ),
), ),
), ),
), ),
), if (presenceUserId != null)
if (presenceUserId != null) PresenceBuilder(
PresenceBuilder( client: client,
client: client, userId: presenceUserId,
userId: presenceUserId, builder: (context, presence) {
builder: (context, presence) { if (presence == null ||
if (presence == null || (presence.presence == PresenceType.offline &&
(presence.presence == PresenceType.offline && presence.lastActiveTimestamp == null)) {
presence.lastActiveTimestamp == null)) { return const SizedBox.shrink();
return const SizedBox.shrink(); }
} final dotColor = presence.presence.isOnline
final dotColor = presence.presence.isOnline ? Colors.green
? Colors.green : presence.presence.isUnavailable
: presence.presence.isUnavailable ? Colors.orange
? Colors.orange : Colors.grey;
: Colors.grey; return Positioned(
return Positioned( bottom: -3,
bottom: -3, right: -3,
right: -3,
child: Container(
width: 16,
height: 16,
decoration: BoxDecoration(
color: presenceBackgroundColor ?? theme.colorScheme.surface,
borderRadius: BorderRadius.circular(32),
),
alignment: Alignment.center,
child: Container( child: Container(
width: 10, width: 16,
height: 10, height: 16,
decoration: BoxDecoration( decoration: BoxDecoration(
color: dotColor, color:
borderRadius: BorderRadius.circular(16), presenceBackgroundColor ?? theme.colorScheme.surface,
border: Border.all( borderRadius: BorderRadius.circular(32),
width: 1, ),
color: theme.colorScheme.surface, alignment: Alignment.center,
child: Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: dotColor,
borderRadius: BorderRadius.circular(16),
border: Border.all(
width: 1,
color: theme.colorScheme.surface,
),
), ),
), ),
), ),
), );
); },
}, ),
), ],
], ),
);
if (onTap == null) return container;
return InkWell(
onTap: onTap,
borderRadius: borderRadius,
child: container,
); );
} }
} }
extension on String {
String get firstTwoCharsOrFallback {
var fallbackLetters = '@';
if (runes.length >= 2) {
fallbackLetters = String.fromCharCodes(runes, 0, 2);
} else if (runes.length == 1) {
fallbackLetters = this;
}
return fallbackLetters;
}
}

@ -1,10 +1,12 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
class PresenceBuilder extends StatelessWidget { class PresenceBuilder extends StatefulWidget {
final Widget Function(BuildContext context, CachedPresence? presence) builder; final Widget Function(BuildContext context, CachedPresence? presence) builder;
final String? userId; final String? userId;
final Client? client; final Client? client;
@ -17,21 +19,41 @@ class PresenceBuilder extends StatelessWidget {
}); });
@override @override
Widget build(BuildContext context) { State<PresenceBuilder> createState() => _PresenceBuilderState();
final userId = this.userId; }
if (userId == null) return builder(context, null);
class _PresenceBuilderState extends State<PresenceBuilder> {
final client = this.client ?? Matrix.of(context).client; CachedPresence? _presence;
return FutureBuilder<CachedPresence>( StreamSubscription<CachedPresence>? _sub;
future: client.fetchCurrentPresence(userId),
builder: (context, cachedPresenceSnapshot) => StreamBuilder( @override
stream: client.onPresenceChanged.stream void initState() {
.where((cachedPresence) => cachedPresence.userid == userId), final client = widget.client ?? Matrix.of(context).client;
builder: (context, snapshot) => builder( final userId = widget.userId;
context, if (userId != null) {
snapshot.data ?? cachedPresenceSnapshot.data, WidgetsBinding.instance.addPostFrameCallback((_) async {
), final presence = await client.fetchCurrentPresence(userId);
), setState(() {
); _presence = presence;
_sub = client.onPresenceChanged.stream.listen((presence) {
if (!mounted) return;
setState(() {
_presence = presence;
});
});
});
});
}
super.initState();
} }
@override
void dispose() {
_sub?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) => widget.builder(context, _presence);
} }

Loading…
Cancel
Save