Improved sign up screen

This commit is contained in:
Marc Rejohn Castillano 2026-02-22 14:46:59 +08:00
parent 62a9544533
commit dd29d2f90f

View File

@ -85,10 +85,11 @@ class _SignUpScreenState extends ConsumerState<SignUpScreen> {
body: ResponsiveBody(
maxWidth: 480,
padding: const EdgeInsets.symmetric(vertical: 24),
child: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Center(
@ -187,7 +188,7 @@ class _SignUpScreenState extends ConsumerState<SignUpScreen> {
return null;
},
),
const SizedBox(height: 16),
const SizedBox(height: 12),
Text('Offices', style: Theme.of(context).textTheme.titleSmall),
const SizedBox(height: 8),
officesAsync.when(
@ -195,28 +196,43 @@ class _SignUpScreenState extends ConsumerState<SignUpScreen> {
if (offices.isEmpty) {
return const Text('No offices available.');
}
final officeNameById = <String, String>{
for (final o in offices) o.id: o.name,
};
return Column(
children: offices
.map(
(office) => CheckboxListTile(
value: _selectedOfficeIds.contains(office.id),
onChanged: _isLoading
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ElevatedButton.icon(
onPressed: _isLoading
? null
: (selected) {
setState(() {
if (selected == true) {
_selectedOfficeIds.add(office.id);
} else {
_selectedOfficeIds.remove(office.id);
}
});
},
title: Text(office.name),
controlAffinity: ListTileControlAffinity.leading,
contentPadding: EdgeInsets.zero,
: () => _showOfficeSelectionDialog(offices),
icon: const Icon(Icons.place),
label: const Text('Select Offices'),
),
)
.toList(),
const SizedBox(height: 8),
if (_selectedOfficeIds.isEmpty)
const Text('No office selected.')
else
Wrap(
spacing: 8,
runSpacing: 8,
children: _selectedOfficeIds.map((id) {
final name = officeNameById[id] ?? id;
return Chip(
label: Text(name),
onDeleted: _isLoading
? null
: () {
setState(
() => _selectedOfficeIds.remove(id),
);
},
);
}).toList(),
),
],
);
},
loading: () => const LinearProgressIndicator(),
@ -242,6 +258,66 @@ class _SignUpScreenState extends ConsumerState<SignUpScreen> {
),
),
),
),
);
}
Future<void> _showOfficeSelectionDialog(List<dynamic> offices) async {
final tempSelected = Set<String>.from(_selectedOfficeIds);
await showDialog<void>(
context: context,
builder: (dialogCtx) => StatefulBuilder(
builder: (ctx2, setStateDialog) {
return AlertDialog(
title: const Text('Select Offices'),
content: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 480),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: offices.map<Widget>((office) {
final id = office.id;
final name = office.name;
return CheckboxListTile(
value: tempSelected.contains(id),
onChanged: (v) {
setStateDialog(() {
if (v == true) {
tempSelected.add(id);
} else {
tempSelected.remove(id);
}
});
},
title: Text(name),
controlAffinity: ListTileControlAffinity.leading,
contentPadding: EdgeInsets.zero,
);
}).toList(),
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(dialogCtx).pop(),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () {
setState(() {
_selectedOfficeIds
..clear()
..addAll(tempSelected);
});
Navigator.of(dialogCtx).pop();
},
child: const Text('Save'),
),
],
);
},
),
);
}
@ -253,18 +329,29 @@ class _SignUpScreenState extends ConsumerState<SignUpScreen> {
if (RegExp(r'[A-Z]').hasMatch(text)) score++;
if (RegExp(r'[a-z]').hasMatch(text)) score++;
if (RegExp(r'\d').hasMatch(text)) score++;
if (RegExp(r'[!@#$%^&*(),.?":{}|<>\[\]\\/+=;_-]').hasMatch(text)) {
if (RegExp(r'[!@#\$%\^&\*(),.?":{}|<>\[\]\\/+=;_-]').hasMatch(text)) {
score++;
}
final normalized = (score / 6).clamp(0.0, 1.0);
final (label, color) = switch (normalized) {
<= 0.2 => ('Very weak', Colors.red),
<= 0.4 => ('Weak', Colors.deepOrange),
<= 0.6 => ('Fair', Colors.orange),
<= 0.8 => ('Strong', Colors.green),
_ => ('Excellent', Colors.teal),
};
String label;
Color color;
if (normalized <= 0.2) {
label = 'Very weak';
color = Colors.red;
} else if (normalized <= 0.4) {
label = 'Weak';
color = Colors.deepOrange;
} else if (normalized <= 0.6) {
label = 'Fair';
color = Colors.orange;
} else if (normalized <= 0.8) {
label = 'Strong';
color = Colors.green;
} else {
label = 'Excellent';
color = Colors.teal;
}
setState(() {
_passwordStrength = normalized;