-
Notifications
You must be signed in to change notification settings - Fork 0
LayrzAvatar
A static display component that renders a user avatar in one of five forms: image (URL or base64), icon, emoji, or initials generated from a name. The avatar is always a rounded box using the r3 radius token (16 logical pixels), consistent with LayrzCard and LayrzAlert, and carries a fixed tokens.shadow.compact1 drop shadow in all render modes.
Metadata
Mirrors: ThemedAvatar (layrz_theme)
Phase: M2 (Core primitives)
Domain: Display
Primitive: Hand-rolled (ClipRRect + Container + Image + Icon + Text)
Status: Confirmed scope. As of 0.0.9, no longer depends on layrz_sdk.
LayrzAvatar renders a static, non-interactive avatar display. It is display-only and does not support interaction callbacks (onTap, onLongPress, etc.). Callers who need interactivity should wrap the avatar in a GestureDetector or similar widget themselves. This keeps the API minimal and separation of concerns clear.
The avatar resolves what to display using a strict fallback order:
-
Image from a named constructor (
.image()) — renders from URL or base64 -
Icon from a named constructor (
.icon()) — renders fromIconData -
Emoji from a named constructor (
.emoji()) — renders a Unicode glyph -
Initials from a named constructor (
.initials()) — renders fromnameText -
Avatar source from
LayrzAvatarSourcemodel (sourceparameter) — resolves by sealed type -
Fallback to initials — from
nameTextwhen the source is null
-
Static display only: No interaction callbacks. Wrap with
GestureDetectorif needed. -
Multiple render modes: Image (URL/base64), icon, emoji, or initials — all from a single
LayrzAvatarSource. - Smart fallback: Falls through gracefully when the source is null.
-
Fixed sizing: A single
sizeparameter defines both width and height. -
Consistent rounding: Always a rounded box using the
r3radius token (16 logical pixels), matching other surface components. - Color-aware text: Text color (for initials and emoji) is automatically contrasted against the background for readability.
-
Fixed drop shadow: Every avatar carries a fixed
tokens.shadow.compact1drop shadow in all modes. This shadow is intentionally not configurable. -
No layrz_sdk dependency: The component is fully self-contained with a native sealed hierarchy
LayrzAvatarSource.
class LayrzAvatar extends StatelessWidget {
/// Avatar source describing what to render.
///
/// When null, falls back to initials from [nameText].
final LayrzAvatarSource? source;
/// Name text from which initials are generated when [source] is null.
///
/// If null and no usable source is available, displays `"NA"` as a placeholder.
final String? nameText;
/// Width and height of the avatar in logical pixels.
///
/// Defaults to 40. The avatar is always square with rounded corners, so this
/// single value defines both dimensions.
final double size;
/// Background fill color of the avatar.
///
/// Defaults to the primary token color when null. Ignored when rendering an
/// image (images render on a white background to ensure visibility when the
/// source has transparency).
final Color? color;
/// Creates a new [LayrzAvatar].
///
/// Renders the avatar described by the [LayrzAvatarSource] object.
/// Falls back to initials from [nameText] when the source is null.
/// At least one of [source] or [nameText] should be provided for useful output.
const LayrzAvatar({
super.key,
this.source,
this.nameText,
this.size = 40,
this.color,
});
}LayrzAvatarSource is a sealed class hierarchy. Extend it to add new avatar types.
Displays an image from a URL or data-URI.
final class LayrzAvatarUrl extends LayrzAvatarSource {
/// The image URL, data-URI, or base64 string.
final String url;
const LayrzAvatarUrl(this.url);
}Displays an image from a base64-encoded string.
final class LayrzAvatarBase64 extends LayrzAvatarSource {
/// The base64-encoded image string.
final String base64;
const LayrzAvatarBase64(this.base64);
}Displays an icon from IconData.
final class LayrzAvatarIcon extends LayrzAvatarSource {
/// The icon to display.
final IconData icon;
const LayrzAvatarIcon(this.icon);
}Displays a Unicode emoji glyph.
final class LayrzAvatarEmoji extends LayrzAvatarSource {
/// The emoji string to display.
final String emoji;
const LayrzAvatarEmoji(this.emoji);
}Renders an avatar from an image source (URL or base64).
const LayrzAvatar.image({
required String imageSource,
this.size = 40,
});-
imageSource: An http(s) URL, a
data:URI, or a bare base64 string. Routed toLayrzImage. - Background: White, ensuring visibility when the image source has transparency.
Renders an avatar from an IconData glyph.
const LayrzAvatar.icon({
required IconData icon,
this.size = 40,
this.color,
});-
icon: The
IconDatato display (e.g., fromMdiIcons/ flutter_material_design_icons or other icon libraries). - Color: Defaults to the primary token color.
-
Icon size: 70% of
size.
Renders an avatar from a Unicode emoji glyph.
const LayrzAvatar.emoji({
required String emoji,
this.size = 40,
});- emoji: A single Unicode emoji or emoji sequence.
- Background: White.
-
Emoji size: 60% of
size.
Renders an avatar from generated initials.
const LayrzAvatar.initials({
required this.nameText,
this.size = 40,
this.color,
});- nameText: The name from which initials are derived.
- Color: Defaults to the primary token color.
When the main constructor receives a LayrzAvatarSource object, the sealed type determines rendering:
| Type | Behaviour | Fallback |
|---|---|---|
LayrzAvatarUrl |
Displays the image from url via LayrzImage
|
Not applicable (source holds the URL directly) |
LayrzAvatarBase64 |
Displays the image from base64 via LayrzImage
|
Not applicable (source holds the base64 directly) |
LayrzAvatarIcon |
Displays the icon from icon (native IconData, no conversion needed) |
Not applicable (source holds IconData directly) |
LayrzAvatarEmoji |
Displays the emoji from emoji
|
Not applicable (source holds the emoji directly) |
null source |
Falls back to initials from nameText
|
"NA" if nameText is null/empty |
The initials algorithm (when falling back to text):
- Strip all non-alphanumeric characters from the input name.
- If empty, display
"NA"(Not Available). - If one character, display that character.
- If two or more characters, display the first two characters in uppercase.
Known limitation: This algorithm is not Unicode-aware (combining characters, non-Latin scripts, ligatures are treated as separate characters). This is an accepted limitation, not a bug to solve. Most real-world use cases involve Latin-script names.
-
Size: A single
sizeparameter (default 40) defines both width and height. -
Shape: Always a rounded box using the
r3radius token (16 logical pixels), consistent withLayrzCard,LayrzAlert, and the dropdown panel. - Background color: Defaults to the primary token color when null. For images, always white to ensure visibility when the source has transparency.
- Text color (for initials/icons): Automatically contrasted against the background using a luminance heuristic — black text for light backgrounds, white text for dark backgrounds.
-
Fixed drop shadow: Every avatar carries
tokens.shadow.compact1as a fixed drop shadow in all render modes (initials, icon, emoji, URL/base64 image). This is the small-component shadow ramp used byLayrzButton, chosen because a soft low-offset shadow disappears at avatar sizes; compact shadows provide clear separation at 40px width. This shadow is intentionally not configurable — it is part of the avatar's baseline visual treatment.
The .image() constructor and LayrzAvatarUrl / LayrzAvatarBase64 sources route image rendering through LayrzImage. See LayrzImage for details on:
- Supported source formats (http(s) URLs, data-URIs, bare base64, asset paths)
- SVG detection and handling
- Placeholder and fallback rendering
- Base64 decoding cache and eviction policy
- LayrzAvatarInput — Input-side component for selecting an avatar from a system image picker.
- LayrzDynamicAvatarInput — Input-side component for selecting an avatar from multiple sources (URL, base64, icon, emoji).
LayrzAvatar(
source: LayrzAvatarUrl('https://cdn.layrz.com/resources/com.layrz.one/favicon/normal.png'),
nameText: 'Jane Smith', // Fallback if URL fails to load
size: 48,
)Or using the named constructor:
LayrzAvatar.image(
imageSource: 'https://cdn.layrz.com/resources/com.layrz.one/favicon/normal.png',
size: 56,
)LayrzAvatar(
source: LayrzAvatarBase64('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg=='),
size: 40,
)LayrzAvatar(
source: LayrzAvatarIcon(MdiIcons.checkCircleOutline),
size: 40,
color: context.tokens.colors.success,
)Or using the named constructor:
LayrzAvatar.icon(
icon: MdiIcons.checkCircleOutline,
size: 40,
color: context.tokens.colors.success,
)LayrzAvatar(
source: LayrzAvatarEmoji('🎨'),
size: 48,
)Or using the named constructor:
LayrzAvatar.emoji(
emoji: '🎨',
size: 48,
)LayrzAvatar.initials(
nameText: 'Alice Johnson',
size: 40,
color: context.tokens.colors.success,
)// No source provided; falls back to initials
LayrzAvatar(
source: null,
nameText: 'Bob Brown',
// Displays initials "BB"
)If you were previously using Avatar models from layrz_sdk, update your code to use LayrzAvatarSource:
Before (layrz_sdk):
final user = Avatar(type: AvatarType.url, url: 'https://example.com/user.png');
LayrzAvatar(avatar: user)After (layrz_ui):
LayrzAvatar(source: LayrzAvatarUrl('https://example.com/user.png'))The conversion logic (if holding SDK Avatar models) should live in an adapter package like layrz_ui_extensions, not in layrz_ui itself.
- Avatars are static display elements with no interaction affordances.
- Initials text is painted directly and is accessible to screen readers if wrapped in semantic containers (e.g., a
Semanticswidget). - Icons are rendered as icon glyphs without semantic labeling (intentional, to avoid noise in screen readers for decorative avatars).
-
Static display only: No
onTap,onLongPress,onSecondaryTap. Wrap withGestureDetectorif needed. -
Avatar source:
LayrzAvatarSourceis a sealed class with four concrete types (LayrzAvatarUrl,LayrzAvatarBase64,LayrzAvatarIcon,LayrzAvatarEmoji). Each holds its data directly with no external dependencies. -
Image rendering: Images are processed by
LayrzImage, which handles multiple formats and caching transparently. -
No interaction states: Unlike
LayrzButtonorLayrzChip, there is no hover, press, or focus state. The component is purely presentational. -
No layrz_sdk dependency: As of 0.0.9, layrz_ui depends only on
google_fonts,layrz_icons, andflutter_svg. The sealed hierarchyLayrzAvatarSourcereplaces the SDK'sAvatarmodel.
- LayrzImage — Image component used for URL and base64 rendering
- LayrzAvatarInput — Input component for avatar selection from camera/gallery
- LayrzDynamicAvatarInput — Input component for multi-source avatar selection
Made with ❤️ by Golden M, Inc.
- LayrzTextInput
- LayrzSelectableAction
- LayrzSelectionToolbar
- LayrzTextSelectionControls
- LayrzSelectionMagnifier
- LayrzSelectionHandlePainter
- LayrzTextAreaInput
- LayrzComboBoxInput
- LayrzNumberInput
- LayrzPasswordInput
- LayrzCheckboxInput
- LayrzRadioInput
- LayrzSelectInput
- LayrzMultiSelectInput
- LayrzSearchInput
- LayrzDualListInput
- LayrzDurationInput