feat: Refactor ProfileHeader and introduce ProfileLevelBadge for improved structure and functionality

This commit is contained in:
Achintha Isuru
2026-02-19 15:11:54 -05:00
parent 5fb9d75c58
commit d54979ceed
4 changed files with 173 additions and 188 deletions

View File

@@ -9,7 +9,7 @@ import 'package:krow_domain/krow_domain.dart';
import '../blocs/profile_cubit.dart';
import '../blocs/profile_state.dart';
import '../widgets/logout_button.dart';
import '../widgets/profile_header.dart';
import '../widgets/header/profile_header.dart';
import '../widgets/reliability_score_bar.dart';
import '../widgets/reliability_stats_card.dart';
import '../widgets/sections/index.dart';
@@ -25,19 +25,6 @@ class StaffProfilePage extends StatelessWidget {
/// Creates a [StaffProfilePage].
const StaffProfilePage({super.key});
String _mapStatusToLevel(StaffStatus status) {
switch (status) {
case StaffStatus.active:
case StaffStatus.verified:
return 'Krower I';
case StaffStatus.pending:
case StaffStatus.completedProfile:
return 'Pending';
default:
return 'New';
}
}
@override
Widget build(BuildContext context) {
final ProfileCubit cubit = Modular.get<ProfileCubit>();
@@ -106,7 +93,6 @@ class StaffProfilePage extends StatelessWidget {
children: <Widget>[
ProfileHeader(
fullName: profile.name,
level: _mapStatusToLevel(profile.status),
photoUrl: profile.avatar,
),
Transform.translate(

View File

@@ -0,0 +1,116 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'profile_level_badge.dart';
/// The header section of the staff profile page, containing avatar, name, and level.
///
/// Uses design system tokens for all colors, typography, and spacing.
class ProfileHeader extends StatelessWidget {
/// Creates a [ProfileHeader].
const ProfileHeader({
super.key,
required this.fullName,
this.photoUrl,
});
/// The staff member's full name
final String fullName;
/// Optional photo URL for the avatar
final String? photoUrl;
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(
UiConstants.space5,
UiConstants.space5,
UiConstants.space5,
UiConstants.space16,
),
decoration: const BoxDecoration(
color: UiColors.primary,
borderRadius: BorderRadius.vertical(
bottom: Radius.circular(UiConstants.space6),
),
),
child: SafeArea(
bottom: false,
child: Column(
children: <Widget>[
// Avatar Section
Container(
width: 112,
height: 112,
padding: const EdgeInsets.all(UiConstants.space1),
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: <Color>[
UiColors.accent,
UiColors.accent.withValues(alpha: 0.5),
UiColors.primaryForeground,
],
),
boxShadow: <BoxShadow>[
BoxShadow(
color: UiColors.foreground.withValues(alpha: 0.2),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: UiColors.primaryForeground.withValues(alpha: 0.2),
width: 4,
),
),
child: CircleAvatar(
backgroundColor: UiColors.background,
backgroundImage: photoUrl != null
? NetworkImage(photoUrl!)
: null,
child: photoUrl == null
? Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: <Color>[
UiColors.accent,
UiColors.accent.withValues(alpha: 0.7),
],
),
),
alignment: Alignment.center,
child: Text(
fullName.isNotEmpty
? fullName[0].toUpperCase()
: 'K',
style: UiTypography.displayM.primary,
),
)
: null,
),
),
),
const SizedBox(height: UiConstants.space4),
Text(fullName, style: UiTypography.headline2m.white),
const SizedBox(height: UiConstants.space1),
const ProfileLevelBadge(),
],
),
),
);
}
}

View File

@@ -0,0 +1,56 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_domain/krow_domain.dart';
import '../../blocs/profile_cubit.dart';
import '../../blocs/profile_state.dart';
/// A widget that displays the staff member's level badge.
///
/// The level is calculated based on the staff status from ProfileCubit and displayed
/// in a styled container with the design system tokens.
class ProfileLevelBadge extends StatelessWidget {
/// Creates a [ProfileLevelBadge].
const ProfileLevelBadge({super.key});
/// Maps staff status to a user-friendly level string.
String _mapStatusToLevel(StaffStatus status) {
switch (status) {
case StaffStatus.active:
case StaffStatus.verified:
return 'Krower I';
case StaffStatus.pending:
case StaffStatus.completedProfile:
return 'Pending';
default:
return 'New';
}
}
@override
Widget build(BuildContext context) {
return BlocBuilder<ProfileCubit, ProfileState>(
builder: (BuildContext context, ProfileState state) {
final Staff? profile = state.profile;
if (profile == null) {
return const SizedBox.shrink();
}
final String level = _mapStatusToLevel(profile.status);
return Container(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space3,
vertical: UiConstants.space1,
),
decoration: BoxDecoration(
color: UiColors.accent.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(UiConstants.space5),
),
child: Text(level, style: UiTypography.footnote1b.accent),
);
},
);
}
}

View File

@@ -1,173 +0,0 @@
import 'package:flutter/material.dart';
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
/// The header section of the staff profile page, containing avatar, name, and level.
///
/// Uses design system tokens for all colors, typography, and spacing.
class ProfileHeader extends StatelessWidget {
/// The staff member's full name
final String fullName;
/// The staff member's level (e.g., "Krower I")
final String level;
/// Optional photo URL for the avatar
final String? photoUrl;
/// Creates a [ProfileHeader].
const ProfileHeader({
super.key,
required this.fullName,
required this.level,
this.photoUrl,
});
@override
Widget build(BuildContext context) {
final TranslationsStaffProfileHeaderEn i18n = t.staff.profile.header;
return Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(
UiConstants.space5,
UiConstants.space5,
UiConstants.space5,
UiConstants.space16,
),
decoration: const BoxDecoration(
color: UiColors.primary,
borderRadius: BorderRadius.vertical(
bottom: Radius.circular(UiConstants.space6),
),
),
child: SafeArea(
bottom: false,
child: Column(
children: <Widget>[
// Top Bar
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
i18n.title,
style: UiTypography.headline4m.textSecondary,
),
],
),
const SizedBox(height: UiConstants.space8),
// Avatar Section
Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
Container(
width: 112,
height: 112,
padding: const EdgeInsets.all(UiConstants.space1),
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: <Color>[
UiColors.accent,
UiColors.accent.withValues(alpha: 0.5),
UiColors.primaryForeground,
],
),
boxShadow: <BoxShadow>[
BoxShadow(
color: UiColors.foreground.withValues(alpha: 0.2),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: UiColors.primaryForeground.withValues(alpha: 0.2),
width: 4,
),
),
child: CircleAvatar(
backgroundColor: UiColors.background,
backgroundImage: photoUrl != null
? NetworkImage(photoUrl!)
: null,
child: photoUrl == null
? Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: <Color>[
UiColors.accent,
UiColors.accent.withValues(alpha: 0.7),
],
),
),
alignment: Alignment.center,
child: Text(
fullName.isNotEmpty
? fullName[0].toUpperCase()
: 'K',
style: UiTypography.displayM.primary,
),
)
: null,
),
),
),
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: UiColors.primaryForeground,
shape: BoxShape.circle,
border: Border.all(color: UiColors.primary, width: 2),
boxShadow: <BoxShadow>[
BoxShadow(
color: UiColors.foreground.withValues(alpha: 0.1),
blurRadius: 4,
),
],
),
child: const Icon(
UiIcons.camera,
size: 16,
color: UiColors.primary,
),
),
],
),
const SizedBox(height: UiConstants.space4),
Text(
fullName,
style: UiTypography.headline3m.textPlaceholder,
),
const SizedBox(height: UiConstants.space1),
Container(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space3,
vertical: UiConstants.space1,
),
decoration: BoxDecoration(
color: UiColors.accent.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(UiConstants.space5),
),
child: Text(
level,
style: UiTypography.footnote1b.accent,
),
),
],
),
),
);
}
}