Move apps to mobile directory structure
Relocated all app directories (client, design_system_viewer, staff) and their contents under the new 'apps/mobile' path. This change improves project organization and prepares for future platform-specific structuring.
This commit is contained in:
31
apps/mobile/packages/design_system/.gitignore
vendored
Normal file
31
apps/mobile/packages/design_system/.gitignore
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins-dependencies
|
||||
/build/
|
||||
/coverage/
|
||||
10
apps/mobile/packages/design_system/.metadata
Normal file
10
apps/mobile/packages/design_system/.metadata
Normal file
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "3b62efc2a3da49882f43c372e0bc53daef7295a6"
|
||||
channel: "stable"
|
||||
|
||||
project_type: package
|
||||
3
apps/mobile/packages/design_system/CHANGELOG.md
Normal file
3
apps/mobile/packages/design_system/CHANGELOG.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
||||
1
apps/mobile/packages/design_system/LICENSE
Normal file
1
apps/mobile/packages/design_system/LICENSE
Normal file
@@ -0,0 +1 @@
|
||||
TODO: Add your license here.
|
||||
39
apps/mobile/packages/design_system/README.md
Normal file
39
apps/mobile/packages/design_system/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
<!--
|
||||
This README describes the package. If you publish this package to pub.dev,
|
||||
this README's contents appear on the landing page for your package.
|
||||
|
||||
For information about how to write a good package README, see the guide for
|
||||
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
|
||||
|
||||
For general information about developing packages, see the Dart guide for
|
||||
[creating packages](https://dart.dev/guides/libraries/create-packages)
|
||||
and the Flutter guide for
|
||||
[developing packages and plugins](https://flutter.dev/to/develop-packages).
|
||||
-->
|
||||
|
||||
TODO: Put a short description of the package here that helps potential users
|
||||
know whether this package might be useful for them.
|
||||
|
||||
## Features
|
||||
|
||||
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||
|
||||
## Getting started
|
||||
|
||||
TODO: List prerequisites and provide or point to information on how to
|
||||
start using the package.
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Include short and useful examples for package users. Add longer examples
|
||||
to `/example` folder.
|
||||
|
||||
```dart
|
||||
const like = 'sample';
|
||||
```
|
||||
|
||||
## Additional information
|
||||
|
||||
TODO: Tell users more about the package: where to find more information, how to
|
||||
contribute to the package, how to file issues, what response they can expect
|
||||
from the package authors, and more.
|
||||
1
apps/mobile/packages/design_system/analysis_options.yaml
Normal file
1
apps/mobile/packages/design_system/analysis_options.yaml
Normal file
@@ -0,0 +1 @@
|
||||
include: ../../analytics_options.yaml
|
||||
BIN
apps/mobile/packages/design_system/assets/logo-blue.png
Normal file
BIN
apps/mobile/packages/design_system/assets/logo-blue.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
BIN
apps/mobile/packages/design_system/assets/logo-yellow.png
Normal file
BIN
apps/mobile/packages/design_system/assets/logo-yellow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
12
apps/mobile/packages/design_system/lib/design_system.dart
Normal file
12
apps/mobile/packages/design_system/lib/design_system.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
export 'src/ui_colors.dart';
|
||||
export 'src/ui_typography.dart';
|
||||
export 'src/ui_constants.dart';
|
||||
export 'src/ui_theme.dart';
|
||||
export 'src/ui_icons.dart';
|
||||
export 'src/ui_images_assets.dart';
|
||||
export 'src/widgets/ui_app_bar.dart';
|
||||
export 'src/widgets/ui_text_field.dart';
|
||||
export 'src/widgets/ui_step_indicator.dart';
|
||||
export 'src/widgets/ui_icon_button.dart';
|
||||
export 'src/widgets/ui_button.dart';
|
||||
export 'src/widgets/ui_chip.dart';
|
||||
322
apps/mobile/packages/design_system/lib/src/ui_colors.dart
Normal file
322
apps/mobile/packages/design_system/lib/src/ui_colors.dart
Normal file
@@ -0,0 +1,322 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Static definitions of color palettes and semantic colors for the Staff Design System.
|
||||
/// Values are defined in design_tokens_react.md.
|
||||
class UiColors {
|
||||
UiColors._();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 1. Base Tokens
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Background color (#FAFBFC)
|
||||
static const Color background = Color(0xFFFAFBFC);
|
||||
|
||||
/// Foreground color (#121826)
|
||||
static const Color foreground = Color(0xFF121826);
|
||||
|
||||
/// Primary brand color blue (#0A39DF)
|
||||
static const Color primary = Color(0xFF0A39DF);
|
||||
|
||||
/// Foreground color on primary background (#F7FAFC)
|
||||
static const Color primaryForeground = Color(0xFFF7FAFC);
|
||||
|
||||
/// Inverse primary color (#9FABF1)
|
||||
static const Color primaryInverse = Color(0xFF9FABF1);
|
||||
|
||||
/// Secondary background color (#F1F3F5)
|
||||
static const Color secondary = Color(0xFFF1F3F5);
|
||||
|
||||
/// Foreground color on secondary background (#121826)
|
||||
static const Color secondaryForeground = Color(0xFF121826);
|
||||
|
||||
/// Muted background color (#F1F3F5)
|
||||
static const Color muted = Color(0xFFF1F3F5);
|
||||
|
||||
/// Muted foreground color (#6A7382)
|
||||
static const Color mutedForeground = Color(0xFF6A7382);
|
||||
|
||||
/// Accent yellow color (#F9E547)
|
||||
static const Color accent = Color(0xFFF9E547);
|
||||
|
||||
/// Foreground color on accent background (#4C460D)
|
||||
static const Color accentForeground = Color(0xFF4C460D);
|
||||
|
||||
/// Destructive red color (#F04444)
|
||||
static const Color destructive = Color(0xFFF04444);
|
||||
|
||||
/// Foreground color on destructive background (#FAFAFA)
|
||||
static const Color destructiveForeground = Color(0xFFFAFAFA);
|
||||
|
||||
/// Default border color (#D1D5DB)
|
||||
static const Color border = Color(0xFFD1D5DB);
|
||||
|
||||
/// Default input border color (#E7EAEE)
|
||||
static const Color input = Color(0xFFF5F6F8);
|
||||
|
||||
/// Focus ring color (#0A39DF)
|
||||
static const Color ring = Color(0xFF0A39DF);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 2. Semantic Mappings
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// --- Background Colors ---
|
||||
|
||||
/// Primary background (#FAFBFC)
|
||||
static const Color bgPrimary = background;
|
||||
|
||||
/// Secondary background (#F1F3F5)
|
||||
static const Color bgSecondary = secondary;
|
||||
|
||||
/// Tertiary background (#EDF0F2)
|
||||
static const Color bgThird = Color(0xFFEDF0F2);
|
||||
|
||||
/// Popup background (#FFFFFF)
|
||||
static const Color bgPopup = Color(0xFFFFFFFF);
|
||||
|
||||
/// Highlighted background (#FEF9C3)
|
||||
static const Color bgHighlight = Color(0xFFFEF9C3);
|
||||
|
||||
/// Menu background (#F8FAFC)
|
||||
static const Color bgMenu = Color(0xFFF8FAFC);
|
||||
|
||||
/// Banner background (#FFFFFF)
|
||||
static const Color bgBanner = Color(0xFFFFFFFF);
|
||||
|
||||
/// Overlay background (#000000 with 50% opacity)
|
||||
static const Color bgOverlay = Color(0x80000000);
|
||||
|
||||
/// Toast background (#121826)
|
||||
static const Color toastBg = Color(0xFF121826);
|
||||
|
||||
/// Input field background (#E3E6E9)
|
||||
static const Color bgInputField = input;
|
||||
|
||||
/// Footer banner background (#F1F5F9)
|
||||
static const Color bgFooterBanner = Color(0xFFF1F5F9);
|
||||
|
||||
// --- Text Colors ---
|
||||
|
||||
/// Primary text (#121826)
|
||||
static const Color textPrimary = foreground;
|
||||
|
||||
/// Secondary text (#6A7382)
|
||||
static const Color textSecondary = mutedForeground;
|
||||
|
||||
/// Inactive text (#9CA3AF)
|
||||
static const Color textInactive = Color(0xFF9CA3AF);
|
||||
|
||||
/// Placeholder text (#9CA3AF)
|
||||
static const Color textPlaceholder = Color(0xFF9CA3AF);
|
||||
|
||||
/// Description text (#6A7382)
|
||||
static const Color textDescription = mutedForeground;
|
||||
|
||||
/// Success text (#10B981)
|
||||
static const Color textSuccess = Color(0xFF10B981);
|
||||
|
||||
/// Error text (#F04444)
|
||||
static const Color textError = destructive;
|
||||
|
||||
/// Deep error text for containers (#450A0A)
|
||||
static const Color textErrorContainer = Color(0xFF450A0A);
|
||||
|
||||
/// Warning text (#D97706)
|
||||
static const Color textWarning = Color(0xFFD97706);
|
||||
|
||||
/// Link text (#0A39DF)
|
||||
static const Color textLink = primary;
|
||||
|
||||
/// Filter text (#4B5563)
|
||||
static const Color textFilter = Color(0xFF4B5563);
|
||||
|
||||
// --- Icon Colors ---
|
||||
|
||||
/// Primary icon (#121826)
|
||||
static const Color iconPrimary = foreground;
|
||||
|
||||
/// Secondary icon (#6A7382)
|
||||
static const Color iconSecondary = mutedForeground;
|
||||
|
||||
/// Tertiary icon (#9CA3AF)
|
||||
static const Color iconThird = Color(0xFF9CA3AF);
|
||||
|
||||
/// Inactive icon (#D1D5DB)
|
||||
static const Color iconInactive = Color(0xFFD1D5DB);
|
||||
|
||||
/// Active icon (#0A39DF)
|
||||
static const Color iconActive = primary;
|
||||
|
||||
/// Success icon (#10B981)
|
||||
static const Color iconSuccess = Color(0xFF10B981);
|
||||
|
||||
/// Error icon (#F04444)
|
||||
static const Color iconError = destructive;
|
||||
|
||||
// --- Loader Colors ---
|
||||
|
||||
/// Active loader (#0A39DF)
|
||||
static const Color loaderActive = primary;
|
||||
|
||||
/// Inactive loader (#E2E8F0)
|
||||
static const Color loaderInactive = Color(0xFFE2E8F0);
|
||||
|
||||
// --- Pin Input Colors ---
|
||||
|
||||
/// Unfilled pin (#E2E8F0)
|
||||
static const Color pinUnfilled = Color(0xFFE2E8F0);
|
||||
|
||||
/// Active pin (#0A39DF)
|
||||
static const Color pinActive = primary;
|
||||
|
||||
/// Inactive pin (#94A3B8)
|
||||
static const Color pinInactive = Color(0xFF94A3B8);
|
||||
|
||||
// --- Separator Colors ---
|
||||
|
||||
/// Primary separator (#E3E6E9)
|
||||
static const Color separatorPrimary = border;
|
||||
|
||||
/// Secondary separator (#F1F5F9)
|
||||
static const Color separatorSecondary = Color(0xFFF1F5F9);
|
||||
|
||||
/// Special separator (#F9E547)
|
||||
static const Color separatorSpecial = accent;
|
||||
|
||||
// --- Tag Colors ---
|
||||
|
||||
/// Default tag background (#F1F5F9)
|
||||
static const Color tagValue = Color(0xFFF1F5F9);
|
||||
|
||||
/// Pending state tag background (#FEF3C7)
|
||||
static const Color tagPending = Color(0xFFFEF3C7);
|
||||
|
||||
/// In-progress state tag background (#DBEAFE)
|
||||
static const Color tagInProgress = Color(0xFFDBEAFE);
|
||||
|
||||
/// Error state tag background (#FEE2E2)
|
||||
static const Color tagError = Color(0xFFFEE2E2);
|
||||
|
||||
/// Active state tag background (#DCFCE7)
|
||||
static const Color tagActive = Color(0xFFDCFCE7);
|
||||
|
||||
/// Frozen state tag background (#F3F4F6)
|
||||
static const Color tagFreeze = Color(0xFFF3F4F6);
|
||||
|
||||
/// Success state tag background (#DCFCE7)
|
||||
static const Color tagSuccess = Color(0xFFDCFCE7);
|
||||
|
||||
/// Refunded state tag background (#E0E7FF)
|
||||
static const Color tagRefunded = Color(0xFFE0E7FF);
|
||||
|
||||
// --- Border Colors ---
|
||||
|
||||
/// Static border (#D1D5DB)
|
||||
static const Color borderStill = border;
|
||||
|
||||
/// Primary border (#D1D5DB)
|
||||
static const Color borderPrimary = border;
|
||||
|
||||
/// Error border (#F04444)
|
||||
static const Color borderError = destructive;
|
||||
|
||||
/// Focus border (#0A39DF)
|
||||
static const Color borderFocus = ring;
|
||||
|
||||
/// Inactive border (#F1F5F9)
|
||||
static const Color borderInactive = Color(0xFFF1F5F9);
|
||||
|
||||
// --- Button Colors ---
|
||||
|
||||
/// Primary button default (#0A39DF)
|
||||
static const Color buttonPrimaryStill = primary;
|
||||
|
||||
/// Primary button hover (#082EB2)
|
||||
static const Color buttonPrimaryHover = Color(0xFF082EB2);
|
||||
|
||||
/// Primary button inactive (#F1F3F5)
|
||||
static const Color buttonPrimaryInactive = secondary;
|
||||
|
||||
/// Secondary button default (#F1F3F5)
|
||||
static const Color buttonSecondaryStill = secondary;
|
||||
|
||||
/// Secondary button hover (#E2E8F0)
|
||||
static const Color buttonSecondaryHover = Color(0xFFE2E8F0);
|
||||
|
||||
/// Secondary button inactive (#F3F4F6)
|
||||
static const Color buttonSecondaryInactive = Color(0xFFF3F4F6);
|
||||
|
||||
/// Button inactive state (#94A3B8)
|
||||
static const Color buttonInactive = Color(0xFF94A3B8);
|
||||
|
||||
/// Pin button background (#F8FAFC)
|
||||
static const Color pinButtonBackground = Color(0xFFF8FAFC);
|
||||
|
||||
// --- Switch Colors ---
|
||||
|
||||
/// Switch active state (#10B981)
|
||||
static const Color switchActive = Color(0xFF10B981);
|
||||
|
||||
/// Switch inactive state (#CBD5E1)
|
||||
static const Color switchInactive = Color(0xFFCBD5E1);
|
||||
|
||||
/// Switch dot inactive state (#FFFFFF)
|
||||
static const Color dotInactive = Color(0xFFFFFFFF);
|
||||
|
||||
// --- Basic Colors ---
|
||||
|
||||
/// Standard white (#FFFFFF)
|
||||
static const Color white = Color(0xFFFFFFFF);
|
||||
|
||||
/// Standard black (#000000)
|
||||
static const Color black = Color(0xFF000000);
|
||||
|
||||
/// Transparent color (0x00000000)
|
||||
static const Color transparent = Color(0x00000000);
|
||||
|
||||
/// Card background (#FFFFFF)
|
||||
static const Color cardViewBackground = Color(0xFFFFFFFF);
|
||||
|
||||
// --- Shadows ---
|
||||
|
||||
/// Primary popup shadow (#000000 with 10% opacity)
|
||||
static const Color popupShadow = Color(0x1A000000);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 3. ColorScheme
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Generates a ColorScheme based on the tokens.
|
||||
static ColorScheme get colorScheme => const ColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primary: primary,
|
||||
onPrimary: primaryForeground,
|
||||
primaryContainer: tagRefunded,
|
||||
onPrimaryContainer: primary,
|
||||
secondary: secondary,
|
||||
onSecondary: secondaryForeground,
|
||||
secondaryContainer: muted,
|
||||
onSecondaryContainer: secondaryForeground,
|
||||
tertiary: accent,
|
||||
onTertiary: accentForeground,
|
||||
tertiaryContainer: bgHighlight,
|
||||
onTertiaryContainer: accentForeground,
|
||||
error: destructive,
|
||||
onError: destructiveForeground,
|
||||
errorContainer: tagError,
|
||||
onErrorContainer: textErrorContainer,
|
||||
surface: background,
|
||||
onSurface: foreground,
|
||||
surfaceContainerHighest: muted,
|
||||
onSurfaceVariant: mutedForeground,
|
||||
outline: border,
|
||||
outlineVariant: separatorSecondary,
|
||||
shadow: black,
|
||||
scrim: black,
|
||||
inverseSurface: foreground,
|
||||
onInverseSurface: background,
|
||||
inversePrimary: primaryInverse,
|
||||
surfaceTint: primary,
|
||||
);
|
||||
}
|
||||
38
apps/mobile/packages/design_system/lib/src/ui_constants.dart
Normal file
38
apps/mobile/packages/design_system/lib/src/ui_constants.dart
Normal file
@@ -0,0 +1,38 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Design system constants for spacing, radii, and other layout properties.
|
||||
class UiConstants {
|
||||
UiConstants._();
|
||||
|
||||
// --- Border Radii ---
|
||||
|
||||
/// Base radius: 12px
|
||||
static const double radiusBase = 12.0;
|
||||
static final BorderRadius radiusLg = BorderRadius.circular(radiusBase);
|
||||
|
||||
/// Medium radius: 6px
|
||||
static const double radiusMdValue = 6.0;
|
||||
static final BorderRadius radiusMd = BorderRadius.circular(radiusMdValue);
|
||||
|
||||
/// Small radius: 4px
|
||||
static final BorderRadius radiusSm = BorderRadius.circular(4.0);
|
||||
|
||||
/// Extra small radius: 2px
|
||||
static final BorderRadius radiusXs = BorderRadius.circular(2.0);
|
||||
|
||||
/// Large/Full radius
|
||||
static final BorderRadius radiusFull = BorderRadius.circular(999.0);
|
||||
|
||||
// --- Spacing ---
|
||||
|
||||
static const double space0 = 0.0;
|
||||
static const double space1 = 4.0;
|
||||
static const double space2 = 8.0;
|
||||
static const double space3 = 12.0;
|
||||
static const double space4 = 16.0;
|
||||
static const double space5 = 20.0;
|
||||
static const double space6 = 24.0;
|
||||
static const double space8 = 32.0;
|
||||
static const double space10 = 40.0;
|
||||
static const double space12 = 48.0;
|
||||
}
|
||||
177
apps/mobile/packages/design_system/lib/src/ui_icons.dart
Normal file
177
apps/mobile/packages/design_system/lib/src/ui_icons.dart
Normal file
@@ -0,0 +1,177 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
|
||||
/// The primary icon library used by the design system.
|
||||
/// This allows for easier swapping of icon libraries in the future.
|
||||
typedef _IconLib = LucideIcons;
|
||||
|
||||
/// The secondary icon library used by the design system.
|
||||
/// This allows for easier swapping of icon libraries in the future.
|
||||
typedef _IconLib2 = FontAwesomeIcons;
|
||||
|
||||
/// Static definitions of icons for the UI design system.
|
||||
/// This class wraps the primary icon library to provide a consistent interface.
|
||||
///
|
||||
/// example:
|
||||
/// ```dart
|
||||
/// Icon(UiIcons.home)
|
||||
/// ```
|
||||
class UiIcons {
|
||||
UiIcons._();
|
||||
|
||||
// --- Navigation ---
|
||||
|
||||
/// Home icon
|
||||
static const IconData home = _IconLib.home;
|
||||
|
||||
/// Calendar icon for shifts or schedules
|
||||
static const IconData calendar = _IconLib.calendar;
|
||||
|
||||
/// Briefcase icon for jobs
|
||||
static const IconData briefcase = _IconLib.briefcase;
|
||||
|
||||
/// User icon for profile
|
||||
static const IconData user = _IconLib.user;
|
||||
|
||||
/// Settings icon
|
||||
static const IconData settings = _IconLib.settings;
|
||||
|
||||
// --- Actions ---
|
||||
|
||||
/// Search icon
|
||||
static const IconData search = _IconLib.search;
|
||||
|
||||
/// Filter icon
|
||||
static const IconData filter = _IconLib.filter;
|
||||
|
||||
/// Plus/Add icon
|
||||
static const IconData add = _IconLib.plus;
|
||||
|
||||
/// Edit icon
|
||||
static const IconData edit = _IconLib.edit2;
|
||||
|
||||
/// Delete/Trash icon
|
||||
static const IconData delete = _IconLib.trash2;
|
||||
|
||||
/// Checkmark icon
|
||||
static const IconData check = _IconLib.check;
|
||||
|
||||
/// X/Cancel icon
|
||||
static const IconData close = _IconLib.x;
|
||||
|
||||
/// Arrow right icon
|
||||
static const IconData arrowRight = _IconLib.arrowRight;
|
||||
|
||||
/// Arrow left icon
|
||||
static const IconData arrowLeft = _IconLib.arrowLeft;
|
||||
|
||||
/// Swap/Transfer icon
|
||||
static const IconData swap = _IconLib.arrowLeftRight;
|
||||
|
||||
/// Chevron right icon
|
||||
static const IconData chevronRight = _IconLib.chevronRight;
|
||||
|
||||
/// Chevron left icon
|
||||
static const IconData chevronLeft = _IconLib.chevronLeft;
|
||||
|
||||
// --- Status & Feedback ---
|
||||
|
||||
/// Info icon
|
||||
static const IconData info = _IconLib.info;
|
||||
|
||||
/// Help/Circle icon
|
||||
static const IconData help = _IconLib.helpCircle;
|
||||
|
||||
/// Alert/Triangle icon for warnings
|
||||
static const IconData warning = _IconLib.alertTriangle;
|
||||
|
||||
/// Alert/Circle icon for errors
|
||||
static const IconData error = _IconLib.alertCircle;
|
||||
|
||||
/// Success/Check circle icon
|
||||
static const IconData success = _IconLib.checkCircle2;
|
||||
|
||||
// --- Miscellaneous ---
|
||||
|
||||
/// Clock icon
|
||||
static const IconData clock = _IconLib.clock;
|
||||
|
||||
/// Log in icon
|
||||
static const IconData logIn = _IconLib.logIn;
|
||||
|
||||
/// Break icon (Coffee)
|
||||
static const IconData breakIcon = _IconLib.coffee;
|
||||
|
||||
/// Map pin icon for locations
|
||||
static const IconData mapPin = _IconLib.mapPin;
|
||||
|
||||
/// Dollar sign icon for payments/earnings
|
||||
static const IconData dollar = _IconLib.dollarSign;
|
||||
|
||||
/// Wallet icon
|
||||
static const IconData wallet = _IconLib.wallet;
|
||||
|
||||
/// Credit card icon
|
||||
static const IconData creditCard = _IconLib.creditCard;
|
||||
|
||||
/// Bell icon for notifications
|
||||
static const IconData bell = _IconLib.bell;
|
||||
|
||||
/// Log out icon
|
||||
static const IconData logOut = _IconLib.logOut;
|
||||
|
||||
/// File/Document icon
|
||||
static const IconData file = _IconLib.fileText;
|
||||
|
||||
/// Lock icon
|
||||
static const IconData lock = _IconLib.lock;
|
||||
|
||||
/// Shield check icon for compliance/security
|
||||
static const IconData shield = _IconLib.shieldCheck;
|
||||
|
||||
/// Sparkles icon for features or AI
|
||||
static const IconData sparkles = _IconLib.sparkles;
|
||||
|
||||
/// Star icon for ratings
|
||||
static const IconData star = _IconLib.star;
|
||||
|
||||
/// Camera icon for photo upload
|
||||
static const IconData camera = _IconLib.camera;
|
||||
|
||||
/// Mail icon
|
||||
static const IconData mail = _IconLib.mail;
|
||||
|
||||
/// Eye icon for visibility
|
||||
static const IconData eye = _IconLib.eye;
|
||||
|
||||
/// Eye off icon for hidden visibility
|
||||
static const IconData eyeOff = _IconLib.eyeOff;
|
||||
|
||||
/// Building icon for companies
|
||||
static const IconData building = _IconLib.building2;
|
||||
|
||||
/// Zap icon for rapid actions
|
||||
static const IconData zap = _IconLib.zap;
|
||||
|
||||
/// Grip vertical icon for reordering
|
||||
static const IconData gripVertical = _IconLib.gripVertical;
|
||||
|
||||
/// Trending down icon for insights
|
||||
static const IconData trendingDown = _IconLib.trendingDown;
|
||||
|
||||
/// Target icon for metrics
|
||||
static const IconData target = _IconLib.target;
|
||||
|
||||
/// Rotate CCW icon for reordering
|
||||
static const IconData rotateCcw = _IconLib.rotateCcw;
|
||||
|
||||
/// Apple icon
|
||||
static const IconData apple = _IconLib2.apple;
|
||||
|
||||
/// Google icon
|
||||
static const IconData google = _IconLib2.google;
|
||||
|
||||
/// NFC icon
|
||||
static const IconData nfc = _IconLib.nfc;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/// Static definitions of image asset paths for the Design System.
|
||||
///
|
||||
/// This class provides a centralized way to access image assets
|
||||
/// stored within the `design_system` package.
|
||||
class UiImageAssets {
|
||||
UiImageAssets._();
|
||||
|
||||
/// The path to the yellow version of the logo image.
|
||||
static const String logoYellow =
|
||||
'packages/design_system/assets/logo-yellow.png';
|
||||
|
||||
/// The path to the blue version of the logo image.
|
||||
static const String logoBlue = 'packages/design_system/assets/logo-blue.png';
|
||||
}
|
||||
359
apps/mobile/packages/design_system/lib/src/ui_theme.dart
Normal file
359
apps/mobile/packages/design_system/lib/src/ui_theme.dart
Normal file
@@ -0,0 +1,359 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'ui_colors.dart';
|
||||
import 'ui_typography.dart';
|
||||
import 'ui_constants.dart';
|
||||
|
||||
/// The main entry point for the Staff Design System theme.
|
||||
/// Assembles colors, typography, and constants into a comprehensive Material 3 theme.
|
||||
///
|
||||
/// Adheres to the tokens defined in design_tokens_react.md.
|
||||
class UiTheme {
|
||||
UiTheme._();
|
||||
|
||||
/// Returns the light theme for the Staff application.
|
||||
static ThemeData get light {
|
||||
final colorScheme = UiColors.colorScheme;
|
||||
final textTheme = UiTypography.textTheme;
|
||||
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: colorScheme,
|
||||
scaffoldBackgroundColor: UiColors.background,
|
||||
primaryColor: UiColors.primary,
|
||||
canvasColor: UiColors.background,
|
||||
|
||||
// Typography
|
||||
textTheme: textTheme,
|
||||
|
||||
// Icon Theme
|
||||
iconTheme: const IconThemeData(color: UiColors.iconPrimary, size: 24),
|
||||
|
||||
// Text Selection Theme
|
||||
textSelectionTheme: const TextSelectionThemeData(
|
||||
cursorColor: UiColors.primary,
|
||||
selectionColor: UiColors.primaryInverse,
|
||||
selectionHandleColor: UiColors.primary,
|
||||
),
|
||||
|
||||
// Divider Theme
|
||||
dividerTheme: const DividerThemeData(
|
||||
color: UiColors.separatorPrimary,
|
||||
space: 1,
|
||||
thickness: 1,
|
||||
),
|
||||
|
||||
// Card Theme
|
||||
cardTheme: CardThemeData(
|
||||
color: UiColors.white,
|
||||
elevation: 2,
|
||||
shadowColor: UiColors.popupShadow,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
side: const BorderSide(color: UiColors.borderStill),
|
||||
),
|
||||
margin: EdgeInsets.zero,
|
||||
),
|
||||
|
||||
// Elevated Button Theme (Primary)
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style:
|
||||
ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
backgroundColor: UiColors.buttonPrimaryStill,
|
||||
foregroundColor: UiColors.primaryForeground,
|
||||
disabledBackgroundColor: UiColors.buttonPrimaryInactive,
|
||||
textStyle: UiTypography.buttonXL,
|
||||
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusLg),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space6,
|
||||
vertical: UiConstants.space3,
|
||||
),
|
||||
minimumSize: const Size(double.infinity, 54),
|
||||
maximumSize: const Size(double.infinity, 54),
|
||||
).copyWith(
|
||||
side: WidgetStateProperty.resolveWith<BorderSide?>((states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return const BorderSide(
|
||||
color: UiColors.borderPrimary,
|
||||
width: 0.5,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
overlayColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.hovered))
|
||||
return UiColors.buttonPrimaryHover;
|
||||
return null;
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
// Text Button Theme
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: UiColors.textPrimary,
|
||||
disabledForegroundColor: UiColors.textInactive,
|
||||
textStyle: UiTypography.buttonXL,
|
||||
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusLg),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space4,
|
||||
vertical: UiConstants.space2,
|
||||
),
|
||||
minimumSize: const Size(double.infinity, 52),
|
||||
maximumSize: const Size(double.infinity, 52),
|
||||
),
|
||||
),
|
||||
|
||||
// Outlined Button Theme (Secondary)
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
elevation: 0,
|
||||
backgroundColor: UiColors.buttonSecondaryStill,
|
||||
foregroundColor: UiColors.primary,
|
||||
side: const BorderSide(color: UiColors.borderFocus, width: 0.5),
|
||||
textStyle: UiTypography.buttonXL,
|
||||
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusLg),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space4,
|
||||
vertical: UiConstants.space3,
|
||||
),
|
||||
minimumSize: const Size(double.infinity, 52),
|
||||
maximumSize: const Size(double.infinity, 52),
|
||||
),
|
||||
),
|
||||
|
||||
// Icon Button Theme
|
||||
iconButtonTheme: IconButtonThemeData(
|
||||
style: IconButton.styleFrom(
|
||||
foregroundColor: UiColors.iconPrimary,
|
||||
disabledForegroundColor: UiColors.iconInactive,
|
||||
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusFull),
|
||||
),
|
||||
),
|
||||
|
||||
// Floating Action Button Theme
|
||||
floatingActionButtonTheme: const FloatingActionButtonThemeData(
|
||||
backgroundColor: UiColors.primary,
|
||||
foregroundColor: UiColors.primaryForeground,
|
||||
elevation: 4,
|
||||
shape: CircleBorder(),
|
||||
),
|
||||
|
||||
// Tab Bar Theme
|
||||
tabBarTheme: TabBarThemeData(
|
||||
labelColor: UiColors.primary,
|
||||
unselectedLabelColor: UiColors.textSecondary,
|
||||
labelStyle: UiTypography.buttonM,
|
||||
unselectedLabelStyle: UiTypography.buttonM,
|
||||
indicatorSize: TabBarIndicatorSize.label,
|
||||
indicator: const UnderlineTabIndicator(
|
||||
borderSide: BorderSide(color: UiColors.primary, width: 2),
|
||||
),
|
||||
),
|
||||
|
||||
// Input Theme
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: UiColors.bgInputField,
|
||||
hintStyle: UiTypography.body2r.textPlaceholder,
|
||||
labelStyle: UiTypography.body4r.textPrimary,
|
||||
errorStyle: UiTypography.footnote1r.textError,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space3,
|
||||
vertical: UiConstants.space3,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
borderSide: const BorderSide(color: UiColors.borderStill),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
borderSide: const BorderSide(color: UiColors.borderStill),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
borderSide: const BorderSide(
|
||||
color: UiColors.borderFocus,
|
||||
width: 0.75,
|
||||
),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
borderSide: const BorderSide(color: UiColors.textError),
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
borderSide: const BorderSide(color: UiColors.textError, width: 1),
|
||||
),
|
||||
),
|
||||
|
||||
// List Tile Theme
|
||||
listTileTheme: ListTileThemeData(
|
||||
textColor: UiColors.textPrimary,
|
||||
iconColor: UiColors.iconPrimary,
|
||||
titleTextStyle: UiTypography.body1m,
|
||||
subtitleTextStyle: UiTypography.body2r.textSecondary,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space4,
|
||||
),
|
||||
tileColor: UiColors.transparent,
|
||||
),
|
||||
|
||||
// Badge Theme
|
||||
badgeTheme: BadgeThemeData(
|
||||
backgroundColor: UiColors.primary,
|
||||
textColor: UiColors.primaryForeground,
|
||||
textStyle: UiTypography.footnote2m,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
),
|
||||
|
||||
// App Bar Theme
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: UiColors.background,
|
||||
elevation: 0,
|
||||
titleTextStyle: UiTypography.headline5m.textPrimary,
|
||||
iconTheme: const IconThemeData(color: UiColors.iconThird, size: 20),
|
||||
surfaceTintColor: UiColors.transparent,
|
||||
),
|
||||
|
||||
// Dialog Theme
|
||||
dialogTheme: DialogThemeData(
|
||||
backgroundColor: UiColors.bgPopup,
|
||||
elevation: 8,
|
||||
shadowColor: UiColors.popupShadow,
|
||||
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusLg),
|
||||
titleTextStyle: UiTypography.headline2r.textPrimary,
|
||||
contentTextStyle: UiTypography.body2r.textDescription,
|
||||
),
|
||||
|
||||
// Bottom Navigation Bar Theme
|
||||
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
||||
backgroundColor: UiColors.white,
|
||||
selectedItemColor: UiColors.primary,
|
||||
unselectedItemColor: UiColors.textInactive,
|
||||
selectedLabelStyle: UiTypography.footnote2m,
|
||||
unselectedLabelStyle: UiTypography.footnote2r,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
elevation: 8,
|
||||
),
|
||||
|
||||
// Navigation Bar Theme (Modern M3 Bottom Nav)
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
backgroundColor: UiColors.white,
|
||||
indicatorColor: UiColors.primaryInverse.withAlpha(51), // 20% of 255
|
||||
labelTextStyle: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return UiTypography.footnote2m.textPrimary;
|
||||
}
|
||||
return UiTypography.footnote2r.textInactive;
|
||||
}),
|
||||
),
|
||||
|
||||
// Switch Theme
|
||||
switchTheme: SwitchThemeData(
|
||||
trackColor: WidgetStateProperty.resolveWith<Color>((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return UiColors.switchActive;
|
||||
}
|
||||
return UiColors.switchInactive;
|
||||
}),
|
||||
thumbColor: const WidgetStatePropertyAll(UiColors.white),
|
||||
),
|
||||
|
||||
// Checkbox Theme
|
||||
checkboxTheme: CheckboxThemeData(
|
||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) return UiColors.primary;
|
||||
return null;
|
||||
}),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
|
||||
),
|
||||
|
||||
// Radio Theme
|
||||
radioTheme: RadioThemeData(
|
||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) return UiColors.primary;
|
||||
return null;
|
||||
}),
|
||||
),
|
||||
|
||||
// Slider Theme
|
||||
sliderTheme: const SliderThemeData(
|
||||
activeTrackColor: UiColors.primary,
|
||||
inactiveTrackColor: UiColors.loaderInactive,
|
||||
thumbColor: UiColors.primary,
|
||||
overlayColor: UiColors.primaryInverse,
|
||||
),
|
||||
|
||||
// Chip Theme
|
||||
chipTheme: ChipThemeData(
|
||||
backgroundColor: UiColors.bgSecondary,
|
||||
labelStyle: UiTypography.footnote1m,
|
||||
secondaryLabelStyle: UiTypography.footnote1m.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusMd),
|
||||
side: const BorderSide(color: UiColors.borderStill, width: 0.5),
|
||||
),
|
||||
|
||||
// SnackBar Theme
|
||||
snackBarTheme: SnackBarThemeData(
|
||||
backgroundColor: UiColors.toastBg,
|
||||
contentTextStyle: UiTypography.body2r.white,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusMd),
|
||||
elevation: 4,
|
||||
),
|
||||
|
||||
// Bottom Sheet Theme
|
||||
bottomSheetTheme: const BottomSheetThemeData(
|
||||
backgroundColor: UiColors.bgSecondary,
|
||||
modalBackgroundColor: UiColors.bgSecondary,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(32)),
|
||||
),
|
||||
),
|
||||
|
||||
// Expansion Tile Theme
|
||||
expansionTileTheme: ExpansionTileThemeData(
|
||||
iconColor: UiColors.iconSecondary,
|
||||
collapsedIconColor: UiColors.iconPrimary,
|
||||
backgroundColor: UiColors.bgPopup,
|
||||
collapsedBackgroundColor: UiColors.transparent,
|
||||
textColor: UiColors.textPrimary,
|
||||
collapsedTextColor: UiColors.textPrimary,
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: UiConstants.space4),
|
||||
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusMd),
|
||||
collapsedShape: RoundedRectangleBorder(
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
),
|
||||
),
|
||||
|
||||
// Menu Theme
|
||||
menuTheme: MenuThemeData(
|
||||
style: MenuStyle(
|
||||
backgroundColor: WidgetStateProperty.all(UiColors.bgPopup),
|
||||
elevation: WidgetStateProperty.all(4),
|
||||
shape: WidgetStateProperty.all(
|
||||
RoundedRectangleBorder(borderRadius: UiConstants.radiusMd),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Tooltip Theme
|
||||
tooltipTheme: TooltipThemeData(
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.toastBg.withAlpha(230), // ~90% of 255
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
),
|
||||
textStyle: UiTypography.footnote2r.white,
|
||||
),
|
||||
|
||||
// Progress Indicator Theme
|
||||
progressIndicatorTheme: const ProgressIndicatorThemeData(
|
||||
color: UiColors.primary,
|
||||
linearTrackColor: UiColors.loaderInactive,
|
||||
linearMinHeight: 4,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
564
apps/mobile/packages/design_system/lib/src/ui_typography.dart
Normal file
564
apps/mobile/packages/design_system/lib/src/ui_typography.dart
Normal file
@@ -0,0 +1,564 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'ui_colors.dart';
|
||||
|
||||
/// Static definitions of typography styles for the Staff Design System.
|
||||
class UiTypography {
|
||||
UiTypography._();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 0. Base Font Styles
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// The primary font family used throughout the design system.
|
||||
static final TextStyle _primaryBase = GoogleFonts.instrumentSans();
|
||||
|
||||
/// The secondary font family used for display or specialized elements.
|
||||
static final TextStyle _secondaryBase = GoogleFonts.spaceGrotesk();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 1. Primary Typography (Instrument Sans)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// --- 1.1 Display ---
|
||||
|
||||
/// Display Large - Font: Instrument Sans, Size: 36, Height: 1.1 (#121826)
|
||||
static final TextStyle displayL = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 36,
|
||||
height: 1.1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Display medium - Font: Instrument Sans, Size: 32, Height: 1.1 (#121826)
|
||||
static final TextStyle displayM = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 32,
|
||||
height: 1.1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Display small - Font: Instrument Sans, Size: 32, Height: 1.1 (#121826)
|
||||
static final TextStyle displayMb = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 32,
|
||||
height: 1.1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Display 1 Medium - Font: Instrument Sans, Size: 26, Height: 1.1 (#121826)
|
||||
static final TextStyle display1m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 26,
|
||||
height: 1.1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Display 1 Regular - Font: Instrument Sans, Size: 38, Height: 1.3 (#121826)
|
||||
static final TextStyle display1r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 26,
|
||||
height: 1.3,
|
||||
letterSpacing: -1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Display 1 Bold - Font: Instrument Sans, Size: 38, Height: 1.3 (#121826)
|
||||
static final TextStyle display1b = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 26,
|
||||
height: 1.3,
|
||||
letterSpacing: -1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Display 2 Medium - Font: Instrument Sans, Size: 16, Height: 1.1 (#121826)
|
||||
static final TextStyle display2m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 16,
|
||||
height: 1.1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Display 2 Regular - Font: Instrument Sans, Size: 28, Height: 1.5 (#121826)
|
||||
static final TextStyle display2r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Display 3 Medium - Font: Instrument Sans, Size: 32, Height: 1.1 (#121826)
|
||||
static final TextStyle display3m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
height: 1.1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Display 3 Regular - Font: Instrument Sans, Size: 32, Height: 1.3 (#121826)
|
||||
static final TextStyle display3r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
height: 1.3,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Display 3 Bold - Font: Instrument Sans, Size: 32, Height: 1.1 (#121826)
|
||||
static final TextStyle display3b = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
height: 1.1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
// --- 1.2 Title ---
|
||||
|
||||
/// Title 1 Medium - Font: Instrument Sans, Size: 18, Height: 1.5 (#121826)
|
||||
static final TextStyle title1m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 18,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Title 1 Regular - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
|
||||
static final TextStyle title1r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 18,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Title 2 Bold - Font: Instrument Sans, Size: 20, Height: 1.1 (#121826)
|
||||
static final TextStyle title2b = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
height: 1.1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Title 2 Medium - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
|
||||
static final TextStyle title2m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 16,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Title 2 Regular - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
|
||||
static final TextStyle title2r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
// --- 1.3 Headline ---
|
||||
|
||||
/// Headline 1 Medium - Font: Instrument Sans, Size: 26, Height: 1.5 (#121826)
|
||||
static final TextStyle headline1m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 26,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Headline 1 Regular - Font: Instrument Sans, Size: 26, Height: 1.5 (#121826)
|
||||
static final TextStyle headline1r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 26,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Headline 2 Medium - Font: Instrument Sans, Size: 20, Height: 1.5 (#121826)
|
||||
static final TextStyle headline2m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 22,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Headline 2 Regular - Font: Instrument Sans, Size: 20, Height: 1.5 (#121826)
|
||||
static final TextStyle headline2r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 22,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Headline 3 Medium - Font: Instrument Sans, Size: 22, Height: 1.5 (#121826)
|
||||
static final TextStyle headline3m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 20,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Headline 4 Medium - Font: Instrument Sans, Size: 22, Height: 1.5 (#121826)
|
||||
static final TextStyle headline4m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 18,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Headline 4 Regular - Font: Instrument Sans, Size: 20, Height: 1.5 (#121826)
|
||||
static final TextStyle headline4r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 18,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Headline 5 Regular - Font: Instrument Sans, Size: 18, Height: 1.5 (#121826)
|
||||
static final TextStyle headline5r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Headline 5 Medium - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
|
||||
static final TextStyle headline5m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 16,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
// --- 1.4 Title Uppercase ---
|
||||
|
||||
/// Title Uppercase 2 Medium - Font: Instrument Sans, Size: 14, Height: 1.5, Spacing: 0.7 (#121826)
|
||||
static final TextStyle titleUppercase2m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
height: 1.5,
|
||||
letterSpacing: 0.7,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Title Uppercase 3 Medium - Font: Instrument Sans, Size: 12, Height: 1.5, Spacing: 1.5 (#121826)
|
||||
static final TextStyle titleUppercase3m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
height: 1.5,
|
||||
letterSpacing: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Title Uppercase 4 Medium - Font: Instrument Sans, Size: 11, Height: 1.5, Spacing: 2.2 (#121826)
|
||||
static final TextStyle titleUppercase4m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 11,
|
||||
height: 1.5,
|
||||
letterSpacing: 2.2,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
// --- 1.5 Body ---
|
||||
|
||||
/// Body 1 Bold - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
|
||||
static final TextStyle body1b = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 16,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Body 1 Medium - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
|
||||
static final TextStyle body1m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 16,
|
||||
height: 1.5,
|
||||
letterSpacing: -0.025,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Body 1 Regular - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
|
||||
static final TextStyle body1r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
height: 1.5,
|
||||
letterSpacing: -0.05,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Body 2 Bold - Font: Instrument Sans, Size: 14, Height: 1.5 (#121826)
|
||||
static final TextStyle body2b = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 14,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Body 2 Medium - Font: Instrument Sans, Size: 14, Height: 1.5 (#121826)
|
||||
static final TextStyle body2m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Body 2 Regular - Font: Instrument Sans, Size: 14, Height: 1.5, Spacing: 0.1 (#121826)
|
||||
static final TextStyle body2r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
height: 1.5,
|
||||
letterSpacing: 0.1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Body 3 Regular - Font: Instrument Sans, Size: 14, Height: 1.5, Spacing: -0.1 (#121826)
|
||||
static final TextStyle body3r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
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,
|
||||
fontSize: 12,
|
||||
height: 1.5,
|
||||
letterSpacing: 0.05,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Body 4 Medium - Font: Instrument Sans, Size: 14, Height: 1.5, Spacing: 0.05 (#121826)
|
||||
static final TextStyle body4m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
height: 1.5,
|
||||
letterSpacing: 0.05,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
// --- 1.6 Footnote ---
|
||||
|
||||
/// Footnote 1 Medium - Font: Instrument Sans, Size: 12, Height: 1.5 (#121826)
|
||||
static final TextStyle footnote1m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Footnote 1 Regular - Font: Instrument Sans, Size: 12, Height: 1.5, Spacing: 0.05 (#121826)
|
||||
static final TextStyle footnote1r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 12,
|
||||
height: 1.5,
|
||||
letterSpacing: 0.05,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Footnote 1 Bold - Font: Instrument Sans, Size: 12, Height: 1.5, Spacing: 0.05 (#121826)
|
||||
static final TextStyle footnote1b = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 12,
|
||||
height: 1.5,
|
||||
letterSpacing: 0.05,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Footnote 2 Medium - Font: Instrument Sans, Size: 10, Height: 1.5 (#121826)
|
||||
static final TextStyle footnote2m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 10,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Footnote 2 Bold - Font: Instrument Sans, Size: 10, Height: 1.5 (#121826)
|
||||
static final TextStyle footnote2b = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 10,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Footnote 2 Regular - Font: Instrument Sans, Size: 10, Height: 1.5 (#121826)
|
||||
static final TextStyle footnote2r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 10,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
// --- 1.7 Button ---
|
||||
|
||||
/// Button S - Font: Instrument Sans, Size: 10, Height: 1.5 (#121826)
|
||||
static final TextStyle buttonS = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 10,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Button Medium - Font: Instrument Sans, Size: 12, Height: 1.5 (#121826)
|
||||
static final TextStyle buttonM = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Button Large - Font: Instrument Sans, Size: 14, Height: 1.5 (#121826)
|
||||
static final TextStyle buttonL = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Button XL - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
|
||||
static final TextStyle buttonXL = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 16,
|
||||
height: 1.5,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
// --- 1.8 Link ---
|
||||
|
||||
/// Link 1 Regular - Font: Instrument Sans, Size: 16, Height: 1.5, Underlined (#0A39DF)
|
||||
static final TextStyle link1r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
height: 1.5,
|
||||
color: UiColors.textLink,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: UiColors.textLink,
|
||||
);
|
||||
|
||||
/// Link 2 Medium - Font: Instrument Sans, Size: 14, Height: 1.5, Underlined (#0A39DF)
|
||||
static final TextStyle link2m = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
height: 1.5,
|
||||
color: UiColors.textLink,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: UiColors.textLink,
|
||||
);
|
||||
|
||||
/// Link 2 Regular - Font: Instrument Sans, Size: 14, Height: 1.5, Underlined (#0A39DF)
|
||||
static final TextStyle link2r = _primaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
height: 1.5,
|
||||
color: UiColors.textLink,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: UiColors.textLink,
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 2. Secondary Typography (Space Grotesk)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// --- 2.1 Display ---
|
||||
|
||||
/// Display 1 Bold (Secondary) - Font: Space Grotesk, Size: 50, Height: 1.1 (#121826)
|
||||
static final TextStyle secondaryDisplay1b = _secondaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 50,
|
||||
height: 1.1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Display 1 Regular (Secondary) - Font: Space Grotesk, Size: 50, Height: 1.1 (#121826)
|
||||
static final TextStyle secondaryDisplay1r = _secondaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 50,
|
||||
height: 1.1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Display 2 Bold (Secondary) - Font: Space Grotesk, Size: 40, Height: 1.1 (#121826)
|
||||
static final TextStyle secondaryDisplay2b = _secondaryBase.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 40,
|
||||
height: 1.1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
/// Display 2 Regular (Secondary) - Font: Space Grotesk, Size: 40, Height: 1.1 (#121826)
|
||||
static final TextStyle secondaryDisplay2r = _secondaryBase.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 40,
|
||||
height: 1.1,
|
||||
color: UiColors.textPrimary,
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 3. TextTheme Mapping
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Primary TextTheme
|
||||
static TextTheme get textTheme => TextTheme(
|
||||
displayLarge: display1r,
|
||||
displayMedium: displayL,
|
||||
displaySmall: display3m,
|
||||
headlineLarge: headline1m,
|
||||
headlineMedium: headline3m,
|
||||
headlineSmall: headline2m,
|
||||
titleLarge: title1m,
|
||||
titleMedium: title2m,
|
||||
titleSmall: body2m,
|
||||
bodyLarge: body1r,
|
||||
bodyMedium: body2r,
|
||||
bodySmall: footnote1r,
|
||||
labelLarge: buttonL,
|
||||
labelMedium: buttonM,
|
||||
labelSmall: footnote2r,
|
||||
);
|
||||
}
|
||||
|
||||
/// Extension to easily color text styles using the Staff Design System color palette.
|
||||
extension TypographyColors on TextStyle {
|
||||
/// Primary text color (#121826)
|
||||
TextStyle get textPrimary => copyWith(color: UiColors.textPrimary);
|
||||
|
||||
/// Secondary text color (#6A7382)
|
||||
TextStyle get textSecondary => copyWith(color: UiColors.textSecondary);
|
||||
|
||||
/// Inactive text color (#9CA3AF)
|
||||
TextStyle get textInactive => copyWith(color: UiColors.textInactive);
|
||||
|
||||
/// Tertiary text color (#9CA3AF)
|
||||
TextStyle get textTertiary => copyWith(color: UiColors.textInactive);
|
||||
|
||||
/// Placeholder text color (#9CA3AF)
|
||||
TextStyle get textPlaceholder => copyWith(color: UiColors.textPlaceholder);
|
||||
|
||||
/// Description text color (#6A7382)
|
||||
TextStyle get textDescription => copyWith(color: UiColors.textDescription);
|
||||
|
||||
/// Success text color (#10B981)
|
||||
TextStyle get textSuccess => copyWith(color: UiColors.textSuccess);
|
||||
|
||||
/// Error text color (#F04444)
|
||||
TextStyle get textError => copyWith(color: UiColors.textError);
|
||||
|
||||
/// Warning text color (#D97706)
|
||||
TextStyle get textWarning => copyWith(color: UiColors.textWarning);
|
||||
|
||||
/// Link text color (#0A39DF)
|
||||
TextStyle get textLink => copyWith(color: UiColors.textLink);
|
||||
|
||||
/// White text color (#FFFFFF)
|
||||
TextStyle get white => copyWith(color: UiColors.white);
|
||||
|
||||
/// Black text color (#000000)
|
||||
TextStyle get black => copyWith(color: UiColors.black);
|
||||
|
||||
/// Underline decoration
|
||||
TextStyle get underline => copyWith(decoration: TextDecoration.underline);
|
||||
|
||||
/// Active content color
|
||||
TextStyle get activeContentColor => copyWith(color: UiColors.textPrimary);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
190
apps/mobile/packages/design_system/lib/src/widgets/ui_chip.dart
Normal file
190
apps/mobile/packages/design_system/lib/src/widgets/ui_chip.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
31
apps/mobile/packages/design_system/pubspec.yaml
Normal file
31
apps/mobile/packages/design_system/pubspec.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
name: design_system
|
||||
description: "A new Flutter package project."
|
||||
version: 0.0.1
|
||||
homepage:
|
||||
resolution: workspace
|
||||
|
||||
environment:
|
||||
sdk: ^3.10.7
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
google_fonts: ^7.0.2
|
||||
lucide_icons: ^0.257.0
|
||||
font_awesome_flutter: ^10.7.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^6.0.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
|
||||
# To add assets to your package, add an assets section, like this:
|
||||
assets:
|
||||
- assets/
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:design_system/design_system.dart';
|
||||
|
||||
void main() {
|
||||
test('adds one to input values', () {
|
||||
final calculator = Calculator();
|
||||
expect(calculator.addOne(2), 3);
|
||||
expect(calculator.addOne(-7), -6);
|
||||
expect(calculator.addOne(0), 1);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user