feat: add attire section components for improved UI organization

This commit is contained in:
Achintha Isuru
2026-03-07 02:51:07 -05:00
parent c9a46a1a71
commit c936d5f2ab
4 changed files with 98 additions and 83 deletions

View File

@@ -8,8 +8,11 @@ import 'package:krow_domain/krow_domain.dart';
import 'package:staff_attire/src/presentation/blocs/attire/attire_cubit.dart';
import 'package:staff_attire/src/presentation/blocs/attire/attire_state.dart';
import '../widgets/attire_empty_section.dart';
import '../widgets/attire_info_card.dart';
import '../widgets/attire_item_card.dart';
import '../widgets/attire_section_header.dart';
import '../widgets/attire_section_tab.dart';
class AttirePage extends StatefulWidget {
const AttirePage({super.key});
@@ -70,7 +73,7 @@ class _AttirePageState extends State<AttirePage> {
// Section toggle chips
Row(
children: <Widget>[
_SectionTab(
AttireSectionTab(
label: 'Required',
isSelected: _showRequired,
onTap: () => setState(
@@ -78,7 +81,7 @@ class _AttirePageState extends State<AttirePage> {
),
),
const SizedBox(width: UiConstants.space3),
_SectionTab(
AttireSectionTab(
label: 'Non-Essential',
isSelected: _showNonEssential,
onTap: () => setState(
@@ -91,13 +94,13 @@ class _AttirePageState extends State<AttirePage> {
// Required section
if (_showRequired) ...<Widget>[
_SectionHeader(
AttireSectionHeader(
title: 'Required',
count: requiredItems.length,
),
const SizedBox(height: UiConstants.space3),
if (requiredItems.isEmpty)
_EmptySection(
AttireEmptySection(
message: context
.t
.staff_profile_attire
@@ -138,13 +141,13 @@ class _AttirePageState extends State<AttirePage> {
// Non-Essential section
if (_showNonEssential) ...<Widget>[
_SectionHeader(
AttireSectionHeader(
title: 'Non-Essential',
count: nonEssentialItems.length,
),
const SizedBox(height: UiConstants.space3),
if (nonEssentialItems.isEmpty)
_EmptySection(
AttireEmptySection(
message: context
.t
.staff_profile_attire
@@ -185,80 +188,3 @@ class _AttirePageState extends State<AttirePage> {
}
}
class _SectionTab extends StatelessWidget {
const _SectionTab({
required this.label,
required this.isSelected,
required this.onTap,
});
final String label;
final bool isSelected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space4,
vertical: UiConstants.space2,
),
decoration: BoxDecoration(
color: isSelected ? UiColors.primary : UiColors.white,
borderRadius: UiConstants.radiusFull,
border: Border.all(
color: isSelected ? UiColors.primary : UiColors.border,
),
),
child: Text(
label,
style: isSelected
? UiTypography.footnote2m.white
: UiTypography.footnote2m.textSecondary,
),
),
);
}
}
class _SectionHeader extends StatelessWidget {
const _SectionHeader({required this.title, required this.count});
final String title;
final int count;
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Text(title, style: UiTypography.headline4b),
const SizedBox(width: UiConstants.space2),
Text('($count)', style: UiTypography.body1m.textSecondary),
],
);
}
}
class _EmptySection extends StatelessWidget {
const _EmptySection({required this.message});
final String message;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: UiConstants.space6),
child: Center(
child: Column(
children: <Widget>[
const Icon(UiIcons.shirt, size: 48, color: UiColors.iconInactive),
const SizedBox(height: UiConstants.space4),
Text(message, style: UiTypography.body1m.textSecondary),
],
),
),
);
}
}

View File

@@ -0,0 +1,24 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
class AttireEmptySection extends StatelessWidget {
const AttireEmptySection({super.key, required this.message});
final String message;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: UiConstants.space6),
child: Center(
child: Column(
children: <Widget>[
const Icon(UiIcons.shirt, size: 48, color: UiColors.iconInactive),
const SizedBox(height: UiConstants.space4),
Text(message, style: UiTypography.body1m.textSecondary),
],
),
),
);
}
}

View File

@@ -0,0 +1,24 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
class AttireSectionHeader extends StatelessWidget {
const AttireSectionHeader({
super.key,
required this.title,
required this.count,
});
final String title;
final int count;
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Text(title, style: UiTypography.headline4b),
const SizedBox(width: UiConstants.space2),
Text('($count)', style: UiTypography.body1m.textSecondary),
],
);
}
}

View File

@@ -0,0 +1,41 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
class AttireSectionTab extends StatelessWidget {
const AttireSectionTab({
super.key,
required this.label,
required this.isSelected,
required this.onTap,
});
final String label;
final bool isSelected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space4,
vertical: UiConstants.space2,
),
decoration: BoxDecoration(
color: isSelected ? UiColors.primary : UiColors.white,
borderRadius: UiConstants.radiusFull,
border: Border.all(
color: isSelected ? UiColors.primary : UiColors.border,
),
),
child: Text(
label,
style: isSelected
? UiTypography.footnote2m.white
: UiTypography.footnote2m.textSecondary,
),
),
);
}
}