image

02 Jan 2026

9K

35K

Building a Profile Avatar Widget with Status Indicator in Flutter

In modern applications, displaying user profiles often involves showing an avatar image alongside a visual indicator of their current status (e.g., online, offline, busy). This seemingly simple UI element can significantly enhance user experience by providing immediate visual cues. In Flutter, creating such a widget is straightforward thanks to its powerful composition model. This article will guide you through building a reusable Profile Avatar widget with a dynamic status indicator.

Understanding the Core Components

Before diving into the code, let's identify the primary Flutter widgets we'll leverage:

  • CircleAvatar: The perfect widget for displaying circular user profile images.
  • Stack: A widget that allows positioning multiple children on top of each other. This is crucial for placing the status indicator over the avatar.
  • Positioned: Used within a Stack to control the exact placement of a child widget relative to the stack's edges.
  • Container: A versatile widget that can be used to create the circular status indicator, providing control over its size, color, and shape.

Step-by-Step Implementation

1. Basic Profile Avatar

First, let's start with a simple CircleAvatar to display a user's image. We'll use NetworkImage for demonstration, but you could also use AssetImage or FileImage.


import 'package:flutter/material.dart';

class BasicAvatarExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: CircleAvatar(
        radius: 50,
        backgroundImage: NetworkImage('https://via.placeholder.com/150'),
      ),
    );
  }
}

2. Adding the Status Indicator Overlay

To place the status indicator, we'll wrap the CircleAvatar in a Stack. The indicator itself will be a small circular Container positioned at the bottom-right corner of the avatar.


import 'package:flutter/material.dart';

class AvatarWithStatusExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Stack(
        children: [
          CircleAvatar(
            radius: 50,
            backgroundImage: NetworkImage('https://via.placeholder.com/150'),
          ),
          Positioned(
            bottom: 0,
            right: 0,
            child: Container(
              width: 20,
              height: 20,
              decoration: BoxDecoration(
                color: Colors.green, // Example: online status
                shape: BoxShape.circle,
                border: Border.all(
                  color: Colors.white, // White border for contrast
                  width: 3,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

3. Creating a Reusable Widget

To make this component reusable across your application, it's best to encapsulate it within a StatelessWidget. We'll introduce properties for the image URL, avatar radius, and status type. Using an enum for status makes the code cleaner and less error-prone.


import 'package:flutter/material.dart';

enum UserStatus { online, offline, busy, away }

class ProfileAvatarWithStatus extends StatelessWidget {
  final String imageUrl;
  final double radius;
  final UserStatus status;
  final double statusIndicatorSize;
  final double statusBorderWidth;
  final Color statusBorderColor;

  const ProfileAvatarWithStatus({
    Key? key,
    required this.imageUrl,
    this.radius = 40,
    this.status = UserStatus.offline,
    this.statusIndicatorSize = 12, // Default size for the indicator
    this.statusBorderWidth = 2,
    this.statusBorderColor = Colors.white,
  }) : super(key: key);

  Color _getStatusColor(UserStatus status) {
    switch (status) {
      case UserStatus.online:
        return Colors.green;
      case UserStatus.offline:
        return Colors.grey;
      case UserStatus.busy:
        return Colors.red;
      case UserStatus.away:
        return Colors.orange;
      default:
        return Colors.grey;
    }
  }

  @override
  Widget build(BuildContext context) {
    // Calculate indicator position dynamically relative to avatar radius
    // This ensures the indicator stays on the edge regardless of avatar size
    final double indicatorOffset = radius * 0.1; // Small offset from edge

    return Stack(
      children: [
        CircleAvatar(
          radius: radius,
          backgroundImage: NetworkImage(imageUrl),
          backgroundColor: Colors.grey.shade200, // Placeholder color
        ),
        Positioned(
          bottom: indicatorOffset,
          right: indicatorOffset,
          child: Container(
            width: statusIndicatorSize,
            height: statusIndicatorSize,
            decoration: BoxDecoration(
              color: _getStatusColor(status),
              shape: BoxShape.circle,
              border: Border.all(
                color: statusBorderColor,
                width: statusBorderWidth,
              ),
            ),
          ),
        ),
      ],
    );
  }
}

4. Usage Example

Now that we have our reusable ProfileAvatarWithStatus widget, let's see how to integrate it into a Flutter application. We'll display a few avatars with different statuses and sizes.


import 'package:flutter/material.dart';
// Assuming ProfileAvatarWithStatus and UserStatus enum are defined above or in a separate file.

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Profile Avatar Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Profile Avatars with Status'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              ProfileAvatarWithStatus(
                imageUrl: 'https://images.unsplash.com/photo-1535713875002-d1d0cfd8041c?auto=format&fit=crop&w=600&q=80',
                radius: 60,
                status: UserStatus.online,
                statusIndicatorSize: 20,
              ),
              ProfileAvatarWithStatus(
                imageUrl: 'https://images.unsplash.com/photo-1544005313-94ddf0286df2?auto=format&fit=crop&w=600&q=80',
                radius: 50,
                status: UserStatus.busy,
                statusIndicatorSize: 18,
                statusBorderColor: Colors.purple,
                statusBorderWidth: 2.5,
              ),
              ProfileAvatarWithStatus(
                imageUrl: 'https://images.unsplash.com/photo-1507003211169-e69da814c7fc?auto=format&fit=crop&w=600&q=80',
                radius: 40,
                status: UserStatus.offline,
                statusIndicatorSize: 16,
              ),
              ProfileAvatarWithStatus(
                imageUrl: 'https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?auto=format&fit=crop&w=600&q=80',
                radius: 30,
                status: UserStatus.away,
                statusIndicatorSize: 14,
                statusBorderColor: Colors.black,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Enhancements and Considerations

While the widget above is functional, here are some further enhancements and considerations:

  • Error Handling and Placeholder: For NetworkImage, consider using the fadeImage, errorImage, or a package like cached_network_image to handle loading states, errors, and caching more gracefully.
  • Tap Gesture: Wrap the entire Stack in a GestureDetector if you want the avatar to be tappable (e.g., to view the user's full profile).
  • Animation: For a more dynamic feel, you could animate the status indicator (e.g., a subtle pulse for online status) using AnimatedContainer or explicit animations.
  • Accessibility: Ensure that the status indicator's color contrast is sufficient and consider adding semantic labels for screen readers.
  • Custom Status Indicators: Instead of just a colored circle, you could pass a custom widget for the status indicator (e.g., an icon for "busy").
  • Local Assets: If avatars are local assets, adjust the backgroundImage property to use AssetImage(widget.imageUrl).

Conclusion

Building a Profile Avatar widget with a status indicator in Flutter is an excellent example of how combining simple widgets like CircleAvatar, Stack, and Positioned allows for the creation of rich and interactive UI components. By encapsulating this logic into a reusable widget, you can maintain a clean codebase and easily apply a consistent look and feel across your application, significantly enhancing the user experience.

Related Articles

May 14, 2026

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter Countdown timers are essential in many applic

May 11, 2026

Unleashing Dynamic UIs: Flutter's Animation Prowess

Unleashing Dynamic UIs: Flutter's Animation Prowess for Slide & Scale Effects Flutter's declarative UI framework, combined with its powerful animation capabilit

May 11, 2026

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy A well-designed Product Detail Page (PDP) is