initalizing the mobile apps

This commit is contained in:
Achintha Isuru
2026-01-21 15:42:51 -05:00
parent fbadd976cf
commit 4a67b2f541
578 changed files with 28462 additions and 2 deletions

View File

@@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import '../ui_icons.dart';
/// A custom AppBar for the Krow UI design system.
///
/// This widget provides a consistent look and feel for top app bars across the application.
class UiAppBar extends StatelessWidget implements PreferredSizeWidget {
/// The title text to display in the app bar.
final String? title;
/// A widget to display instead of the title text.
final Widget? titleWidget;
/// The widget to display before the title.
/// Usually an [IconButton] for navigation.
final Widget? leading;
/// A list of Widgets to display in a row after the [title] widget.
final List<Widget>? actions;
/// The height of the app bar. Defaults to [kToolbarHeight].
final double height;
/// Whether the title should be centered.
final bool centerTitle;
/// Signature for the callback that is called when the leading button is pressed.
/// If [leading] is null, this callback will be used for a default back button.
final VoidCallback? onLeadingPressed;
/// Whether to show a default back button if [leading] is null.
final bool showBackButton;
/// This widget appears across the bottom of the app bar.
/// Typically a [TabBar]. Only widgets that implement [PreferredSizeWidget] can be used at the bottom of an app bar.
final PreferredSizeWidget? bottom;
const UiAppBar({
super.key,
this.title,
this.titleWidget,
this.leading,
this.actions,
this.height = kToolbarHeight,
this.centerTitle = true,
this.onLeadingPressed,
this.showBackButton = true,
this.bottom,
});
@override
Widget build(BuildContext context) {
return AppBar(
title: titleWidget ??
(title != null
? Text(
title!,
)
: null),
leading: leading ??
(showBackButton
? IconButton(
icon: const Icon(UiIcons.chevronLeft, size: 20),
onPressed: onLeadingPressed ?? () => Navigator.of(context).pop(),
)
: null),
actions: actions,
centerTitle: centerTitle,
bottom: bottom,
);
}
@override
Size get preferredSize => Size.fromHeight(height + (bottom?.preferredSize.height ?? 0.0));
}

View File

@@ -0,0 +1,191 @@
import 'package:flutter/material.dart';
import '../ui_constants.dart';
/// A custom button widget with different variants and icon support.
class UiButton extends StatelessWidget {
/// The text to display on the button.
final String? text;
/// Optional custom child widget. If provided, overrides text and icons.
final Widget? child;
/// Callback when the button is tapped.
final VoidCallback? onPressed;
/// Optional leading icon.
final IconData? leadingIcon;
/// Optional trailing icon.
final IconData? trailingIcon;
/// Optional Style
final ButtonStyle? style;
/// The size of the icons. Defaults to 20.
final double iconSize;
/// The size of the button.
final UiButtonSize size;
/// The button widget to use (ElevatedButton, OutlinedButton, or TextButton).
final Widget Function(
BuildContext context,
VoidCallback? onPressed,
ButtonStyle? style,
Widget child,
)
buttonBuilder;
/// Creates a [UiButton] with a custom button builder.
const UiButton({
super.key,
this.text,
this.child,
required this.buttonBuilder,
this.onPressed,
this.leadingIcon,
this.trailingIcon,
this.style,
this.iconSize = 20,
this.size = UiButtonSize.medium,
}) : assert(
text != null || child != null,
'Either text or child must be provided',
);
/// Creates a primary button using [ElevatedButton].
UiButton.primary({
super.key,
this.text,
this.child,
this.onPressed,
this.leadingIcon,
this.trailingIcon,
this.style,
this.iconSize = 20,
this.size = UiButtonSize.medium,
}) : buttonBuilder = _elevatedButtonBuilder,
assert(
text != null || child != null,
'Either text or child must be provided',
);
/// Creates a secondary button using [OutlinedButton].
UiButton.secondary({
super.key,
this.text,
this.child,
this.onPressed,
this.leadingIcon,
this.trailingIcon,
this.style,
this.iconSize = 20,
this.size = UiButtonSize.medium,
}) : buttonBuilder = _outlinedButtonBuilder,
assert(
text != null || child != null,
'Either text or child must be provided',
);
/// Creates a text button using [TextButton].
UiButton.text({
super.key,
this.text,
this.child,
this.onPressed,
this.leadingIcon,
this.trailingIcon,
this.style,
this.iconSize = 20,
this.size = UiButtonSize.medium,
}) : buttonBuilder = _textButtonBuilder,
assert(
text != null || child != null,
'Either text or child must be provided',
);
@override
/// Builds the button UI.
Widget build(BuildContext context) {
return buttonBuilder(context, onPressed, style, _buildButtonContent());
}
/// Builds the button content with optional leading and trailing icons.
Widget _buildButtonContent() {
if (child != null) {
return child!;
}
// Single icon or text case
if (leadingIcon == null && trailingIcon == null) {
return Text(text!);
}
if (leadingIcon != null && text == null && trailingIcon == null) {
return Icon(leadingIcon, size: iconSize);
}
// Multiple elements case
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 (trailingIcon != null) {
children.add(const SizedBox(width: UiConstants.space2));
children.add(Icon(trailingIcon, size: iconSize));
}
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: children,
);
}
/// Builder for ElevatedButton.
static Widget _elevatedButtonBuilder(
BuildContext context,
VoidCallback? onPressed,
ButtonStyle? style,
Widget child,
) {
return ElevatedButton(onPressed: onPressed, style: style, child: child);
}
/// Builder for OutlinedButton.
static Widget _outlinedButtonBuilder(
BuildContext context,
VoidCallback? onPressed,
ButtonStyle? style,
Widget child,
) {
return OutlinedButton(onPressed: onPressed, style: style, child: child);
}
/// Builder for TextButton.
static Widget _textButtonBuilder(
BuildContext context,
VoidCallback? onPressed,
ButtonStyle? style,
Widget child,
) {
return TextButton(onPressed: onPressed, style: style, child: child);
}
}
/// Defines the size of a [UiButton].
enum UiButtonSize {
/// Small button (compact)
small,
/// Medium button (standard)
medium,
/// Large button (prominent)
large,
}

View File

@@ -0,0 +1,190 @@
import 'package:flutter/material.dart';
import '../ui_colors.dart';
import '../ui_constants.dart';
import '../ui_typography.dart';
/// Sizes for the [UiChip] widget.
enum UiChipSize {
/// Small size (e.g. for tags in tight spaces).
small,
/// Medium size (default).
medium,
/// Large size (e.g. for standalone filters).
large,
}
/// Themes for the [UiChip] widget.
enum UiChipVariant {
/// Primary style with solid background.
primary,
/// Secondary style with light background.
secondary,
/// Accent style with highlight background.
accent,
}
/// A custom chip widget with supports for different sizes, themes, and icons.
class UiChip extends StatelessWidget {
/// The text label to display.
final String label;
/// The size of the chip. Defaults to [UiChipSize.medium].
final UiChipSize size;
/// The theme variant of the chip. Defaults to [UiChipVariant.secondary].
final UiChipVariant variant;
/// Optional leading icon.
final IconData? leadingIcon;
/// Optional trailing icon.
final IconData? trailingIcon;
/// Callback when the chip is tapped.
final VoidCallback? onTap;
/// Callback when the trailing icon is tapped (e.g. for removal).
final VoidCallback? onTrailingIconTap;
/// Whether the chip is currently selected/active.
final bool isSelected;
/// Creates a [UiChip].
const UiChip({
super.key,
required this.label,
this.size = UiChipSize.medium,
this.variant = UiChipVariant.secondary,
this.leadingIcon,
this.trailingIcon,
this.onTap,
this.onTrailingIconTap,
this.isSelected = false,
});
@override
Widget build(BuildContext context) {
final backgroundColor = _getBackgroundColor();
final contentColor = _getContentColor();
final textStyle = _getTextStyle().copyWith(color: contentColor);
final padding = _getPadding();
final iconSize = _getIconSize();
final content = Row(
mainAxisSize: MainAxisSize.min,
children: [
if (leadingIcon != null) ...[
Icon(leadingIcon, size: iconSize, color: contentColor),
SizedBox(width: _getGap()),
],
Text(label, style: textStyle),
if (trailingIcon != null) ...[
SizedBox(width: _getGap()),
GestureDetector(
onTap: onTrailingIconTap,
child: Icon(trailingIcon, size: iconSize, color: contentColor),
),
],
],
);
return GestureDetector(
onTap: onTap,
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: padding,
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: UiConstants.radiusFull,
border: _getBorder(),
),
child: content,
),
);
}
Color _getBackgroundColor() {
if (!isSelected && variant == UiChipVariant.primary) {
return UiColors.white;
}
switch (variant) {
case UiChipVariant.primary:
return UiColors.primary;
case UiChipVariant.secondary:
return UiColors.tagInProgress;
case UiChipVariant.accent:
return UiColors.accent;
}
}
Color _getContentColor() {
if (!isSelected && variant == UiChipVariant.primary) {
return UiColors.textSecondary;
}
switch (variant) {
case UiChipVariant.primary:
return UiColors.white;
case UiChipVariant.secondary:
return UiColors.primary;
case UiChipVariant.accent:
return UiColors.accentForeground;
}
}
TextStyle _getTextStyle() {
switch (size) {
case UiChipSize.small:
return UiTypography.body3r;
case UiChipSize.medium:
return UiTypography.body2m;
case UiChipSize.large:
return UiTypography.body1m;
}
}
EdgeInsets _getPadding() {
switch (size) {
case UiChipSize.small:
return const EdgeInsets.symmetric(horizontal: 10, vertical: 6);
case UiChipSize.medium:
return const EdgeInsets.symmetric(horizontal: 12, vertical: 8);
case UiChipSize.large:
return const EdgeInsets.symmetric(horizontal: 16, vertical: 10);
}
}
double _getIconSize() {
switch (size) {
case UiChipSize.small:
return 12;
case UiChipSize.medium:
return 16;
case UiChipSize.large:
return 20;
}
}
double _getGap() {
switch (size) {
case UiChipSize.small:
return UiConstants.space1;
case UiChipSize.medium:
return UiConstants.space1 + 2;
case UiChipSize.large:
return UiConstants.space2;
}
}
BoxBorder? _getBorder() {
if (!isSelected && variant == UiChipVariant.primary) {
return Border.all(color: UiColors.border);
}
return null;
}
}

View File

@@ -0,0 +1,89 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import '../ui_colors.dart';
import '../ui_constants.dart';
/// A custom icon button with blur effect and different variants.
class UiIconButton extends StatelessWidget {
/// The icon to display.
final IconData icon;
/// The size of the icon button.
final double size;
/// The size of the icon.
final double iconSize;
/// The background color of the button.
final Color backgroundColor;
/// The color of the icon.
final Color iconColor;
/// Whether to apply blur effect.
final bool useBlur;
/// Callback when the button is tapped.
final VoidCallback? onTap;
/// Creates a [UiIconButton] with custom properties.
const UiIconButton({
super.key,
required this.icon,
this.size = 40,
this.iconSize = 20,
required this.backgroundColor,
required this.iconColor,
this.useBlur = false,
this.onTap,
});
/// Creates a primary variant icon button with solid background.
const UiIconButton.primary({
super.key,
required this.icon,
this.size = 40,
this.iconSize = 20,
this.onTap,
}) : backgroundColor = UiColors.primary,
iconColor = UiColors.white,
useBlur = false;
/// Creates a secondary variant icon button with blur effect.
UiIconButton.secondary({
super.key,
required this.icon,
this.size = 40,
this.iconSize = 20,
this.onTap,
}) : backgroundColor = UiColors.primary.withAlpha(96),
iconColor = UiColors.primary,
useBlur = true;
@override
/// Builds the icon button UI.
Widget build(BuildContext context) {
final Widget button = Container(
width: size,
height: size,
decoration: BoxDecoration(color: backgroundColor, shape: BoxShape.circle),
child: Icon(icon, color: iconColor, size: iconSize),
);
final Widget content = useBlur
? ClipRRect(
borderRadius: UiConstants.radiusFull,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: button,
),
)
: button;
if (onTap != null) {
return GestureDetector(onTap: onTap, child: content);
}
return content;
}
}

View File

@@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import '../ui_colors.dart';
import '../ui_constants.dart';
import '../ui_icons.dart';
/// A widget that displays a horizontal step indicator with icons.
///
/// This widget shows a series of circular step indicators connected by lines,
/// with different visual states for completed, active, and inactive steps.
class UiStepIndicator extends StatelessWidget {
/// The list of icons to display for each step.
final List<IconData> stepIcons;
/// The index of the currently active step (0-based).
final int currentStep;
/// Creates a [UiStepIndicator].
const UiStepIndicator({
super.key,
required this.stepIcons,
required this.currentStep,
});
@override
/// Builds the step indicator UI.
Widget build(BuildContext context) {
// active step color
const Color activeColor = UiColors.primary;
// completed step color
const Color completedColor = UiColors.textSuccess;
// inactive step color
const Color inactiveColor = UiColors.iconSecondary;
return Padding(
padding: const EdgeInsets.symmetric(vertical: UiConstants.space2),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(stepIcons.length, (index) {
final bool isActive = index == currentStep;
final bool isCompleted = index < currentStep;
Color bgColor;
Color iconColor;
if (isCompleted) {
bgColor = completedColor.withAlpha(24);
iconColor = completedColor;
} else if (isActive) {
bgColor = activeColor.withAlpha(24);
iconColor = activeColor;
} else {
bgColor = inactiveColor.withAlpha(24);
iconColor = inactiveColor.withAlpha(128);
}
return Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: bgColor,
shape: BoxShape.circle,
),
child: Icon(
isCompleted ? UiIcons.check : stepIcons[index],
size: 20,
color: iconColor,
),
),
if (index < stepIcons.length - 1)
Container(
width: 30,
height: 2,
margin: const EdgeInsets.symmetric(
horizontal: UiConstants.space1,
),
color: isCompleted
? completedColor.withAlpha(96)
: inactiveColor.withAlpha(96),
),
],
);
}),
),
);
}
}

View File

@@ -0,0 +1,115 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../ui_typography.dart';
import '../ui_constants.dart';
import '../ui_colors.dart';
/// A custom TextField for the Krow UI design system.
///
/// This widget combines a label and a [TextField] with consistent styling.
class UiTextField extends StatelessWidget {
/// The label text to display above the text field.
final String? label;
/// The hint text to display inside the text field when empty.
final String? hintText;
/// Signature for the callback that is called when the text in the field changes.
final ValueChanged<String>? onChanged;
/// The controller for the text field.
final TextEditingController? controller;
/// The type of keyboard to use for editing the text.
final TextInputType? keyboardType;
/// The maximum number of lines for the text field. Defaults to 1.
final int? maxLines;
/// Whether to hide the text being edited (e.g., for passwords). Defaults to false.
final bool obscureText;
/// The type of action button to use for the keyboard.
final TextInputAction? textInputAction;
/// Signature for the callback that is called when the user submits the text field.
final ValueChanged<String>? onSubmitted;
/// Whether the text field should be focused automatically. Defaults to false.
final bool autofocus;
/// Optional input formatters to validate or format the text as it is typed.
final List<TextInputFormatter>? inputFormatters;
/// Optional prefix icon to display at the start of the text field.
final IconData? prefixIcon;
/// Optional suffix icon to display at the end of the text field.
final IconData? suffixIcon;
/// Optional custom suffix widget to display at the end (e.g., password toggle).
final Widget? suffix;
/// Whether the text field should be read-only.
final bool readOnly;
/// Callback when the text field is tapped.
final VoidCallback? onTap;
const UiTextField({
super.key,
this.label,
this.hintText,
this.onChanged,
this.controller,
this.keyboardType,
this.maxLines = 1,
this.obscureText = false,
this.textInputAction,
this.onSubmitted,
this.autofocus = false,
this.inputFormatters,
this.prefixIcon,
this.suffixIcon,
this.suffix,
this.readOnly = false,
this.onTap,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (label != null) ...[
Text(label!, style: UiTypography.body4m.textSecondary),
const SizedBox(height: UiConstants.space1),
],
TextField(
controller: controller,
onChanged: onChanged,
keyboardType: keyboardType,
maxLines: maxLines,
obscureText: obscureText,
textInputAction: textInputAction,
onSubmitted: onSubmitted,
autofocus: autofocus,
inputFormatters: inputFormatters,
readOnly: readOnly,
onTap: onTap,
style: UiTypography.body1r.textPrimary,
decoration: InputDecoration(
hintText: hintText,
prefixIcon: prefixIcon != null
? Icon(prefixIcon, size: 20, color: UiColors.iconSecondary)
: null,
suffixIcon: suffixIcon != null
? Icon(suffixIcon, size: 20, color: UiColors.iconSecondary)
: suffix,
),
),
],
);
}
}