refactor: Performance boost for avatar widget
This commit is contained in:
parent
9906668f1c
commit
58577bb9e8
2 changed files with 148 additions and 119 deletions
|
|
@ -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: [
|
|
||||||
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(
|
return InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
borderRadius: borderRadius,
|
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: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…
Add table
Add a link
Reference in a new issue