twonly-app/lib/src/views/components/avatar_icon.component.dart
otsmr c3e92ac7d1
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
fix avatar render issues, fix list update issues
2025-11-18 22:56:42 +01:00

201 lines
5.5 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/utils/avatars.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:vector_graphics/vector_graphics.dart';
class AvatarIcon extends StatefulWidget {
const AvatarIcon({
super.key,
this.group,
this.contactId,
this.myAvatar = false,
this.fontSize = 20,
this.color,
});
final Group? group;
final int? contactId;
final bool myAvatar;
final double? fontSize;
final Color? color;
@override
State<AvatarIcon> createState() => _AvatarIconState();
}
class _AvatarIconState extends State<AvatarIcon> {
List<Contact> _avatarContacts = [];
String? _globalUserDataCallBackId;
String? _avatarSvg;
StreamSubscription<List<Contact>>? groupStream;
StreamSubscription<List<Contact>>? contactsStream;
StreamSubscription<Contact?>? contactStream;
@override
void initState() {
initAsync();
super.initState();
}
@override
void dispose() {
groupStream?.cancel();
contactStream?.cancel();
contactsStream?.cancel();
if (_globalUserDataCallBackId != null) {
globalUserDataChangedCallBack.remove(_globalUserDataCallBackId);
}
super.dispose();
}
Widget errorBuilder(_, __, ___) {
return const SvgPicture(
AssetBytesLoader('assets/images/default_avatar.svg.vec'),
);
}
Widget getAvatarForContact(Contact contact) {
final avatarFile = avatarPNGFile(contact.userId);
if (avatarFile.existsSync()) {
return Image.file(
avatarFile,
errorBuilder: errorBuilder,
);
}
if (contact.avatarSvgCompressed != null) {
return SvgPicture.string(
getAvatarSvg(contact.avatarSvgCompressed!),
errorBuilder: errorBuilder,
);
}
return errorBuilder(null, null, null);
}
Future<void> initAsync() async {
if (widget.group != null) {
groupStream = twonlyDB.groupsDao
.watchGroupContact(widget.group!.groupId)
.listen((contacts) {
_avatarContacts = [];
if (contacts.length == 1) {
if (contacts.first.avatarSvgCompressed != null) {
_avatarContacts.add(contacts.first);
}
} else {
for (final contact in contacts) {
if (contact.avatarSvgCompressed != null) {
_avatarContacts.add(contact);
}
}
}
setState(() {});
});
} else if (widget.myAvatar) {
_globalUserDataCallBackId = 'avatar_${getRandomString(10)}';
globalUserDataChangedCallBack[_globalUserDataCallBackId!] = () {
setState(() {
if (gUser.avatarSvg != null) {
_avatarSvg = gUser.avatarSvg;
} else {
_avatarContacts = [];
}
});
};
if (gUser.avatarSvg != null) {
_avatarSvg = gUser.avatarSvg;
}
} else if (widget.contactId != null) {
contactStream = twonlyDB.contactsDao
.watchContact(widget.contactId!)
.listen((contact) {
if (contact != null && contact.avatarSvgCompressed != null) {
_avatarContacts = [contact];
setState(() {});
}
});
}
if (mounted) setState(() {});
}
@override
Widget build(BuildContext context) {
final proSize = (widget.fontSize == null) ? 40 : (widget.fontSize! * 2);
Widget avatars = Container();
if (_avatarSvg != null) {
avatars = SvgPicture.string(
_avatarSvg!,
errorBuilder: errorBuilder,
);
} else if (_avatarContacts.length == 1) {
avatars = getAvatarForContact(_avatarContacts.first);
} else if (_avatarContacts.length >= 2) {
final a = getAvatarForContact(_avatarContacts.first);
final b = getAvatarForContact(_avatarContacts[1]);
if (_avatarContacts.length >= 3) {
final c = getAvatarForContact(_avatarContacts[2]);
avatars = Stack(
children: [
Transform.translate(
offset: const Offset(-15, 5),
child: Transform.scale(
scale: 0.8,
child: c,
),
),
Transform.translate(
offset: const Offset(15, 5),
child: Transform.scale(
scale: 0.8,
child: b,
),
),
a,
],
);
} else {
avatars = Stack(
children: [
Transform.translate(
offset: const Offset(-10, 5),
child: Transform.scale(
scale: 0.8,
child: b,
),
),
Transform.translate(offset: const Offset(10, 0), child: a),
],
);
}
} else {
avatars = const SvgPicture(
AssetBytesLoader('assets/images/default_avatar.svg.vec'),
);
}
return Container(
constraints: BoxConstraints(
minHeight: 2 * (widget.fontSize ?? 20),
minWidth: 2 * (widget.fontSize ?? 20),
maxWidth: 2 * (widget.fontSize ?? 20),
maxHeight: 2 * (widget.fontSize ?? 20),
),
child: Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Container(
height: proSize as double,
width: proSize,
color: widget.color,
child: Center(child: avatars),
),
),
),
);
}
}