feat: add attire section components for improved UI organization
This commit is contained in:
@@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user