Skip to content

LayrzAvatar

Kenny Mochizuki Escalona edited this page Aug 20, 2026 · 9 revisions

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.


Overview

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:

  1. Image from a named constructor (.image()) — renders from URL or base64
  2. Icon from a named constructor (.icon()) — renders from IconData
  3. Emoji from a named constructor (.emoji()) — renders a Unicode glyph
  4. Initials from a named constructor (.initials()) — renders from nameText
  5. Avatar source from LayrzAvatarSource model (source parameter) — resolves by sealed type
  6. Fallback to initials — from nameText when the source is null

Design Principles

  • Static display only: No interaction callbacks. Wrap with GestureDetector if 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 size parameter defines both width and height.
  • Consistent rounding: Always a rounded box using the r3 radius 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.compact1 drop 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.

API Structure

Core Constructor

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,
  });
}

Avatar Source Types

LayrzAvatarSource is a sealed class hierarchy. Extend it to add new avatar types.

LayrzAvatarUrl

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);
}

LayrzAvatarBase64

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);
}

LayrzAvatarIcon

Displays an icon from IconData.

final class LayrzAvatarIcon extends LayrzAvatarSource {
  /// The icon to display.
  final IconData icon;
  
  const LayrzAvatarIcon(this.icon);
}

LayrzAvatarEmoji

Displays a Unicode emoji glyph.

final class LayrzAvatarEmoji extends LayrzAvatarSource {
  /// The emoji string to display.
  final String emoji;
  
  const LayrzAvatarEmoji(this.emoji);
}

Named Constructors

.image()

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 to LayrzImage.
  • Background: White, ensuring visibility when the image source has transparency.

.icon()

Renders an avatar from an IconData glyph.

const LayrzAvatar.icon({
  required IconData icon,
  this.size = 40,
  this.color,
});
  • icon: The IconData to display (e.g., from MdiIcons / flutter_material_design_icons or other icon libraries).
  • Color: Defaults to the primary token color.
  • Icon size: 70% of size.

.emoji()

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.

.initials()

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.

Avatar Source Resolution

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

Initials Generation

The initials algorithm (when falling back to text):

  1. Strip all non-alphanumeric characters from the input name.
  2. If empty, display "NA" (Not Available).
  3. If one character, display that character.
  4. 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.


Sizing and Styling

  • Size: A single size parameter (default 40) defines both width and height.
  • Shape: Always a rounded box using the r3 radius token (16 logical pixels), consistent with LayrzCard, 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.compact1 as a fixed drop shadow in all render modes (initials, icon, emoji, URL/base64 image). This is the small-component shadow ramp used by LayrzButton, 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.

Image Loading

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

Relationship to Input Components

  • 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).

Examples

Image Avatar

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,
)

Base64 Avatar

LayrzAvatar(
  source: LayrzAvatarBase64('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg=='),
  size: 40,
)

Icon Avatar

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,
)

Emoji Avatar

LayrzAvatar(
  source: LayrzAvatarEmoji('🎨'),
  size: 48,
)

Or using the named constructor:

LayrzAvatar.emoji(
  emoji: '🎨',
  size: 48,
)

Initials Avatar

LayrzAvatar.initials(
  nameText: 'Alice Johnson',
  size: 40,
  color: context.tokens.colors.success,
)

Fallback to Initials

// No source provided; falls back to initials
LayrzAvatar(
  source: null,
  nameText: 'Bob Brown',
  // Displays initials "BB"
)

Migration from layrz_sdk Avatar

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.


Accessibility

  • 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 Semantics widget).
  • Icons are rendered as icon glyphs without semantic labeling (intentional, to avoid noise in screen readers for decorative avatars).

Notes

  • Static display only: No onTap, onLongPress, onSecondaryTap. Wrap with GestureDetector if needed.
  • Avatar source: LayrzAvatarSource is 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 LayrzButton or LayrzChip, 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, and flutter_svg. The sealed hierarchy LayrzAvatarSource replaces the SDK's Avatar model.

See Also

Clone this wiki locally