fix svg performance issue
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run

This commit is contained in:
otsmr 2026-06-16 10:30:22 +02:00
parent 83fd99ea70
commit 9daf275310
3 changed files with 73 additions and 29 deletions

View file

@ -60,10 +60,26 @@ File avatarPNGFile(int contactId) {
return File('${avatarsDirectory.path}/$contactId.png');
}
Future<Uint8List> getUserAvatar() async {
File currentUserAvatarFile(int avatarCounter) {
final avatarsDirectory = Directory(
'${AppEnvironment.cacheDir}/avatars',
);
if (!avatarsDirectory.existsSync()) {
avatarsDirectory.createSync(recursive: true);
}
return File('${avatarsDirectory.path}/user_$avatarCounter.png');
}
Future<String?> getUserAvatar() async {
if (userService.currentUser.avatarSvg == null) {
final data = await rootBundle.load('assets/images/default_avatar.png');
return data.buffer.asUint8List();
return null;
}
final avatarCounter = userService.currentUser.avatarCounter;
final file = currentUserAvatarFile(avatarCounter);
if (file.existsSync()) {
return file.path;
}
final pictureInfo = await vg.loadPicture(
@ -76,9 +92,8 @@ Future<Uint8List> getUserAvatar() async {
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
final pngBytes = byteData!.buffer.asUint8List();
final file = avatarPNGFile(userService.currentUser.userId)
..writeAsBytesSync(pngBytes);
await file.writeAsBytes(pngBytes);
pictureInfo.picture.dispose();
return file.readAsBytesSync();
return file.path;
}

View file

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
@ -29,7 +30,7 @@ class AvatarIcon extends StatefulWidget {
class _AvatarIconState extends State<AvatarIcon> {
List<Contact> _avatarContacts = [];
String? _avatarSvg;
String? _myAvatarPath;
StreamSubscription<List<Contact>>? groupStream;
StreamSubscription<List<Contact>>? contactsStream;
@ -101,19 +102,9 @@ class _AvatarIconState extends State<AvatarIcon> {
});
} else if (widget.myAvatar) {
_userSub = userService.onUserUpdated.listen((_) {
if (mounted) {
setState(() {
if (userService.currentUser.avatarSvg != null) {
_avatarSvg = userService.currentUser.avatarSvg;
} else {
_avatarContacts = [];
}
unawaited(_updateMyAvatar());
});
}
});
if (userService.currentUser.avatarSvg != null) {
_avatarSvg = userService.currentUser.avatarSvg;
}
unawaited(_updateMyAvatar());
} else if (widget.contactId != null) {
contactStream = twonlyDB.contactsDao
.watchContact(widget.contactId!)
@ -127,17 +118,44 @@ class _AvatarIconState extends State<AvatarIcon> {
if (mounted) setState(() {});
}
Future<void> _updateMyAvatar() async {
final avatarSvg = userService.currentUser.avatarSvg;
if (avatarSvg == null) {
if (mounted) {
setState(() {
_myAvatarPath = null;
_avatarContacts = [];
});
}
return;
}
final path = await getUserAvatar();
if (mounted) {
setState(() {
_myAvatarPath = path;
});
}
}
@override
Widget build(BuildContext context) {
final proSize = (widget.fontSize == null) ? 40 : (widget.fontSize! * 2);
Widget avatars = Container();
if (_avatarSvg != null) {
avatars = SvgPicture.string(
_avatarSvg!,
if (widget.myAvatar) {
if (_myAvatarPath != null) {
avatars = Image.file(
File(_myAvatarPath!),
errorBuilder: errorBuilder,
);
} else {
avatars = const SvgPicture(
AssetBytesLoader('assets/images/default_avatar.svg.vec'),
);
}
} else if (_avatarContacts.length == 1) {
avatars = getAvatarForContact(_avatarContacts.first);
} else if (_avatarContacts.length >= 2) {

View file

@ -1,5 +1,6 @@
import 'dart:typed_data';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:twonly/src/utils/avatars.dart';
import 'package:twonly/src/utils/misc.dart';
@ -32,11 +33,20 @@ class _ProfileQrCodeCompState extends State<ProfileQrCodeComp> {
Future<void> _loadData() async {
final qr = await QrCodeUtils.publicProfileLink();
final avatar = widget.showAvatar ? await getUserAvatar() : null;
Uint8List? avatarBytes;
if (widget.showAvatar) {
final avatarPath = await getUserAvatar();
if (avatarPath != null) {
avatarBytes = await File(avatarPath).readAsBytes();
} else {
final data = await rootBundle.load('assets/images/default_avatar.png');
avatarBytes = data.buffer.asUint8List();
}
}
if (mounted) {
setState(() {
_qrCode = qr;
_userAvatar = avatar;
_userAvatar = avatarBytes;
_isLoading = false;
});
}
@ -53,7 +63,6 @@ class _ProfileQrCodeCompState extends State<ProfileQrCodeComp> {
child: loaded
? Container(
key: const ValueKey('qr_code_container'),
// padding: const EdgeInsets.all(3),
decoration: BoxDecoration(
color: context.color.primary,
borderRadius: BorderRadius.circular(12),
@ -79,7 +88,9 @@ class _ProfileQrCodeCompState extends State<ProfileQrCodeComp> {
borderRadius: 2,
),
gapless: false,
embeddedImage: (widget.showAvatar && _userAvatar != null) ? MemoryImage(_userAvatar!) : null,
embeddedImage: (widget.showAvatar && _userAvatar != null)
? MemoryImage(_userAvatar!)
: null,
embeddedImageStyle: QrEmbeddedImageStyle(
size: const Size(60, 66),
embeddedImageShape: EmbeddedImageShape.square,