refactor: Performance boost for avatar widget
This commit is contained in:
parent
9906668f1c
commit
58577bb9e8
2 changed files with 154 additions and 125 deletions
|
|
@ -19,7 +19,7 @@ class Avatar extends StatelessWidget {
|
|||
final IconData? icon;
|
||||
final BorderSide? border;
|
||||
|
||||
const Avatar({
|
||||
Avatar({
|
||||
this.mxContent,
|
||||
this.name,
|
||||
this.size = defaultSize,
|
||||
|
|
@ -31,122 +31,129 @@ class Avatar extends StatelessWidget {
|
|||
this.border,
|
||||
this.icon,
|
||||
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
|
||||
Widget build(BuildContext 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 presenceUserId = this.presenceUserId;
|
||||
final container = Stack(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: Material(
|
||||
color: theme.brightness == Brightness.light
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: borderRadius,
|
||||
side: border ?? BorderSide.none,
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: noPic
|
||||
? textWidget
|
||||
: 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)
|
||||
PresenceBuilder(
|
||||
client: client,
|
||||
userId: presenceUserId,
|
||||
builder: (context, presence) {
|
||||
if (presence == null ||
|
||||
(presence.presence == PresenceType.offline &&
|
||||
presence.lastActiveTimestamp == null)) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final dotColor = presence.presence.isOnline
|
||||
? Colors.green
|
||||
: presence.presence.isUnavailable
|
||||
? Colors.orange
|
||||
: Colors.grey;
|
||||
return Positioned(
|
||||
bottom: -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(
|
||||
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,
|
||||
child: Stack(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: Material(
|
||||
color: theme.brightness == Brightness.light
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: borderRadius,
|
||||
side: border ?? BorderSide.none,
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: noPic
|
||||
? Container(
|
||||
color: textColor,
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
fallbackLetters,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: (size / 3).roundToDouble(),
|
||||
),
|
||||
),
|
||||
)
|
||||
: 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)
|
||||
PresenceBuilder(
|
||||
client: client,
|
||||
userId: presenceUserId,
|
||||
builder: (context, presence) {
|
||||
if (presence == null ||
|
||||
(presence.presence == PresenceType.offline &&
|
||||
presence.lastActiveTimestamp == null)) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final dotColor = presence.presence.isOnline
|
||||
? Colors.green
|
||||
: presence.presence.isUnavailable
|
||||
? Colors.orange
|
||||
: Colors.grey;
|
||||
return Positioned(
|
||||
bottom: -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(
|
||||
width: 10,
|
||||
height: 10,
|
||||
decoration: BoxDecoration(
|
||||
color: dotColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: theme.colorScheme.surface,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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:matrix/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 String? userId;
|
||||
final Client? client;
|
||||
|
|
@ -17,21 +19,41 @@ class PresenceBuilder extends StatelessWidget {
|
|||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final userId = this.userId;
|
||||
if (userId == null) return builder(context, null);
|
||||
|
||||
final client = this.client ?? Matrix.of(context).client;
|
||||
return FutureBuilder<CachedPresence>(
|
||||
future: client.fetchCurrentPresence(userId),
|
||||
builder: (context, cachedPresenceSnapshot) => StreamBuilder(
|
||||
stream: client.onPresenceChanged.stream
|
||||
.where((cachedPresence) => cachedPresence.userid == userId),
|
||||
builder: (context, snapshot) => builder(
|
||||
context,
|
||||
snapshot.data ?? cachedPresenceSnapshot.data,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
State<PresenceBuilder> createState() => _PresenceBuilderState();
|
||||
}
|
||||
|
||||
class _PresenceBuilderState extends State<PresenceBuilder> {
|
||||
CachedPresence? _presence;
|
||||
StreamSubscription<CachedPresence>? _sub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final client = widget.client ?? Matrix.of(context).client;
|
||||
final userId = widget.userId;
|
||||
if (userId != null) {
|
||||
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…
Add table
Reference in a new issue