PRF Design
A reusable Flutter package containing shared widgets, theme system, and essential utilities for PRF applications.
Installation
Add this package to your pubspec.yaml:
dependencies:
prf_design: ^0.3.0
Or run:
flutter pub add prf_design
For local development in a monorepo, use a path dependency instead:
dependencies:
prf_design:
path: packages/prf_design
Quick Start
import 'package:prf_design/prf_design.dart';
MaterialApp(
theme: PRFTheme.light(scaleFactor: DeviceHelper.getScaleFactor(context: context)),
darkTheme: PRFTheme.dark(scaleFactor: DeviceHelper.getScaleFactor(context: context)),
home: const MyHomePage(),
)
Imports
Full Import
import 'package:prf_design/prf_design.dart';
Granular Imports
// Theme only
import 'package:prf_design/exports/theme.dart';
// Widgets only
import 'package:prf_design/exports/widgets.dart';
// Utilities only
import 'package:prf_design/exports/utils.dart';
// Enums & error models only
import 'package:prf_design/exports/enums.dart';
Theme System
PRFTheme
Provides complete Material 3 theme configurations for light and dark modes.
// Static factory methods (breaking API)
static ThemeData light({required double scaleFactor})
static ThemeData dark({required double scaleFactor})
Both methods configure: color scheme, text theme, app bar, buttons, inputs, cards, dividers, tab bar, data table, snackbar, icon, dialog, list tile, chip, and dropdown menu themes.
PRFColors
Core brand colors and semantic color constants.
Brand Colors:
| Color | Hex | Usage |
|---|---|---|
| Navy Blue | #17154C |
Primary brand color |
| Lime Green | #93D500 |
Secondary brand color |
Neutral Colors: gray50 through gray900 (10 shades from light to dark)
Status Colors: success, successLight, successDark, warning, warningLight, warningDark, error, errorLight, errorDark, info, infoLight, infoDark
Accent Colors: purple (#6B21A8), blue (#2563EB), orange (#EA580C), emerald (#10B981)
Common Colors: white, black, transparent
PRFColorPalette
Tints and shades for brand colors.
- Navy palette:
navy50throughnavy900(10 shades) - Lime palette:
lime50throughlime900(10 shades) - Convenience getters:
primary,primaryLight,primaryContainer,primaryDark,secondary,secondaryLight,secondaryContainer,secondaryDark
PRFColorsExtension
Theme extension for accessing brand colors through the theme system. Includes all palette colors, grays, and accent colors with light/dark variants.
// Access via context
final colors = context.prfColors;
Container(color: colors.navyBlue)
PRFStatusExtension
Theme extension providing semantic status colors via StatusColorSet.
StatusColorSet:
StatusColorSet({
required Color main, // Primary status color
required Color background, // Light background for containers
required Color onColor, // Text/icon color on main
})
Standard statuses: success, warning, error, info
Mission statuses: pending (orange), initiated (blue), scheduled (orange), inProgress (emerald), completed (success), failed (error), ignored (gray)
Active indicator: active, activeGlow
final status = context.statusColors;
Container(
color: status.success.background,
child: Text('Done', style: TextStyle(color: status.success.main)),
)
Context Extensions
Convenience getters on BuildContext:
context.prfColors // PRFColorsExtension
context.statusColors // PRFStatusExtension
context.colorScheme // ColorScheme
context.textTheme // TextTheme
context.theme // ThemeData
PRFTextTheme
Typography system using Google Fonts (Lato) with responsive scaling and light/dark variants.
static TextTheme getLightTheme({required double scaleFactor})
static TextTheme getDarkTheme({required double scaleFactor})
Utility text styles:
PRFTextTheme.getErrorTextStyle(scaleFactor: 1)
PRFTextTheme.getSuccessTextStyle(scaleFactor: 1)
PRFTextTheme.getWarningTextStyle(scaleFactor: 1)
PRFTextTheme.getInfoTextStyle(scaleFactor: 1)
PRFTextTheme.getButtonTextStyle(scaleFactor: 1)
PRFTextTheme.getCaptionTextStyle(scaleFactor: 1)
Widgets
Buttons
PRFPrimaryButton
Adaptive primary button with handset/tablet variants.
PRFPrimaryButton({
required VoidCallback onPressed,
required String title,
required bool disabled,
bool? isLoading,
})
PRFSecondaryButton
Outlined style variant. Same signature as PRFPrimaryButton.
PRFDestroyButton
Destructive/error style variant. Same signature as PRFPrimaryButton.
PRFGoogleAuthButton
PRFGoogleAuthButton({
required VoidCallback onPressed,
required String title,
required bool disabled,
bool? isLoading,
Widget? googleLogoAsset,
})
Inputs
All input widgets are responsive with handset/tablet adaptive variants.
PRFTextInput
PRFTextInput({
required String hintText,
required TextEditingController controller,
bool enabled = true,
ValueChanged<String>? onChanged,
})
PRFEmailInput
PRFEmailInput({
required String hintText,
required TextEditingController emailController,
bool enabled = true,
})
PRFPasswordInput
PRFPasswordInput({
required String hintText,
required ValueNotifier<bool> hidePasswordNotifier,
required TextEditingController passwordController,
bool enabled = true,
})
PRFNameInput
PRFNameInput({
required String hintText,
required TextEditingController controller,
bool enabled = true,
})
PRFNumberInput
PRFNumberInput({
required String hintText,
required TextEditingController controller,
bool isLoading = false,
String? prefixText,
TextInputAction textInputAction = TextInputAction.next,
})
PRFTextAreaInput
PRFTextAreaInput({
required String hintText,
required TextEditingController controller,
bool enabled = true,
int minLines = 3,
int maxLines = 5,
TextInputAction textInputAction = TextInputAction.newline,
})
FormFieldLabel
FormFieldLabel({
required String label,
bool? isRequired,
Color? color,
bool? isBold,
})
Progress Indicators
PRFCircularProgressIndicator
PRFCircularProgressIndicator({
Color? color,
double? value,
double size = 24,
double strokeWidth = 2,
})
PRFLinearProgressIndicator
PRFLinearProgressIndicator({
Color? color,
double? value,
double height = 4,
double? borderRadius,
Color? backgroundColor,
})
State Displays
PRFEmptyView
PRFEmptyView({
required String label,
required String description,
IconData? icon,
Widget? action,
String? actionLabel,
VoidCallback? onActionPressed,
})
PRFErrorView
PRFErrorView({
required PRFFailure failure,
VoidCallback? onRetry,
bool compact = false,
})
// Factory constructors
PRFErrorView.fromMessage({required String message, VoidCallback? onRetry, bool compact})
PRFErrorView.fromFailure({required PRFFailure failure, VoidCallback? onRetry, bool compact})
PRFSnackbar
Static methods for typed snackbar notifications. Replaces the old PRFErrorSnackbar.
PRFSnackbar.error(context, 'Something went wrong', onRetry: () => _retry());
PRFSnackbar.success(context, 'Saved successfully');
PRFSnackbar.info(context, 'New update available');
PRFSnackbar.warning(context, 'Low storage space');
Each method accepts an optional Duration duration parameter.
PRFCategoryChips<T>
PRFCategoryChips<T>({
required List<T> categories,
required void Function(T?) onCategorySelected,
required String Function(T) labelBuilder,
T? selectedCategory,
bool isLoading = false,
bool showAllOption = true,
String allLabel = 'ALL',
double height = 48,
double spacing = 10,
EdgeInsets padding = const EdgeInsets.symmetric(horizontal: 16),
})
ImagePreviewPage
Full-screen image preview with pan/zoom and pagination.
ImagePreviewPage({
required List<String> imageUrls,
required int initialIndex,
String Function(int index, int total)? titleBuilder,
String? appBarTitle,
})
ReplyStatusView
ReplyStatusView({
required void Function({required bool status}) onStatusSelected,
required String unreadLabel,
required String repliedLabel,
bool reversed = false,
bool defaultStatus = false,
})
Cards
AnimatedStatCard
AnimatedStatCard({
required String value,
required String label,
IconData? icon,
Color? color,
Duration delay = Duration.zero,
})
StatHighlightCard
StatHighlightCard({
required String title,
required String subtitle,
IconData? icon,
List<Color>? gradient,
Duration delay = Duration.zero,
})
Viewers
PDFViewerPage
PDFViewerPage({
required String pdfUrl,
required String title,
})
Indicators
WrappedPageIndicator
WrappedPageIndicator({
required int currentPage,
required int pageCount,
})
Utilities
DeviceHelper
Device type detection and responsive scaling.
// Get scale factor for responsive sizing
double scale = DeviceHelper.getScaleFactor(
context: context,
customBaseWidth: 375,
minScale: 0.8,
maxScale: 1.4,
);
// Detect device type
DeviceType type = DeviceHelper.getDeviceType(context: context);
// DeviceType.phone | DeviceType.tablet | DeviceType.desktop
// Check orientation
bool landscape = DeviceHelper.isLandscape(context: context);
Debouncer
final debouncer = Debouncer(milliseconds: 300);
debouncer.run(() => search(query));
debouncer.cancel();
debouncer.flush(); // Execute pending action immediately
debouncer.isPending; // Check if action is pending
debouncer.dispose();
DateFormatter
Timezone-aware date/time formatting.
DateFormatter.formatDateTime(dateTime, 'Africa/Nairobi') // "Jan 15, 2025, 2:30 PM"
DateFormatter.formatMissionDate(dateTime, 'Africa/Nairobi') // Mission-specific format
DateFormatter.formatDate(dateTime, 'Africa/Nairobi') // "Jan 15, 2025"
DateFormatter.timestamp(dateTime, 'Africa/Nairobi') // Timestamp format
DateFormatter.formatTime('14:30', 'Africa/Nairobi') // "2:30 PM"
DateFormatter.formatTimeFromDateTime(dateTime, 'Africa/Nairobi')
DateFormatter.getRelativeTime(dateTime) // "2 hours ago"
DateFormatter.getMonthAbbreviation(1) // "Jan"
All methods accept an optional locale parameter.
NumberFormatter
NumberFormatter.formatCash(1500.50, locale: 'en_KE', symbol: '', decimalDigits: 0, customSymbol: 'KES')
NumberFormatter.truncateToDecimalPlaces(3.14159, 2) // 3.14
NumberFormatter.roundToDecimalPlaces(3.14159, 2) // 3.14
NumberFormatter.formatFileSize(1572864) // "1.5 MB"
StringFormatter
StringFormatter.getUserNameInitials('John Doe', maxInitials: 2) // "JD"
StringFormatter.getFileName('/path/to/file.pdf') // "file.pdf"
StringFormatter.generateRandomString(16, includeNumbers: true, includeSymbols: false)
Error Handling
PRFFailure
Comprehensive error model for structured error handling.
PRFFailure({
required String message, // User-friendly message
int? statusCode, // HTTP status code
PRFErrorType type, // Error category (default: unknown)
PRFErrorSeverity severity, // Severity level (default: medium)
String? technicalMessage, // Debug details (not shown to users)
bool isRecoverable, // Whether user can retry (default: true)
StackTrace? stackTrace,
Map<String, dynamic> context, // Additional context data
})
Factory constructors:
PRFFailure.fromStatusCode(404, 'Not found')
PRFFailure.fromException(error, stackTrace)
PRFFailure.noConnection()
PRFFailure.timeout()
PRFFailure.authentication(message: 'Session expired')
PRFFailure.authorization(message: 'Insufficient permissions')
Methods:
failure.copyWith(message: 'Updated message', severity: PRFErrorSeverity.high)
PRFErrorType
network | authentication | authorization | validation | notFound | server | timeout | cancelled | unknown
PRFErrorSeverity
low | medium | high | critical
PRFSnackbarType
error | success | info | warning
Example: Error Creation & Display
// Create a failure
final failure = PRFFailure.noConnection();
// Display as error view
PRFErrorView(
failure: failure,
onRetry: () => _reload(),
)
// Display as snackbar
PRFSnackbar.error(context, failure.message, onRetry: () => _retry());
Package Structure
prf_design/
├── lib/
│ ├── prf_design.dart # Main barrel export
│ ├── exports/
│ │ ├── theme.dart # Theme system exports
│ │ ├── widgets.dart # Widget exports
│ │ ├── utils.dart # Utility exports
│ │ └── enums.dart # Enum & model exports
│ └── src/
│ ├── enums/ # Error types, failure model
│ │ ├── prf_error_type.dart
│ │ ├── prf_error_severity.dart
│ │ ├── prf_snackbar_type.dart
│ │ └── prf_failure.dart
│ ├── theme/ # Colors, text, extensions
│ │ ├── prf_theme.dart
│ │ ├── text_theme.dart
│ │ ├── colors/
│ │ │ ├── prf_colors.dart
│ │ │ └── prf_color_palette.dart
│ │ └── extensions/
│ │ ├── prf_colors_extension.dart
│ │ ├── prf_status_extension.dart
│ │ └── theme_context_extensions.dart
│ ├── utils/ # DeviceHelper, formatters, debouncer
│ │ ├── debouncer.dart
│ │ ├── device_helper.dart
│ │ └── formatters/
│ │ ├── date_formatter.dart
│ │ ├── number_formatter.dart
│ │ └── string_formatter.dart
│ └── widgets/ # All UI components
│ ├── buttons/
│ ├── cards/
│ ├── indicators/
│ ├── inputs/
│ ├── progress/
│ ├── states/
│ └── viewers/
Requirements
| Requirement | Version |
|---|---|
| Dart SDK | >=3.10.3 <4.0.0 |
| Flutter | >=3.38.5 |
Dependencies
| Package | Description |
|---|---|
flutter_adaptive_ui |
Responsive handset/tablet layouts |
flutter_animate |
Declarative animations |
google_fonts |
Lato font and other Google Fonts |
http |
HTTP client for network requests |
intl |
Internationalization and formatting |
pdfx |
PDF rendering |
timezone |
Timezone-aware date/time handling |
Contributing
Development
# Format code
make fmt
# Run analysis
flutter analyze
# Run tests
flutter test
CI
All pull requests are checked for:
- Code formatting
- Static analysis
- Tests
Publishing to pub.dev is automated via GitHub Actions.
License
MIT
Libraries
- exports/enums
- Models exports for prf_design
- exports/theme
- Theme exports for prf_design
- exports/utils
- Utilities exports for prf_design
- exports/widgets
- Widgets exports for prf_design
- prf_design
- PRF Design System - Shared widgets, theme system, and essential utilities.
- tokens
- PRF Design System – standalone tokens library.