Files
tatuchat/lib/widgets/avatar.dart
Welton Moura 0037b0b9cf
Some checks failed
Main Deploy Workflow / deploy_web (push) Has been cancelled
Main Deploy Workflow / deploy_playstore_internal (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled
Initial commit
2026-09-11 15:59:56 -03:00

171 lines
5.5 KiB
Dart

// SPDX-FileCopyrightText: 2019-Present Christian Kußowski
// SPDX-FileCopyrightText: 2019-Present Contributors to FluffyChat
//
// SPDX-License-Identifier: AGPL-3.0-or-later
import 'package:tatchat/config/tatu_palette.dart';
import 'package:tatchat/utils/string_color.dart';
import 'package:tatchat/widgets/mxc_image.dart';
import 'package:tatchat/widgets/presence_builder.dart';
import 'package:material_ui/material_ui.dart';
import 'package:matrix/matrix.dart';
class Avatar extends StatelessWidget {
final Uri? mxContent;
final String? name;
final double size;
final void Function()? onTap;
static const double defaultSize = 48;
final Client? client;
final String? presenceUserId;
final Color? presenceBackgroundColor;
final BorderRadius? borderRadius;
final IconData? icon;
final ShapeBorder? shapeBorder;
final Color? backgroundColor;
final Color? textColor;
const Avatar({
this.mxContent,
this.name,
this.size = defaultSize,
this.onTap,
this.client,
this.presenceUserId,
this.presenceBackgroundColor,
this.borderRadius,
this.shapeBorder,
this.icon,
this.backgroundColor,
this.textColor,
super.key,
});
String _calcFallbackLetters() {
final name = this.name?.trim();
if (name == null || name.isEmpty) return '@';
final words = name.split(' ');
if (words.length <= 1) return name.substring(0, 1);
return '${words.first.substring(0, 1)}${words.last.substring(0, 1)}';
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final fallbackLetters = _calcFallbackLetters();
final noPic =
mxContent == null ||
mxContent.toString().isEmpty ||
mxContent.toString() == 'null';
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:
shapeBorder ??
RoundedSuperellipseBorder(
borderRadius: borderRadius,
side: BorderSide.none,
),
clipBehavior: Clip.antiAlias,
child: MxcImage(
client: client,
borderRadius: borderRadius,
key: ValueKey(mxContent.toString()),
cacheKey: '${mxContent}_$size',
uri: mxContent,
fit: BoxFit.cover,
width: size,
height: size,
placeholder: (_) => noPic
? Container(
decoration: BoxDecoration(
color:
backgroundColor ??
fallbackLetters.colorScheme.primaryContainer,
),
alignment: Alignment.center,
child: Text(
fallbackLetters,
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'RobotoMono',
color:
textColor ??
fallbackLetters.colorScheme.onPrimaryContainer,
fontWeight: FontWeight.bold,
fontSize: (size / 2.5).roundToDouble(),
),
),
)
: 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
? TatuPalette.brandEmerald
: 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 MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(onTap: onTap, child: container),
);
}
}