feat: Implement dedicated attire capture page, refactor attire selection with item cards and filtering.
This commit is contained in:
@@ -0,0 +1,245 @@
|
|||||||
|
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 'package:flutter_modular/flutter_modular.dart';
|
||||||
|
import 'package:core_localization/core_localization.dart';
|
||||||
|
|
||||||
|
import '../blocs/attire_cubit.dart';
|
||||||
|
import '../blocs/attire_state.dart';
|
||||||
|
import '../widgets/attestation_checkbox.dart';
|
||||||
|
|
||||||
|
class AttireCapturePage extends StatefulWidget {
|
||||||
|
const AttireCapturePage({super.key, required this.item});
|
||||||
|
|
||||||
|
final AttireItem item;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AttireCapturePage> createState() => _AttireCapturePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||||
|
bool _isAttested = false;
|
||||||
|
|
||||||
|
void _onUpload(BuildContext context) {
|
||||||
|
if (!_isAttested) {
|
||||||
|
UiSnackbar.show(
|
||||||
|
context,
|
||||||
|
message: 'Please attest that you own this item.',
|
||||||
|
type: UiSnackbarType.error,
|
||||||
|
margin: const EdgeInsets.all(UiConstants.space4),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Call the upload via cubit
|
||||||
|
final AttireCubit cubit = Modular.get<AttireCubit>();
|
||||||
|
cubit.uploadPhoto(widget.item.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _viewEnlargedImage(BuildContext context) {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return Dialog(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
child: Container(
|
||||||
|
constraints: const BoxConstraints(maxHeight: 500, maxWidth: 500),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
|
image: DecorationImage(
|
||||||
|
image: NetworkImage(
|
||||||
|
widget.item.imageUrl ??
|
||||||
|
'https://images.unsplash.com/photo-1549298916-b41d501d3772?auto=format&fit=crop&q=80&w=400&h=400',
|
||||||
|
),
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final AttireCubit cubit = Modular.get<AttireCubit>();
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: UiColors.background,
|
||||||
|
appBar: UiAppBar(title: widget.item.label, showBackButton: true),
|
||||||
|
body: BlocConsumer<AttireCubit, AttireState>(
|
||||||
|
bloc: cubit,
|
||||||
|
listener: (BuildContext context, AttireState state) {
|
||||||
|
if (state.status == AttireStatus.failure) {
|
||||||
|
UiSnackbar.show(
|
||||||
|
context,
|
||||||
|
message: translateErrorKey(state.errorMessage ?? 'Error'),
|
||||||
|
type: UiSnackbarType.error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
builder: (BuildContext context, AttireState state) {
|
||||||
|
final bool isUploading =
|
||||||
|
state.uploadingStatus[widget.item.id] ?? false;
|
||||||
|
final bool hasPhoto = state.photoUrls.containsKey(widget.item.id);
|
||||||
|
final String statusText = hasPhoto
|
||||||
|
? 'Pending Verification'
|
||||||
|
: 'Not Uploaded';
|
||||||
|
final Color statusColor = hasPhoto
|
||||||
|
? UiColors.textWarning
|
||||||
|
: UiColors.textInactive;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
// Image Preview
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () => _viewEnlargedImage(context),
|
||||||
|
child: Container(
|
||||||
|
height: 200,
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: UiColors.white,
|
||||||
|
borderRadius: BorderRadius.circular(
|
||||||
|
UiConstants.radiusBase,
|
||||||
|
),
|
||||||
|
boxShadow: const <BoxShadow>[
|
||||||
|
BoxShadow(
|
||||||
|
color: Color(0x19000000),
|
||||||
|
blurRadius: 4,
|
||||||
|
offset: Offset(0, 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
image: DecorationImage(
|
||||||
|
image: NetworkImage(
|
||||||
|
widget.item.imageUrl ??
|
||||||
|
'https://images.unsplash.com/photo-1549298916-b41d501d3772?auto=format&fit=crop&q=80&w=400&h=400',
|
||||||
|
),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Align(
|
||||||
|
alignment: Alignment.bottomRight,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Icon(
|
||||||
|
UiIcons.search,
|
||||||
|
color: UiColors.white,
|
||||||
|
shadows: <Shadow>[
|
||||||
|
Shadow(color: Colors.black, blurRadius: 4),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
|
Text(
|
||||||
|
widget.item.description ?? '',
|
||||||
|
style: UiTypography.body1r.textSecondary,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
|
// Verification info
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: UiColors.bgPopup,
|
||||||
|
borderRadius: BorderRadius.circular(
|
||||||
|
UiConstants.radiusBase,
|
||||||
|
),
|
||||||
|
border: Border.all(color: UiColors.border),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
const Icon(
|
||||||
|
UiIcons.info,
|
||||||
|
color: UiColors.primary,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space3),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
'Verification Status',
|
||||||
|
style: UiTypography.footnote2m.textPrimary,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
statusText,
|
||||||
|
style: UiTypography.body2m.copyWith(
|
||||||
|
color: statusColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
|
AttestationCheckbox(
|
||||||
|
isChecked: _isAttested,
|
||||||
|
onChanged: (bool? val) {
|
||||||
|
setState(() {
|
||||||
|
_isAttested = val ?? false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
|
if (isUploading)
|
||||||
|
const Center(child: CircularProgressIndicator())
|
||||||
|
else if (!hasPhoto ||
|
||||||
|
true) // Show options even if has photo (allows re-upload)
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: UiButton.secondary(
|
||||||
|
text: 'Gallery',
|
||||||
|
onPressed: () => _onUpload(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space4),
|
||||||
|
Expanded(
|
||||||
|
child: UiButton.primary(
|
||||||
|
text: 'Camera',
|
||||||
|
onPressed: () => _onUpload(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (hasPhoto)
|
||||||
|
SafeArea(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
|
child: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: UiButton.primary(
|
||||||
|
text: 'Submit Image',
|
||||||
|
onPressed: () {
|
||||||
|
Modular.to.pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,89 +6,142 @@ import 'package:core_localization/core_localization.dart';
|
|||||||
|
|
||||||
import '../blocs/attire_cubit.dart';
|
import '../blocs/attire_cubit.dart';
|
||||||
import '../blocs/attire_state.dart';
|
import '../blocs/attire_state.dart';
|
||||||
import '../widgets/attestation_checkbox.dart';
|
|
||||||
import '../widgets/attire_bottom_bar.dart';
|
|
||||||
import '../widgets/attire_grid.dart';
|
|
||||||
import '../widgets/attire_info_card.dart';
|
import '../widgets/attire_info_card.dart';
|
||||||
|
import '../widgets/attire_item_card.dart';
|
||||||
|
import 'attire_capture_page.dart';
|
||||||
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
class AttirePage extends StatelessWidget {
|
class AttirePage extends StatefulWidget {
|
||||||
const AttirePage({super.key});
|
const AttirePage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
State<AttirePage> createState() => _AttirePageState();
|
||||||
// Note: t.staff_profile_attire is available via re-export of core_localization
|
}
|
||||||
final AttireCubit cubit = Modular.get<AttireCubit>();
|
|
||||||
|
|
||||||
return BlocProvider<AttireCubit>.value(
|
class _AttirePageState extends State<AttirePage> {
|
||||||
value: cubit,
|
String _filter = 'All';
|
||||||
child: Scaffold(
|
|
||||||
backgroundColor: UiColors.background, // FAFBFC
|
Widget _buildFilterChip(String label) {
|
||||||
appBar: UiAppBar(
|
final bool isSelected = _filter == label;
|
||||||
title: t.staff_profile_attire.title,
|
return GestureDetector(
|
||||||
showBackButton: true,
|
onTap: () => setState(() => _filter = label),
|
||||||
bottom: PreferredSize(
|
child: Container(
|
||||||
preferredSize: const Size.fromHeight(1.0),
|
padding: const EdgeInsets.symmetric(
|
||||||
child: Container(color: UiColors.border, height: 1.0),
|
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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: BlocConsumer<AttireCubit, AttireState>(
|
child: Text(
|
||||||
|
label,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: (isSelected
|
||||||
|
? UiTypography.footnote2m.white
|
||||||
|
: UiTypography.footnote2m.textSecondary),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final AttireCubit cubit = Modular.get<AttireCubit>();
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: UiColors.background,
|
||||||
|
appBar: UiAppBar(
|
||||||
|
title: t.staff_profile_attire.title,
|
||||||
|
showBackButton: true,
|
||||||
|
bottom: PreferredSize(
|
||||||
|
preferredSize: const Size.fromHeight(1.0),
|
||||||
|
child: Container(color: UiColors.border, height: 1.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: BlocProvider<AttireCubit>.value(
|
||||||
|
value: cubit,
|
||||||
|
child: BlocConsumer<AttireCubit, AttireState>(
|
||||||
listener: (BuildContext context, AttireState state) {
|
listener: (BuildContext context, AttireState state) {
|
||||||
if (state.status == AttireStatus.failure) {
|
if (state.status == AttireStatus.failure) {
|
||||||
UiSnackbar.show(
|
UiSnackbar.show(
|
||||||
context,
|
context,
|
||||||
message: translateErrorKey(state.errorMessage ?? 'Error'),
|
message: translateErrorKey(state.errorMessage ?? 'Error'),
|
||||||
type: UiSnackbarType.error,
|
type: UiSnackbarType.error,
|
||||||
margin: const EdgeInsets.only(
|
|
||||||
bottom: 150,
|
|
||||||
left: UiConstants.space4,
|
|
||||||
right: UiConstants.space4,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (state.status == AttireStatus.saved) {
|
|
||||||
Modular.to.pop();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
builder: (BuildContext context, AttireState state) {
|
builder: (BuildContext context, AttireState state) {
|
||||||
if (state.status == AttireStatus.loading && state.options.isEmpty) {
|
if (state.status == AttireStatus.loading && state.options.isEmpty) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final List<AttireItem> options = state.options;
|
||||||
|
final List<AttireItem> filteredOptions = options.where((
|
||||||
|
AttireItem item,
|
||||||
|
) {
|
||||||
|
if (_filter == 'Required') return item.isMandatory;
|
||||||
|
if (_filter == 'Non-Essential') return !item.isMandatory;
|
||||||
|
return true;
|
||||||
|
}).toList();
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(UiConstants.space5),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const AttireInfoCard(),
|
const AttireInfoCard(),
|
||||||
const SizedBox(height: UiConstants.space6),
|
const SizedBox(height: UiConstants.space6),
|
||||||
AttireGrid(
|
|
||||||
items: state.options,
|
// Filter Chips
|
||||||
selectedIds: state.selectedIds,
|
SingleChildScrollView(
|
||||||
photoUrls: state.photoUrls,
|
scrollDirection: Axis.horizontal,
|
||||||
uploadingStatus: state.uploadingStatus,
|
child: Row(
|
||||||
onToggle: cubit.toggleSelection,
|
children: <Widget>[
|
||||||
onUpload: cubit.uploadPhoto,
|
_buildFilterChip('All'),
|
||||||
|
const SizedBox(width: UiConstants.space2),
|
||||||
|
_buildFilterChip('Required'),
|
||||||
|
const SizedBox(width: UiConstants.space2),
|
||||||
|
_buildFilterChip('Non-Essential'),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space6),
|
const SizedBox(height: UiConstants.space6),
|
||||||
AttestationCheckbox(
|
|
||||||
isChecked: state.attestationChecked,
|
// Item List
|
||||||
onChanged: (bool? val) =>
|
...filteredOptions.map((AttireItem item) {
|
||||||
cubit.toggleAttestation(val ?? false),
|
return Padding(
|
||||||
),
|
padding: const EdgeInsets.only(
|
||||||
|
bottom: UiConstants.space3,
|
||||||
|
),
|
||||||
|
child: AttireItemCard(
|
||||||
|
item: item,
|
||||||
|
isUploading:
|
||||||
|
state.uploadingStatus[item.id] ?? false,
|
||||||
|
uploadedPhotoUrl: state.photoUrls[item.id],
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push<void>(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute<void>(
|
||||||
|
builder: (BuildContext ctx) =>
|
||||||
|
AttireCapturePage(item: item),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
const SizedBox(height: UiConstants.space20),
|
const SizedBox(height: UiConstants.space20),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
AttireBottomBar(
|
|
||||||
canSave: state.canSave,
|
|
||||||
allMandatorySelected: state.allMandatorySelected,
|
|
||||||
allMandatoryHavePhotos: state.allMandatoryHavePhotos,
|
|
||||||
attestationChecked: state.attestationChecked,
|
|
||||||
onSave: cubit.save,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import 'package:design_system/design_system.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
|
class AttireItemCard extends StatelessWidget {
|
||||||
|
final AttireItem item;
|
||||||
|
final String? uploadedPhotoUrl;
|
||||||
|
final bool isUploading;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
const AttireItemCard({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
this.uploadedPhotoUrl,
|
||||||
|
this.isUploading = false,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final bool hasPhoto = uploadedPhotoUrl != null;
|
||||||
|
|
||||||
|
final String statusText = hasPhoto ? 'Pending' : 'Not Uploaded';
|
||||||
|
final Color statusColor = hasPhoto
|
||||||
|
? UiColors.textWarning
|
||||||
|
: UiColors.textInactive;
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: UiColors.white,
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
|
border: Border.all(color: UiColors.border),
|
||||||
|
boxShadow: const <BoxShadow>[
|
||||||
|
BoxShadow(
|
||||||
|
color: Color(0x19000000),
|
||||||
|
blurRadius: 4,
|
||||||
|
offset: Offset(0, 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
// Image
|
||||||
|
Container(
|
||||||
|
width: 64,
|
||||||
|
height: 64,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: UiColors.background,
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
|
image: DecorationImage(
|
||||||
|
image: NetworkImage(
|
||||||
|
item.imageUrl ??
|
||||||
|
'https://images.unsplash.com/photo-1549298916-b41d501d3772?auto=format&fit=crop&q=80&w=400&h=400',
|
||||||
|
),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space4),
|
||||||
|
// details
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(item.label, style: UiTypography.body1m.textPrimary),
|
||||||
|
if (item.description != null) ...<Widget>[
|
||||||
|
const SizedBox(height: UiConstants.space1),
|
||||||
|
Text(
|
||||||
|
item.description!,
|
||||||
|
style: UiTypography.body2r.textSecondary,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
const SizedBox(height: UiConstants.space2),
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
if (item.isMandatory)
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8,
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: UiColors.error.withValues(alpha: 0.1),
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Required',
|
||||||
|
style: UiTypography.footnote2m.textError,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
if (isUploading)
|
||||||
|
const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
|
)
|
||||||
|
else if (hasPhoto)
|
||||||
|
Text(
|
||||||
|
statusText,
|
||||||
|
style: UiTypography.footnote2m.copyWith(
|
||||||
|
color: statusColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space2),
|
||||||
|
// Chevron or status
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
if (!hasPhoto && !isUploading)
|
||||||
|
const Icon(
|
||||||
|
UiIcons.chevronRight,
|
||||||
|
color: UiColors.textInactive,
|
||||||
|
size: 24,
|
||||||
|
)
|
||||||
|
else if (hasPhoto && !isUploading)
|
||||||
|
const Icon(
|
||||||
|
UiIcons.check,
|
||||||
|
color: UiColors.textWarning,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user