feat: Refactor hub widgets to use design system styles

Replaced hardcoded colors, icons, and spacing in hub-related widgets with values from the design system (UiColors, UiTypography, UiIcons, UiConstants). This improves consistency and maintainability across the UI components.
This commit is contained in:
Achintha Isuru
2026-01-22 13:20:39 -05:00
parent d2770d2d29
commit cd0b591c5c
5 changed files with 117 additions and 174 deletions

View File

@@ -42,20 +42,17 @@ class _AddHubDialogState extends State<AddHubDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
color: Colors.black.withValues(alpha: 0.5), color: UiColors.bgOverlay,
child: Center( child: Center(
child: SingleChildScrollView( child: SingleChildScrollView(
child: Container( child: Container(
width: MediaQuery.of(context).size.width * 0.9, width: MediaQuery.of(context).size.width * 0.9,
padding: const EdgeInsets.all(24), padding: const EdgeInsets.all(UiConstants.space5),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: UiColors.bgPopup,
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
boxShadow: [ boxShadow: const [
BoxShadow( BoxShadow(color: UiColors.popupShadow, blurRadius: 20),
color: Colors.black.withValues(alpha: 0.1),
blurRadius: 20,
),
], ],
), ),
child: Column( child: Column(
@@ -64,29 +61,27 @@ class _AddHubDialogState extends State<AddHubDialog> {
children: [ children: [
Text( Text(
t.client_hubs.add_hub_dialog.title, t.client_hubs.add_hub_dialog.title,
style: const TextStyle( style: UiTypography.headline3m.textPrimary,
fontSize: 20,
fontWeight: FontWeight.bold,
color: Color(0xFF0F172A),
),
), ),
const SizedBox(height: 24), const SizedBox(height: UiConstants.space5),
_buildFieldLabel(t.client_hubs.add_hub_dialog.name_label), _buildFieldLabel(t.client_hubs.add_hub_dialog.name_label),
TextField( TextField(
controller: _nameController, controller: _nameController,
style: UiTypography.body1r.textPrimary,
decoration: _buildInputDecoration( decoration: _buildInputDecoration(
t.client_hubs.add_hub_dialog.name_hint, t.client_hubs.add_hub_dialog.name_hint,
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: UiConstants.space4),
_buildFieldLabel(t.client_hubs.add_hub_dialog.address_label), _buildFieldLabel(t.client_hubs.add_hub_dialog.address_label),
TextField( TextField(
controller: _addressController, controller: _addressController,
style: UiTypography.body1r.textPrimary,
decoration: _buildInputDecoration( decoration: _buildInputDecoration(
t.client_hubs.add_hub_dialog.address_hint, t.client_hubs.add_hub_dialog.address_hint,
), ),
), ),
const SizedBox(height: 32), const SizedBox(height: UiConstants.space8),
Row( Row(
children: [ children: [
Expanded( Expanded(
@@ -95,7 +90,7 @@ class _AddHubDialogState extends State<AddHubDialog> {
text: t.common.cancel, text: t.common.cancel,
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: UiConstants.space3),
Expanded( Expanded(
child: UiButton.primary( child: UiButton.primary(
onPressed: () { onPressed: () {
@@ -121,36 +116,32 @@ class _AddHubDialogState extends State<AddHubDialog> {
Widget _buildFieldLabel(String label) { Widget _buildFieldLabel(String label) {
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 6), padding: const EdgeInsets.only(bottom: UiConstants.space2),
child: Text( child: Text(label, style: UiTypography.body2m.textPrimary),
label,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF0F172A),
),
),
); );
} }
InputDecoration _buildInputDecoration(String hint) { InputDecoration _buildInputDecoration(String hint) {
return InputDecoration( return InputDecoration(
hintText: hint, hintText: hint,
hintStyle: const TextStyle(color: Color(0xFF94A3B8), fontSize: 14), hintStyle: UiTypography.body2r.textPlaceholder,
filled: true, filled: true,
fillColor: const Color(0xFFF8FAFC), fillColor: UiColors.input,
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), contentPadding: const EdgeInsets.symmetric(
horizontal: UiConstants.space4,
vertical: 14,
),
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
borderSide: const BorderSide(color: Color(0xFFE2E8F0)), borderSide: const BorderSide(color: UiColors.border),
), ),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
borderSide: const BorderSide(color: Color(0xFFE2E8F0)), borderSide: const BorderSide(color: UiColors.border),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
borderSide: const BorderSide(color: Color(0xFF2563EB), width: 2), borderSide: const BorderSide(color: UiColors.ring, width: 2),
), ),
); );
} }

View File

@@ -1,6 +1,6 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:krow_domain/krow_domain.dart'; import 'package:krow_domain/krow_domain.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:core_localization/core_localization.dart'; import 'package:core_localization/core_localization.dart';
/// A card displaying information about a single hub. /// A card displaying information about a single hub.
@@ -27,68 +27,56 @@ class HubCard extends StatelessWidget {
final bool hasNfc = hub.nfcTagId != null; final bool hasNfc = hub.nfcTagId != null;
return Container( return Container(
margin: const EdgeInsets.only(bottom: 12), margin: const EdgeInsets.only(bottom: UiConstants.space3),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: UiColors.white,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
boxShadow: [ boxShadow: const [
BoxShadow( BoxShadow(
color: Colors.black.withValues(alpha: 0.04), color: UiColors.popupShadow,
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 4), offset: Offset(0, 4),
), ),
], ],
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(UiConstants.space4),
child: Row( child: Row(
children: [ children: [
Container( Container(
width: 52, width: 52,
height: 52, height: 52,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFEFF6FF), // blue-50 color: UiColors.tagInProgress,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
), ),
child: Icon( child: Icon(
hasNfc ? LucideIcons.checkCircle : LucideIcons.nfc, hasNfc ? UiIcons.success : UiIcons.nfc,
color: hasNfc color: hasNfc ? UiColors.iconSuccess : UiColors.iconThird,
? const Color(0xFF16A34A)
: const Color(0xFF94A3B8), // green-600 or slate-400
size: 24, size: 24,
), ),
), ),
const SizedBox(width: 16), const SizedBox(width: UiConstants.space4),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(hub.name, style: UiTypography.body1b.textPrimary),
hub.name,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Color(0xFF0F172A),
),
),
if (hub.address.isNotEmpty) if (hub.address.isNotEmpty)
Padding( Padding(
padding: const EdgeInsets.only(top: 4), padding: const EdgeInsets.only(top: UiConstants.space1),
child: Row( child: Row(
children: [ children: [
const Icon( const Icon(
LucideIcons.mapPin, UiIcons.mapPin,
size: 12, size: 12,
color: Color(0xFF94A3B8), color: UiColors.iconThird,
), ),
const SizedBox(width: 4), const SizedBox(width: UiConstants.space1),
Expanded( Expanded(
child: Text( child: Text(
hub.address, hub.address,
style: const TextStyle( style: UiTypography.footnote1r.textSecondary,
color: Color(0xFF64748B),
fontSize: 12,
),
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@@ -98,14 +86,12 @@ class HubCard extends StatelessWidget {
), ),
if (hasNfc) if (hasNfc)
Padding( Padding(
padding: const EdgeInsets.only(top: 4), padding: const EdgeInsets.only(top: UiConstants.space1),
child: Text( child: Text(
t.client_hubs.hub_card.tag_label(id: hub.nfcTagId!), t.client_hubs.hub_card.tag_label(id: hub.nfcTagId!),
style: const TextStyle( style: UiTypography.footnote1b.copyWith(
color: Color(0xFF16A34A), color: UiColors.textSuccess,
fontSize: 12,
fontFamily: 'monospace', fontFamily: 'monospace',
fontWeight: FontWeight.bold,
), ),
), ),
), ),
@@ -117,20 +103,20 @@ class HubCard extends StatelessWidget {
IconButton( IconButton(
onPressed: onNfcPressed, onPressed: onNfcPressed,
icon: const Icon( icon: const Icon(
LucideIcons.nfc, UiIcons.nfc,
color: Color(0xFF2563EB), color: UiColors.primary,
size: 20, size: 20,
), ),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
constraints: const BoxConstraints(), constraints: const BoxConstraints(),
splashRadius: 20, splashRadius: 20,
), ),
const SizedBox(width: 8), const SizedBox(width: UiConstants.space2),
IconButton( IconButton(
onPressed: onDeletePressed, onPressed: onDeletePressed,
icon: const Icon( icon: const Icon(
LucideIcons.trash2, UiIcons.delete,
color: Color(0xFFDC2626), color: UiColors.destructive,
size: 20, size: 20,
), ),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,

View File

@@ -1,6 +1,5 @@
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:core_localization/core_localization.dart'; import 'package:core_localization/core_localization.dart';
/// Widget displayed when there are no hubs. /// Widget displayed when there are no hubs.
@@ -14,15 +13,15 @@ class HubEmptyState extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
padding: const EdgeInsets.all(32), padding: const EdgeInsets.all(UiConstants.space8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: UiColors.bgBanner,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
boxShadow: [ boxShadow: const [
BoxShadow( BoxShadow(
color: Colors.black.withValues(alpha: 0.04), color: UiColors.popupShadow,
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 4), offset: Offset(0, 4),
), ),
], ],
), ),
@@ -32,35 +31,27 @@ class HubEmptyState extends StatelessWidget {
width: 64, width: 64,
height: 64, height: 64,
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Color(0xFFF1F5F9), // slate-100 color: UiColors.secondary,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: const Icon( child: const Icon(UiIcons.nfc, size: 32, color: UiColors.iconThird),
LucideIcons.nfc,
size: 32,
color: Color(0xFF94A3B8),
),
), ),
const SizedBox(height: 16), const SizedBox(height: UiConstants.space4),
Text( Text(
t.client_hubs.empty_state.title, t.client_hubs.empty_state.title,
style: const TextStyle( style: UiTypography.title1m.textPrimary,
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF0F172A),
),
), ),
const SizedBox(height: 8), const SizedBox(height: UiConstants.space2),
Text( Text(
t.client_hubs.empty_state.description, t.client_hubs.empty_state.description,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle(color: Color(0xFF64748B), fontSize: 14), style: UiTypography.body2r.textSecondary,
), ),
const SizedBox(height: 24), const SizedBox(height: UiConstants.space5),
UiButton.primary( UiButton.primary(
onPressed: onAddPressed, onPressed: onAddPressed,
text: t.client_hubs.empty_state.button, text: t.client_hubs.empty_state.button,
leadingIcon: LucideIcons.plus, leadingIcon: UiIcons.add,
), ),
], ],
), ),

View File

@@ -1,5 +1,5 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:core_localization/core_localization.dart'; import 'package:core_localization/core_localization.dart';
/// A card with information about how hubs work. /// A card with information about how hubs work.
@@ -10,34 +10,29 @@ class HubInfoCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFEFF6FF), // blue-50 color: UiColors.tagInProgress,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Icon(LucideIcons.nfc, size: 20, color: Color(0xFF2563EB)), const Icon(UiIcons.nfc, size: 20, color: UiColors.primary),
const SizedBox(width: 12), const SizedBox(width: UiConstants.space3),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
t.client_hubs.about_hubs.title, t.client_hubs.about_hubs.title,
style: const TextStyle( style: UiTypography.body2b.textPrimary,
fontWeight: FontWeight.bold,
fontSize: 14,
color: Color(0xFF0F172A),
),
), ),
const SizedBox(height: 4), const SizedBox(height: UiConstants.space1),
Text( Text(
t.client_hubs.about_hubs.description, t.client_hubs.about_hubs.description,
style: const TextStyle( style: UiTypography.footnote1r.copyWith(
color: Color(0xFF334155), color: UiColors.textSecondary,
fontSize: 12,
height: 1.4, height: 1.4,
), ),
), ),

View File

@@ -1,6 +1,5 @@
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:krow_domain/krow_domain.dart'; import 'package:krow_domain/krow_domain.dart';
import 'package:core_localization/core_localization.dart'; import 'package:core_localization/core_localization.dart';
@@ -40,20 +39,17 @@ class _IdentifyNfcDialogState extends State<IdentifyNfcDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
color: Colors.black.withValues(alpha: 0.5), color: UiColors.bgOverlay,
child: Center( child: Center(
child: SingleChildScrollView( child: SingleChildScrollView(
child: Container( child: Container(
width: MediaQuery.of(context).size.width * 0.9, width: MediaQuery.of(context).size.width * 0.9,
padding: const EdgeInsets.all(24), padding: const EdgeInsets.all(UiConstants.space5),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: UiColors.bgPopup,
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
boxShadow: [ boxShadow: const [
BoxShadow( BoxShadow(color: UiColors.popupShadow, blurRadius: 20),
color: Colors.black.withValues(alpha: 0.1),
blurRadius: 20,
),
], ],
), ),
child: Column( child: Column(
@@ -61,57 +57,45 @@ class _IdentifyNfcDialogState extends State<IdentifyNfcDialog> {
children: [ children: [
Text( Text(
t.client_hubs.nfc_dialog.title, t.client_hubs.nfc_dialog.title,
style: const TextStyle( style: UiTypography.headline3m.textPrimary,
fontSize: 20,
fontWeight: FontWeight.bold,
color: Color(0xFF0F172A),
),
), ),
const SizedBox(height: 32), const SizedBox(height: UiConstants.space8),
Container( Container(
width: 80, width: 80,
height: 80, height: 80,
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Color(0xFFEFF6FF), // blue-50 color: UiColors.tagInProgress,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: const Icon( child: const Icon(
LucideIcons.nfc, UiIcons.nfc,
size: 40, size: 40,
color: Color(0xFF2563EB), color: UiColors.primary,
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: UiConstants.space4),
Text( Text(widget.hub.name, style: UiTypography.body1b.textPrimary),
widget.hub.name, const SizedBox(height: UiConstants.space2),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Color(0xFF0F172A),
),
),
const SizedBox(height: 8),
Text( Text(
t.client_hubs.nfc_dialog.instruction, t.client_hubs.nfc_dialog.instruction,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: UiTypography.body2m.textSecondary,
color: Color(0xFF64748B),
fontSize: 14,
),
), ),
const SizedBox(height: 24), const SizedBox(height: UiConstants.space5),
UiButton.secondary( UiButton.secondary(
onPressed: _simulateNFCScan, onPressed: _simulateNFCScan,
text: t.client_hubs.nfc_dialog.scan_button, text: t.client_hubs.nfc_dialog.scan_button,
leadingIcon: LucideIcons.nfc, leadingIcon: UiIcons.nfc,
), ),
if (_nfcTagId != null) ...[ if (_nfcTagId != null) ...[
const SizedBox(height: 24), const SizedBox(height: UiConstants.space6),
Container( Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFF0FDF4), // green-50 color: UiColors.tagSuccess,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(
UiConstants.radiusBase,
),
), ),
child: Column( child: Column(
children: [ children: [
@@ -119,39 +103,35 @@ class _IdentifyNfcDialogState extends State<IdentifyNfcDialog> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const Icon( const Icon(
LucideIcons.checkCircle, UiIcons.success,
size: 20, size: 20,
color: Color(0xFF16A34A), color: UiColors.textSuccess,
), ),
const SizedBox(width: 8), const SizedBox(width: UiConstants.space2),
Text( Text(
t.client_hubs.nfc_dialog.tag_identified, t.client_hubs.nfc_dialog.tag_identified,
style: const TextStyle( style: UiTypography.body2b.textPrimary,
fontWeight: FontWeight.bold,
fontSize: 14,
color: Color(0xFF0F172A),
),
), ),
], ],
), ),
const SizedBox(height: 8), const SizedBox(height: UiConstants.space2),
Container( Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 12, horizontal: UiConstants.space3,
vertical: 8, vertical: UiConstants.space2,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: UiColors.white,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(
border: Border.all(color: const Color(0xFFDCE8E0)), UiConstants.radiusMdValue + 2,
),
border: Border.all(color: UiColors.border),
), ),
child: Text( child: Text(
_nfcTagId!, _nfcTagId!,
style: const TextStyle( style: UiTypography.footnote1b.copyWith(
fontFamily: 'monospace', fontFamily: 'monospace',
fontWeight: FontWeight.bold, color: UiColors.textPrimary,
fontSize: 12,
color: Color(0xFF334155),
), ),
), ),
), ),
@@ -159,7 +139,7 @@ class _IdentifyNfcDialogState extends State<IdentifyNfcDialog> {
), ),
), ),
], ],
const SizedBox(height: 32), const SizedBox(height: UiConstants.space8),
Row( Row(
children: [ children: [
Expanded( Expanded(
@@ -168,7 +148,7 @@ class _IdentifyNfcDialogState extends State<IdentifyNfcDialog> {
text: t.common.cancel, text: t.common.cancel,
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: UiConstants.space3),
Expanded( Expanded(
child: UiButton.primary( child: UiButton.primary(
onPressed: _nfcTagId != null onPressed: _nfcTagId != null