Enhance UI button and order card features

Added fullWidth support to UiButton and updated usages to ensure buttons span container width. Improved ViewOrderCard with expanded worker details, avatar stack, formatted date/time, and an order edit bottom sheet. Introduced new icons and typography style in the design system.
This commit is contained in:
Achintha Isuru
2026-01-23 11:53:36 -05:00
parent 960b21ec8c
commit 868688fb02
7 changed files with 543 additions and 100 deletions

View File

@@ -163,6 +163,12 @@ class UiIcons {
/// Eye off icon for hidden visibility
static const IconData eyeOff = _IconLib.eyeOff;
/// Phone icon for calls
static const IconData phone = _IconLib.phone;
/// Message circle icon for chat
static const IconData messageCircle = _IconLib.messageCircle;
/// Building icon for companies
static const IconData building = _IconLib.building2;

View File

@@ -12,8 +12,8 @@ class UiTheme {
/// Returns the light theme for the Staff application.
static ThemeData get light {
final colorScheme = UiColors.colorScheme;
final textTheme = UiTypography.textTheme;
final ColorScheme colorScheme = UiColors.colorScheme;
final TextTheme textTheme = UiTypography.textTheme;
return ThemeData(
useMaterial3: true,
@@ -68,7 +68,6 @@ class UiTheme {
horizontal: UiConstants.space6,
vertical: UiConstants.space3,
),
minimumSize: const Size(double.infinity, 54),
maximumSize: const Size(double.infinity, 54),
).copyWith(
side: WidgetStateProperty.resolveWith<BorderSide?>((states) {
@@ -99,7 +98,6 @@ class UiTheme {
horizontal: UiConstants.space4,
vertical: UiConstants.space2,
),
minimumSize: const Size(double.infinity, 52),
maximumSize: const Size(double.infinity, 52),
),
),
@@ -117,7 +115,6 @@ class UiTheme {
horizontal: UiConstants.space4,
vertical: UiConstants.space3,
),
minimumSize: const Size(double.infinity, 52),
maximumSize: const Size(double.infinity, 52),
),
),

View File

@@ -320,6 +320,15 @@ class UiTypography {
color: UiColors.textPrimary,
);
/// Body 3 Medium - Font: Instrument Sans, Size: 14, Height: 1.5, Spacing: -0.1 (#121826)
static final TextStyle body3m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 12,
height: 1.5,
letterSpacing: -0.1,
color: UiColors.textPrimary,
);
/// Body 4 Regular - Font: Instrument Sans, Size: 14, Height: 1.5, Spacing: 0.05 (#121826)
static final TextStyle body4r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,

View File

@@ -27,6 +27,9 @@ class UiButton extends StatelessWidget {
/// The size of the button.
final UiButtonSize size;
/// Whether the button should take up the full width of its container.
final bool fullWidth;
/// The button widget to use (ElevatedButton, OutlinedButton, or TextButton).
final Widget Function(
BuildContext context,
@@ -48,6 +51,7 @@ class UiButton extends StatelessWidget {
this.style,
this.iconSize = 20,
this.size = UiButtonSize.medium,
this.fullWidth = false,
}) : assert(
text != null || child != null,
'Either text or child must be provided',
@@ -64,6 +68,7 @@ class UiButton extends StatelessWidget {
this.style,
this.iconSize = 20,
this.size = UiButtonSize.medium,
this.fullWidth = false,
}) : buttonBuilder = _elevatedButtonBuilder,
assert(
text != null || child != null,
@@ -81,6 +86,7 @@ class UiButton extends StatelessWidget {
this.style,
this.iconSize = 20,
this.size = UiButtonSize.medium,
this.fullWidth = false,
}) : buttonBuilder = _outlinedButtonBuilder,
assert(
text != null || child != null,
@@ -98,6 +104,25 @@ class UiButton extends StatelessWidget {
this.style,
this.iconSize = 20,
this.size = UiButtonSize.medium,
this.fullWidth = false,
}) : buttonBuilder = _textButtonBuilder,
assert(
text != null || child != null,
'Either text or child must be provided',
);
/// Creates a ghost button (transparent background).
UiButton.ghost({
super.key,
this.text,
this.child,
this.onPressed,
this.leadingIcon,
this.trailingIcon,
this.style,
this.iconSize = 20,
this.size = UiButtonSize.medium,
this.fullWidth = false,
}) : buttonBuilder = _textButtonBuilder,
assert(
text != null || child != null,
@@ -107,7 +132,18 @@ class UiButton extends StatelessWidget {
@override
/// Builds the button UI.
Widget build(BuildContext context) {
return buttonBuilder(context, onPressed, style, _buildButtonContent());
final Widget button = buttonBuilder(
context,
onPressed,
style,
_buildButtonContent(),
);
if (fullWidth) {
return SizedBox(width: double.infinity, child: button);
}
return button;
}
/// Builds the button content with optional leading and trailing icons.
@@ -116,27 +152,40 @@ class UiButton extends StatelessWidget {
return child!;
}
// Single icon or text case
final String buttonText = text ?? '';
// Optimization: If no icons, return plain text to avoid Row layout overhead
if (leadingIcon == null && trailingIcon == null) {
return Text(text!);
return Text(buttonText, textAlign: TextAlign.center);
}
if (leadingIcon != null && text == null && trailingIcon == null) {
return Icon(leadingIcon, size: iconSize);
}
// Multiple elements case
// Multiple elements case: Use a Row with MainAxisSize.min
final List<Widget> children = [];
if (leadingIcon != null) {
children.add(Icon(leadingIcon, size: iconSize));
children.add(const SizedBox(width: UiConstants.space2));
}
children.add(Text(text!));
if (buttonText.isNotEmpty) {
if (leadingIcon != null) {
children.add(const SizedBox(width: UiConstants.space2));
}
// Use flexible to ensure text doesn't force infinite width in flex parents
children.add(
Flexible(
child: Text(
buttonText,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
),
),
);
}
if (trailingIcon != null) {
children.add(const SizedBox(width: UiConstants.space2));
if (buttonText.isNotEmpty || leadingIcon != null) {
children.add(const SizedBox(width: UiConstants.space2));
}
children.add(Icon(trailingIcon, size: iconSize));
}