Initial commit: Svrnty Console Flutter application with GetWidget integration
- Implemented responsive dashboard UI with Material Design 3 - Integrated GetWidget 7.0.0 for modern UI components (GFAvatar, custom badges) - Created collapsible navigation sidebar with smooth animations - Built status cards grid with uniform badge sizing - Added activity timeline with recent events - Configured custom Svrnty branding (Crimson Red #F3574E, Slate Blue #5A6F7D) - Set up development server on http://localhost:54952/ - Included Montserrat and IBM Plex Mono custom fonts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,322 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:animate_do/animate_do.dart';
|
||||
import 'package:iconsax/iconsax.dart';
|
||||
import 'package:getwidget/getwidget.dart';
|
||||
|
||||
class NavigationSidebar extends StatefulWidget {
|
||||
final bool isExpanded;
|
||||
final Function(String) onNavigate;
|
||||
final String currentPage;
|
||||
|
||||
const NavigationSidebar({
|
||||
Key? key,
|
||||
required this.isExpanded,
|
||||
required this.onNavigate,
|
||||
required this.currentPage,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<NavigationSidebar> createState() => _NavigationSidebarState();
|
||||
}
|
||||
|
||||
class _NavigationSidebarState extends State<NavigationSidebar> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final width = widget.isExpanded ? 250.0 : 70.0;
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
width: width,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.surfaceContainerLow,
|
||||
border: Border(
|
||||
right: BorderSide(
|
||||
color: colorScheme.outline.withOpacity(0.2),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// Svrnty Logo Section
|
||||
_buildLogoSection(colorScheme),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Navigation Menu Items
|
||||
Expanded(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
children: [
|
||||
_buildMenuItem(
|
||||
icon: Iconsax.home,
|
||||
title: 'Dashboard',
|
||||
pageId: 'dashboard',
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildMenuItem(
|
||||
icon: Iconsax.hierarchy_square,
|
||||
title: 'The Architect',
|
||||
pageId: 'architect',
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildMenuItem(
|
||||
icon: Iconsax.cpu,
|
||||
title: 'Agents',
|
||||
pageId: 'agents',
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildMenuItem(
|
||||
icon: Iconsax.chart_square,
|
||||
title: 'Analytics',
|
||||
pageId: 'analytics',
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildMenuItem(
|
||||
icon: Iconsax.box,
|
||||
title: 'Tools',
|
||||
pageId: 'tools',
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildMenuItem(
|
||||
icon: Iconsax.setting_2,
|
||||
title: 'Settings',
|
||||
pageId: 'settings',
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// User Profile Section (bottom)
|
||||
_buildUserSection(colorScheme),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLogoSection(ColorScheme colorScheme) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 20,
|
||||
horizontal: widget.isExpanded ? 20 : 12,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: colorScheme.outline.withOpacity(0.2),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: widget.isExpanded
|
||||
? FadeIn(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.primary,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Text(
|
||||
'S',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 23,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Svrnty',
|
||||
style: TextStyle(
|
||||
fontSize: 20.7,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Console',
|
||||
style: TextStyle(
|
||||
fontSize: 12.65,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.primary,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Text(
|
||||
'S',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 23,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMenuItem({
|
||||
required IconData icon,
|
||||
required String title,
|
||||
required String pageId,
|
||||
required ColorScheme colorScheme,
|
||||
}) {
|
||||
final isActive = widget.currentPage == pageId;
|
||||
|
||||
return FadeInLeft(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () => widget.onNavigate(pageId),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 14,
|
||||
horizontal: widget.isExpanded ? 16 : 12,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: isActive
|
||||
? colorScheme.primary.withOpacity(0.25)
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: isActive
|
||||
? colorScheme.primary.withOpacity(0.7)
|
||||
: Colors.transparent,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
color: isActive
|
||||
? Colors.white
|
||||
: colorScheme.onSurfaceVariant,
|
||||
size: 24,
|
||||
),
|
||||
if (widget.isExpanded) ...[
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: FadeIn(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 17.25,
|
||||
fontWeight:
|
||||
isActive ? FontWeight.w600 : FontWeight.w400,
|
||||
color: isActive
|
||||
? Colors.white
|
||||
: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildUserSection(ColorScheme colorScheme) {
|
||||
return Container(
|
||||
padding: EdgeInsets.all(widget.isExpanded ? 16 : 12),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: colorScheme.outline.withOpacity(0.2),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: widget.isExpanded
|
||||
? FadeIn(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
GFAvatar(
|
||||
radius: 18,
|
||||
backgroundColor: colorScheme.primary.withOpacity(0.3),
|
||||
child: Icon(
|
||||
Iconsax.user,
|
||||
color: colorScheme.primary,
|
||||
size: 18,
|
||||
),
|
||||
shape: GFAvatarShape.circle,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'Admin',
|
||||
style: TextStyle(
|
||||
fontSize: 14.95,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'admin@svrnty.ai',
|
||||
style: TextStyle(
|
||||
fontSize: 11.5,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Center(
|
||||
child: GFAvatar(
|
||||
radius: 20,
|
||||
backgroundColor: colorScheme.primary.withOpacity(0.2),
|
||||
child: Icon(
|
||||
Iconsax.user,
|
||||
color: colorScheme.primary,
|
||||
size: 20,
|
||||
),
|
||||
shape: GFAvatarShape.circle,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:animate_do/animate_do.dart';
|
||||
|
||||
/// Svrnty Design System Components
|
||||
/// Reusable, branded components for the Svrnty Console application
|
||||
///
|
||||
/// Brand Colors:
|
||||
/// - Primary (Crimson): #C44D58
|
||||
/// - Secondary (Slate Blue): #475C6C
|
||||
|
||||
// ============================================================================
|
||||
// SVRNTY BUTTONS
|
||||
// ============================================================================
|
||||
|
||||
enum SvrntyButtonVariant { primary, secondary, ghost, danger }
|
||||
|
||||
class SvrntyButton extends StatelessWidget {
|
||||
final String text;
|
||||
final VoidCallback? onPressed;
|
||||
final SvrntyButtonVariant variant;
|
||||
final IconData? icon;
|
||||
final bool isLoading;
|
||||
final bool fullWidth;
|
||||
|
||||
const SvrntyButton({
|
||||
Key? key,
|
||||
required this.text,
|
||||
this.onPressed,
|
||||
this.variant = SvrntyButtonVariant.primary,
|
||||
this.icon,
|
||||
this.isLoading = false,
|
||||
this.fullWidth = false,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
Color backgroundColor;
|
||||
Color textColor;
|
||||
Color? borderColor;
|
||||
|
||||
switch (variant) {
|
||||
case SvrntyButtonVariant.primary:
|
||||
backgroundColor = colorScheme.primary;
|
||||
textColor = Colors.white;
|
||||
borderColor = null;
|
||||
break;
|
||||
case SvrntyButtonVariant.secondary:
|
||||
backgroundColor = colorScheme.secondary;
|
||||
textColor = Colors.white;
|
||||
borderColor = null;
|
||||
break;
|
||||
case SvrntyButtonVariant.ghost:
|
||||
backgroundColor = Colors.transparent;
|
||||
textColor = colorScheme.primary;
|
||||
borderColor = colorScheme.primary;
|
||||
break;
|
||||
case SvrntyButtonVariant.danger:
|
||||
backgroundColor = colorScheme.error;
|
||||
textColor = Colors.white;
|
||||
borderColor = null;
|
||||
break;
|
||||
}
|
||||
|
||||
return FadeInUp(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: SizedBox(
|
||||
width: fullWidth ? double.infinity : null,
|
||||
child: ElevatedButton(
|
||||
onPressed: isLoading ? null : onPressed,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: backgroundColor,
|
||||
foregroundColor: textColor,
|
||||
elevation: variant == SvrntyButtonVariant.ghost ? 0 : 2,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
side: borderColor != null
|
||||
? BorderSide(color: borderColor, width: 2)
|
||||
: BorderSide.none,
|
||||
),
|
||||
),
|
||||
child: isLoading
|
||||
? SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(textColor),
|
||||
),
|
||||
)
|
||||
: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (icon != null) ...[
|
||||
Icon(icon, size: 20),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
Text(
|
||||
text,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SVRNTY CARDS
|
||||
// ============================================================================
|
||||
|
||||
class SvrntyCard extends StatelessWidget {
|
||||
final Widget child;
|
||||
final Color? accentColor;
|
||||
final VoidCallback? onTap;
|
||||
final bool showBorder;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
const SvrntyCard({
|
||||
Key? key,
|
||||
required this.child,
|
||||
this.accentColor,
|
||||
this.onTap,
|
||||
this.showBorder = true,
|
||||
this.padding,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final effectiveAccentColor = accentColor ?? colorScheme.primary;
|
||||
|
||||
return FadeInUp(
|
||||
duration: const Duration(milliseconds: 400),
|
||||
child: Container(
|
||||
decoration: showBorder
|
||||
? BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
effectiveAccentColor.withOpacity(0.15),
|
||||
effectiveAccentColor.withOpacity(0.05),
|
||||
],
|
||||
),
|
||||
border: Border.all(
|
||||
color: effectiveAccentColor.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
child: Card(
|
||||
elevation: 2,
|
||||
color: showBorder ? Colors.transparent : null,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Container(
|
||||
decoration: showBorder
|
||||
? BoxDecoration(
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
color: effectiveAccentColor,
|
||||
width: 4,
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.all(20),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SVRNTY BADGES
|
||||
// ============================================================================
|
||||
|
||||
enum SvrntyBadgeStatus { success, warning, error, info, neutral }
|
||||
|
||||
class SvrntyBadge extends StatelessWidget {
|
||||
final String text;
|
||||
final SvrntyBadgeStatus status;
|
||||
final IconData? icon;
|
||||
|
||||
const SvrntyBadge({
|
||||
Key? key,
|
||||
required this.text,
|
||||
this.status = SvrntyBadgeStatus.neutral,
|
||||
this.icon,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
Color backgroundColor;
|
||||
Color textColor;
|
||||
|
||||
switch (status) {
|
||||
case SvrntyBadgeStatus.success:
|
||||
backgroundColor = Colors.green;
|
||||
textColor = Colors.white;
|
||||
break;
|
||||
case SvrntyBadgeStatus.warning:
|
||||
backgroundColor = Colors.orange;
|
||||
textColor = Colors.white;
|
||||
break;
|
||||
case SvrntyBadgeStatus.error:
|
||||
backgroundColor = colorScheme.error;
|
||||
textColor = Colors.white;
|
||||
break;
|
||||
case SvrntyBadgeStatus.info:
|
||||
backgroundColor = colorScheme.primary;
|
||||
textColor = Colors.white;
|
||||
break;
|
||||
case SvrntyBadgeStatus.neutral:
|
||||
backgroundColor = colorScheme.secondary;
|
||||
textColor = Colors.white;
|
||||
break;
|
||||
}
|
||||
|
||||
return Pulse(
|
||||
duration: const Duration(milliseconds: 1000),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: backgroundColor.withOpacity(0.3),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (icon != null) ...[
|
||||
Icon(icon, color: textColor, size: 16),
|
||||
const SizedBox(width: 6),
|
||||
],
|
||||
Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: 'Montserrat',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SVRNTY ICON BUTTONS
|
||||
// ============================================================================
|
||||
|
||||
class SvrntyIconButton extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final VoidCallback? onPressed;
|
||||
final Color? backgroundColor;
|
||||
final Color? iconColor;
|
||||
final double size;
|
||||
|
||||
const SvrntyIconButton({
|
||||
Key? key,
|
||||
required this.icon,
|
||||
this.onPressed,
|
||||
this.backgroundColor,
|
||||
this.iconColor,
|
||||
this.size = 40,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return FadeIn(
|
||||
child: Container(
|
||||
width: size,
|
||||
height: size,
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor ?? colorScheme.primary.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: IconButton(
|
||||
icon: Icon(icon),
|
||||
color: iconColor ?? colorScheme.primary,
|
||||
iconSize: size * 0.5,
|
||||
onPressed: onPressed,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SVRNTY SECTION HEADER
|
||||
// ============================================================================
|
||||
|
||||
class SvrntySectionHeader extends StatelessWidget {
|
||||
final String title;
|
||||
final String? subtitle;
|
||||
final Widget? action;
|
||||
|
||||
const SvrntySectionHeader({
|
||||
Key? key,
|
||||
required this.title,
|
||||
this.subtitle,
|
||||
this.action,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return FadeInLeft(
|
||||
duration: const Duration(milliseconds: 400),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 4,
|
||||
height: 28,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.primary,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.onSurface,
|
||||
fontFamily: 'Montserrat',
|
||||
),
|
||||
),
|
||||
if (subtitle != null) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
subtitle!,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
fontFamily: 'Montserrat',
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
if (action != null) action!,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SVRNTY INPUT FIELD
|
||||
// ============================================================================
|
||||
|
||||
class SvrntyTextField extends StatelessWidget {
|
||||
final String label;
|
||||
final String? hint;
|
||||
final TextEditingController? controller;
|
||||
final IconData? prefixIcon;
|
||||
final bool obscureText;
|
||||
final TextInputType? keyboardType;
|
||||
final String? Function(String?)? validator;
|
||||
|
||||
const SvrntyTextField({
|
||||
Key? key,
|
||||
required this.label,
|
||||
this.hint,
|
||||
this.controller,
|
||||
this.prefixIcon,
|
||||
this.obscureText = false,
|
||||
this.keyboardType,
|
||||
this.validator,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return FadeInUp(
|
||||
duration: const Duration(milliseconds: 350),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: colorScheme.onSurface,
|
||||
fontFamily: 'Montserrat',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(
|
||||
controller: controller,
|
||||
obscureText: obscureText,
|
||||
keyboardType: keyboardType,
|
||||
validator: validator,
|
||||
style: const TextStyle(fontFamily: 'Montserrat'),
|
||||
decoration: InputDecoration(
|
||||
hintText: hint,
|
||||
prefixIcon: prefixIcon != null ? Icon(prefixIcon) : null,
|
||||
filled: true,
|
||||
fillColor: colorScheme.surfaceContainerHighest,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.outline.withOpacity(0.3),
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.outline.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.error,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,597 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:iconsax/iconsax.dart';
|
||||
import 'package:animate_do/animate_do.dart';
|
||||
import 'package:getwidget/getwidget.dart';
|
||||
import 'components/navigation_sidebar.dart';
|
||||
|
||||
class ConsoleLandingPage extends StatefulWidget {
|
||||
const ConsoleLandingPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ConsoleLandingPage> createState() => _ConsoleLandingPageState();
|
||||
}
|
||||
|
||||
class _ConsoleLandingPageState extends State<ConsoleLandingPage> {
|
||||
bool _isSidebarExpanded = true;
|
||||
String _currentPage = 'dashboard';
|
||||
|
||||
void _toggleSidebar() {
|
||||
setState(() {
|
||||
_isSidebarExpanded = !_isSidebarExpanded;
|
||||
});
|
||||
}
|
||||
|
||||
void _navigateToPage(String pageId) {
|
||||
setState(() {
|
||||
_currentPage = pageId;
|
||||
});
|
||||
// Handle page navigation here
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Navigating to: $pageId')),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: colorScheme.surface,
|
||||
body: Row(
|
||||
children: [
|
||||
// Navigation Sidebar
|
||||
NavigationSidebar(
|
||||
isExpanded: _isSidebarExpanded,
|
||||
onNavigate: _navigateToPage,
|
||||
currentPage: _currentPage,
|
||||
),
|
||||
|
||||
// Main Content Area
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
// AppBar
|
||||
_buildAppBar(colorScheme),
|
||||
|
||||
// Main Content
|
||||
Expanded(
|
||||
child: _buildMainContent(colorScheme),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAppBar(ColorScheme colorScheme) {
|
||||
return Container(
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.surfaceContainerLow,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: colorScheme.outline.withOpacity(0.2),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// Toggle Sidebar Button
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
_isSidebarExpanded ? Iconsax.arrow_left_2 : Iconsax.arrow_right_3,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
onPressed: _toggleSidebar,
|
||||
tooltip: _isSidebarExpanded ? 'Collapse sidebar' : 'Expand sidebar',
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
|
||||
// Page Title
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
_getPageTitle(),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'sovereign AI solutions',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Action Buttons
|
||||
Container(
|
||||
margin: const EdgeInsets.only(right: 8),
|
||||
child: IconButton(
|
||||
icon: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.primary.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(Iconsax.notification, color: colorScheme.primary),
|
||||
),
|
||||
onPressed: () {},
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(right: 12),
|
||||
child: IconButton(
|
||||
icon: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.secondary.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(Iconsax.setting_2, color: colorScheme.secondary),
|
||||
),
|
||||
onPressed: () {},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _getPageTitle() {
|
||||
switch (_currentPage) {
|
||||
case 'dashboard':
|
||||
return 'Dashboard';
|
||||
case 'architect':
|
||||
return 'The Architect';
|
||||
case 'agents':
|
||||
return 'AI Agents';
|
||||
case 'analytics':
|
||||
return 'Analytics';
|
||||
case 'tools':
|
||||
return 'Tools';
|
||||
case 'settings':
|
||||
return 'Settings';
|
||||
default:
|
||||
return 'Svrnty Console';
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildMainContent(ColorScheme colorScheme) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SingleChildScrollView(
|
||||
padding: EdgeInsets.all(_getPagePadding(constraints.maxWidth)),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Page Title
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
colorScheme.primary.withOpacity(0.2),
|
||||
colorScheme.secondary.withOpacity(0.15),
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: colorScheme.primary.withOpacity(0.5),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.primary,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: const Text(
|
||||
'S',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
'Svrnty Console',
|
||||
style: TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'sovereign AI solutions • 30 October 2025',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
fontFamily: 'Montserrat',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 28),
|
||||
|
||||
// Status Cards Grid
|
||||
_buildResponsiveGrid(
|
||||
constraints.maxWidth,
|
||||
[
|
||||
_buildStatusCard(
|
||||
'Backend',
|
||||
'Access Swagger',
|
||||
colorScheme.primary,
|
||||
Icons.api,
|
||||
'Active',
|
||||
),
|
||||
_buildStatusCard(
|
||||
'Frontend',
|
||||
'UI Status',
|
||||
colorScheme.primary,
|
||||
Icons.web,
|
||||
'Online',
|
||||
),
|
||||
_buildStatusCard(
|
||||
'To-Do',
|
||||
'Analytics Dashboard',
|
||||
colorScheme.primary,
|
||||
Icons.analytics_outlined,
|
||||
'Running',
|
||||
),
|
||||
_buildStatusCard(
|
||||
'Pull requests',
|
||||
'Connection Pool',
|
||||
colorScheme.secondary,
|
||||
Icons.storage_outlined,
|
||||
'Connected',
|
||||
),
|
||||
_buildStatusCard(
|
||||
'Work Logs',
|
||||
'View Recent Activity',
|
||||
colorScheme.secondary,
|
||||
Icons.receipt_long_outlined,
|
||||
'Active',
|
||||
),
|
||||
_buildStatusCard(
|
||||
'Mindbox',
|
||||
'Metrics & Monitoring',
|
||||
colorScheme.secondary,
|
||||
Icons.speed_outlined,
|
||||
'Normal',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Recent Activity Section
|
||||
_buildSectionHeader('Recent Activity'),
|
||||
const SizedBox(height: 16),
|
||||
_buildActivityCard(
|
||||
'PR request',
|
||||
'Authentification_module.config is updated',
|
||||
'2 minutes ago',
|
||||
Icons.check_circle_outline,
|
||||
colorScheme.primary,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildActivityCard(
|
||||
'Database Sync',
|
||||
'Completed backup operation',
|
||||
'3 hours 11 minutes ago',
|
||||
Icons.cloud_done_outlined,
|
||||
colorScheme.secondary,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildActivityCard(
|
||||
'The Archivist agent created',
|
||||
'Modele configured and operational',
|
||||
'3 days ago',
|
||||
Icons.security_outlined,
|
||||
colorScheme.secondary,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Helper method: Get responsive padding based on screen width
|
||||
double _getPagePadding(double width) {
|
||||
if (width < 600) {
|
||||
return 16.0; // Mobile
|
||||
} else if (width < 1024) {
|
||||
return 24.0; // Tablet
|
||||
} else {
|
||||
return 32.0; // Desktop
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method: Build responsive grid
|
||||
Widget _buildResponsiveGrid(double width, List<Widget> children) {
|
||||
int crossAxisCount;
|
||||
double childAspectRatio;
|
||||
double spacing;
|
||||
|
||||
if (width < 600) {
|
||||
// Mobile: 1 column
|
||||
crossAxisCount = 1;
|
||||
childAspectRatio = 1.8;
|
||||
spacing = 12.0;
|
||||
} else if (width < 900) {
|
||||
// Tablet: 2 columns
|
||||
crossAxisCount = 2;
|
||||
childAspectRatio = 1.3;
|
||||
spacing = 16.0;
|
||||
} else if (width < 1200) {
|
||||
// Small desktop: 3 columns
|
||||
crossAxisCount = 3;
|
||||
childAspectRatio = 1.1;
|
||||
spacing = 20.0;
|
||||
} else {
|
||||
// Large desktop: 3 columns with more space
|
||||
crossAxisCount = 3;
|
||||
childAspectRatio = 1.3;
|
||||
spacing = 24.0;
|
||||
}
|
||||
|
||||
return GridView.count(
|
||||
crossAxisCount: crossAxisCount,
|
||||
childAspectRatio: childAspectRatio,
|
||||
crossAxisSpacing: spacing,
|
||||
mainAxisSpacing: spacing,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
|
||||
// Helper method: Build GetWidget-powered status card
|
||||
Widget _buildStatusCard(
|
||||
String title,
|
||||
String subtitle,
|
||||
Color color,
|
||||
IconData icon,
|
||||
String status,
|
||||
) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return FadeInUp(
|
||||
duration: const Duration(milliseconds: 400),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
color.withOpacity(0.25),
|
||||
color.withOpacity(0.12),
|
||||
],
|
||||
),
|
||||
border: Border.all(
|
||||
color: color.withOpacity(0.5),
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: Card(
|
||||
elevation: 4,
|
||||
color: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
GFToast.showToast(
|
||||
'$title tapped',
|
||||
context,
|
||||
toastPosition: GFToastPosition.BOTTOM,
|
||||
textStyle: const TextStyle(fontSize: 14, color: Colors.white),
|
||||
backgroundColor: color.withOpacity(0.9),
|
||||
toastDuration: 2,
|
||||
);
|
||||
},
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
color: color,
|
||||
width: 4,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GFAvatar(
|
||||
backgroundColor: color.withOpacity(0.3),
|
||||
child: Icon(icon, size: 28, color: color),
|
||||
radius: 26,
|
||||
shape: GFAvatarShape.standard,
|
||||
),
|
||||
Container(
|
||||
constraints: const BoxConstraints(minWidth: 100),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
status,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 0.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.onSurface,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
subtitle,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Helper method: Build section header
|
||||
Widget _buildSectionHeader(String title) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 4,
|
||||
height: 24,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.primary,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// Helper method: Build GetWidget-powered activity card
|
||||
Widget _buildActivityCard(
|
||||
String title,
|
||||
String description,
|
||||
String time,
|
||||
IconData icon,
|
||||
Color color,
|
||||
) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return FadeInLeft(
|
||||
duration: const Duration(milliseconds: 400),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(bottom: 0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: color.withOpacity(0.4),
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: Card(
|
||||
elevation: 2,
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
leading: GFAvatar(
|
||||
backgroundColor: color.withOpacity(0.25),
|
||||
child: Icon(icon, color: color, size: 22),
|
||||
radius: 22,
|
||||
shape: GFAvatarShape.circle,
|
||||
),
|
||||
title: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 15,
|
||||
color: colorScheme.onSurface,
|
||||
letterSpacing: 0.2,
|
||||
),
|
||||
),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(
|
||||
description,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 8),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(
|
||||
time,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'console_landing_page.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
|
||||
return MaterialApp(
|
||||
title: 'Svrnty Console - Sovereign AI Solutions',
|
||||
theme: MaterialTheme(textTheme).light(),
|
||||
darkTheme: MaterialTheme(textTheme).dark(),
|
||||
themeMode: ThemeMode.dark,
|
||||
home: const ConsoleLandingPage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyHomePage extends StatefulWidget {
|
||||
const MyHomePage({super.key, required this.title});
|
||||
|
||||
// This widget is the home page of your application. It is stateful, meaning
|
||||
// that it has a State object (defined below) that contains fields that affect
|
||||
// how it looks.
|
||||
|
||||
// This class is the configuration for the state. It holds the values (in this
|
||||
// case the title) provided by the parent (in this case the App widget) and
|
||||
// used by the build method of the State. Fields in a Widget subclass are
|
||||
// always marked "final".
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
State<MyHomePage> createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
int _counter = 0;
|
||||
|
||||
void _incrementCounter() {
|
||||
setState(() {
|
||||
// This call to setState tells the Flutter framework that something has
|
||||
// changed in this State, which causes it to rerun the build method below
|
||||
// so that the display can reflect the updated values. If we changed
|
||||
// _counter without calling setState(), then the build method would not be
|
||||
// called again, and so nothing would appear to happen.
|
||||
_counter++;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// This method is rerun every time setState is called, for instance as done
|
||||
// by the _incrementCounter method above.
|
||||
//
|
||||
// The Flutter framework has been optimized to make rerunning build methods
|
||||
// fast, so that you can just rebuild anything that needs updating rather
|
||||
// than having to individually change instances of widgets.
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
// TRY THIS: Try changing the color here to a specific color (to
|
||||
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
|
||||
// change color while the other colors stay the same.
|
||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||
// Here we take the value from the MyHomePage object that was created by
|
||||
// the App.build method, and use it to set our appbar title.
|
||||
title: Text(widget.title),
|
||||
),
|
||||
body: Center(
|
||||
// Center is a layout widget. It takes a single child and positions it
|
||||
// in the middle of the parent.
|
||||
child: Column(
|
||||
// Column is also a layout widget. It takes a list of children and
|
||||
// arranges them vertically. By default, it sizes itself to fit its
|
||||
// children horizontally, and tries to be as tall as its parent.
|
||||
//
|
||||
// Column has various properties to control how it sizes itself and
|
||||
// how it positions its children. Here we use mainAxisAlignment to
|
||||
// center the children vertically; the main axis here is the vertical
|
||||
// axis because Columns are vertical (the cross axis would be
|
||||
// horizontal).
|
||||
//
|
||||
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
|
||||
// action in the IDE, or press "p" in the console), to see the
|
||||
// wireframe for each widget.
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
const Text('You have pushed the button this many times:'),
|
||||
Text(
|
||||
'$_counter',
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _incrementCounter,
|
||||
tooltip: 'Increment',
|
||||
child: const Icon(Icons.add),
|
||||
), // This trailing comma makes auto-formatting nicer for build methods.
|
||||
);
|
||||
}
|
||||
}
|
||||
+408
@@ -0,0 +1,408 @@
|
||||
import "package:flutter/material.dart";
|
||||
|
||||
class MaterialTheme {
|
||||
final TextTheme textTheme;
|
||||
|
||||
const MaterialTheme(this.textTheme);
|
||||
|
||||
// Svrnty Brand Colors - Light Theme
|
||||
static ColorScheme lightScheme() {
|
||||
return const ColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primary: Color(0xffC44D58), // Svrnty Crimson Red
|
||||
surfaceTint: Color(0xffC44D58),
|
||||
onPrimary: Color(0xffffffff),
|
||||
primaryContainer: Color(0xffffd8db),
|
||||
onPrimaryContainer: Color(0xff8b3238),
|
||||
secondary: Color(0xff475C6C), // Svrnty Slate Blue
|
||||
onSecondary: Color(0xffffffff),
|
||||
secondaryContainer: Color(0xffd1dce7),
|
||||
onSecondaryContainer: Color(0xff2e3d4a),
|
||||
tertiary: Color(0xff5a4a6c),
|
||||
onTertiary: Color(0xffffffff),
|
||||
tertiaryContainer: Color(0xffe0d3f2),
|
||||
onTertiaryContainer: Color(0xff3d2f4d),
|
||||
error: Color(0xffba1a1a),
|
||||
onError: Color(0xffffffff),
|
||||
errorContainer: Color(0xffffdad6),
|
||||
onErrorContainer: Color(0xff93000a),
|
||||
surface: Color(0xfffafafa),
|
||||
onSurface: Color(0xff1a1c1e),
|
||||
onSurfaceVariant: Color(0xff43474e),
|
||||
outline: Color(0xff74777f),
|
||||
outlineVariant: Color(0xffc4c6cf),
|
||||
shadow: Color(0xff000000),
|
||||
scrim: Color(0xff000000),
|
||||
inverseSurface: Color(0xff2f3033),
|
||||
inversePrimary: Color(0xffffb3b9),
|
||||
primaryFixed: Color(0xffffd8db),
|
||||
onPrimaryFixed: Color(0xff410008),
|
||||
primaryFixedDim: Color(0xffffb3b9),
|
||||
onPrimaryFixedVariant: Color(0xff8b3238),
|
||||
secondaryFixed: Color(0xffd1dce7),
|
||||
onSecondaryFixed: Color(0xff0f1a24),
|
||||
secondaryFixedDim: Color(0xffb5c0cb),
|
||||
onSecondaryFixedVariant: Color(0xff2e3d4a),
|
||||
tertiaryFixed: Color(0xffe0d3f2),
|
||||
onTertiaryFixed: Color(0xff1f122f),
|
||||
tertiaryFixedDim: Color(0xffc4b7d6),
|
||||
onTertiaryFixedVariant: Color(0xff3d2f4d),
|
||||
surfaceDim: Color(0xffdadcde),
|
||||
surfaceBright: Color(0xfffafafa),
|
||||
surfaceContainerLowest: Color(0xffffffff),
|
||||
surfaceContainerLow: Color(0xfff4f5f7),
|
||||
surfaceContainer: Color(0xffeef0f2),
|
||||
surfaceContainerHigh: Color(0xffe8eaec),
|
||||
surfaceContainerHighest: Color(0xffe2e4e7),
|
||||
);
|
||||
}
|
||||
|
||||
ThemeData light() {
|
||||
return theme(lightScheme());
|
||||
}
|
||||
|
||||
static ColorScheme lightMediumContrastScheme() {
|
||||
return const ColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primary: Color(0xff0d3665),
|
||||
surfaceTint: Color(0xff3d5f90),
|
||||
onPrimary: Color(0xffffffff),
|
||||
primaryContainer: Color(0xff4d6ea0),
|
||||
onPrimaryContainer: Color(0xffffffff),
|
||||
secondary: Color(0xff2d3747),
|
||||
onSecondary: Color(0xffffffff),
|
||||
secondaryContainer: Color(0xff636d80),
|
||||
onSecondaryContainer: Color(0xffffffff),
|
||||
tertiary: Color(0xff442e4c),
|
||||
onTertiary: Color(0xffffffff),
|
||||
tertiaryContainer: Color(0xff7d6485),
|
||||
onTertiaryContainer: Color(0xffffffff),
|
||||
error: Color(0xff740006),
|
||||
onError: Color(0xffffffff),
|
||||
errorContainer: Color(0xffcf2c27),
|
||||
onErrorContainer: Color(0xffffffff),
|
||||
surface: Color(0xfff9f9ff),
|
||||
onSurface: Color(0xff0f1116),
|
||||
onSurfaceVariant: Color(0xff33363d),
|
||||
outline: Color(0xff4f525a),
|
||||
outlineVariant: Color(0xff6a6d75),
|
||||
shadow: Color(0xff000000),
|
||||
scrim: Color(0xff000000),
|
||||
inverseSurface: Color(0xff2e3035),
|
||||
inversePrimary: Color(0xffa6c8ff),
|
||||
primaryFixed: Color(0xff4d6ea0),
|
||||
onPrimaryFixed: Color(0xffffffff),
|
||||
primaryFixedDim: Color(0xff335686),
|
||||
onPrimaryFixedVariant: Color(0xffffffff),
|
||||
secondaryFixed: Color(0xff636d80),
|
||||
onSecondaryFixed: Color(0xffffffff),
|
||||
secondaryFixedDim: Color(0xff4b5567),
|
||||
onSecondaryFixedVariant: Color(0xffffffff),
|
||||
tertiaryFixed: Color(0xff7d6485),
|
||||
onTertiaryFixed: Color(0xffffffff),
|
||||
tertiaryFixedDim: Color(0xff644c6c),
|
||||
onTertiaryFixedVariant: Color(0xffffffff),
|
||||
surfaceDim: Color(0xffc5c6cd),
|
||||
surfaceBright: Color(0xfff9f9ff),
|
||||
surfaceContainerLowest: Color(0xffffffff),
|
||||
surfaceContainerLow: Color(0xfff3f3fa),
|
||||
surfaceContainer: Color(0xffe7e8ee),
|
||||
surfaceContainerHigh: Color(0xffdcdce3),
|
||||
surfaceContainerHighest: Color(0xffd0d1d8),
|
||||
);
|
||||
}
|
||||
|
||||
ThemeData lightMediumContrast() {
|
||||
return theme(lightMediumContrastScheme());
|
||||
}
|
||||
|
||||
static ColorScheme lightHighContrastScheme() {
|
||||
return const ColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primary: Color(0xff002c58),
|
||||
surfaceTint: Color(0xff3d5f90),
|
||||
onPrimary: Color(0xffffffff),
|
||||
primaryContainer: Color(0xff264a79),
|
||||
onPrimaryContainer: Color(0xffffffff),
|
||||
secondary: Color(0xff232d3d),
|
||||
onSecondary: Color(0xffffffff),
|
||||
secondaryContainer: Color(0xff404a5b),
|
||||
onSecondaryContainer: Color(0xffffffff),
|
||||
tertiary: Color(0xff392441),
|
||||
onTertiary: Color(0xffffffff),
|
||||
tertiaryContainer: Color(0xff584160),
|
||||
onTertiaryContainer: Color(0xffffffff),
|
||||
error: Color(0xff600004),
|
||||
onError: Color(0xffffffff),
|
||||
errorContainer: Color(0xff98000a),
|
||||
onErrorContainer: Color(0xffffffff),
|
||||
surface: Color(0xfff9f9ff),
|
||||
onSurface: Color(0xff000000),
|
||||
onSurfaceVariant: Color(0xff000000),
|
||||
outline: Color(0xff292c33),
|
||||
outlineVariant: Color(0xff464951),
|
||||
shadow: Color(0xff000000),
|
||||
scrim: Color(0xff000000),
|
||||
inverseSurface: Color(0xff2e3035),
|
||||
inversePrimary: Color(0xffa6c8ff),
|
||||
primaryFixed: Color(0xff264a79),
|
||||
onPrimaryFixed: Color(0xffffffff),
|
||||
primaryFixedDim: Color(0xff063361),
|
||||
onPrimaryFixedVariant: Color(0xffffffff),
|
||||
secondaryFixed: Color(0xff404a5b),
|
||||
onSecondaryFixed: Color(0xffffffff),
|
||||
secondaryFixedDim: Color(0xff293343),
|
||||
onSecondaryFixedVariant: Color(0xffffffff),
|
||||
tertiaryFixed: Color(0xff584160),
|
||||
onTertiaryFixed: Color(0xffffffff),
|
||||
tertiaryFixedDim: Color(0xff402b48),
|
||||
onTertiaryFixedVariant: Color(0xffffffff),
|
||||
surfaceDim: Color(0xffb7b8bf),
|
||||
surfaceBright: Color(0xfff9f9ff),
|
||||
surfaceContainerLowest: Color(0xffffffff),
|
||||
surfaceContainerLow: Color(0xfff0f0f7),
|
||||
surfaceContainer: Color(0xffe1e2e9),
|
||||
surfaceContainerHigh: Color(0xffd3d4da),
|
||||
surfaceContainerHighest: Color(0xffc5c6cd),
|
||||
);
|
||||
}
|
||||
|
||||
ThemeData lightHighContrast() {
|
||||
return theme(lightHighContrastScheme());
|
||||
}
|
||||
|
||||
// Svrnty Brand Colors - Dark Theme (Bold & Saturated)
|
||||
static ColorScheme darkScheme() {
|
||||
return const ColorScheme(
|
||||
brightness: Brightness.dark,
|
||||
primary: Color(0xffF3574E), // Bold Svrnty Crimson Red (slightly desaturated)
|
||||
surfaceTint: Color(0xffF3574E),
|
||||
onPrimary: Color(0xffffffff),
|
||||
primaryContainer: Color(0xffC44D58), // True brand crimson
|
||||
onPrimaryContainer: Color(0xffffffff),
|
||||
secondary: Color(0xff5A6F7D), // Rich Svrnty Slate Blue
|
||||
onSecondary: Color(0xffffffff),
|
||||
secondaryContainer: Color(0xff475C6C), // True brand slate
|
||||
onSecondaryContainer: Color(0xffffffff),
|
||||
tertiary: Color(0xffA78BBF), // Richer purple
|
||||
onTertiary: Color(0xffffffff),
|
||||
tertiaryContainer: Color(0xff8B6FA3),
|
||||
onTertiaryContainer: Color(0xffffffff),
|
||||
error: Color(0xffFF5449),
|
||||
onError: Color(0xffffffff),
|
||||
errorContainer: Color(0xffD32F2F),
|
||||
onErrorContainer: Color(0xffffffff),
|
||||
surface: Color(0xff1a1c1e), // Svrnty Dark Background
|
||||
onSurface: Color(0xfff0f0f0),
|
||||
onSurfaceVariant: Color(0xffc8cad0),
|
||||
outline: Color(0xff8d9199),
|
||||
outlineVariant: Color(0xff43474e),
|
||||
shadow: Color(0xff000000),
|
||||
scrim: Color(0xff000000),
|
||||
inverseSurface: Color(0xffe2e4e7),
|
||||
inversePrimary: Color(0xffC44D58),
|
||||
primaryFixed: Color(0xffFFD8DB),
|
||||
onPrimaryFixed: Color(0xff2d0008),
|
||||
primaryFixedDim: Color(0xffF3574E),
|
||||
onPrimaryFixedVariant: Color(0xffffffff),
|
||||
secondaryFixed: Color(0xffD1DCE7),
|
||||
onSecondaryFixed: Color(0xff0f1a24),
|
||||
secondaryFixedDim: Color(0xff5A6F7D),
|
||||
onSecondaryFixedVariant: Color(0xffffffff),
|
||||
tertiaryFixed: Color(0xffE0D3F2),
|
||||
onTertiaryFixed: Color(0xff1f122f),
|
||||
tertiaryFixedDim: Color(0xffA78BBF),
|
||||
onTertiaryFixedVariant: Color(0xffffffff),
|
||||
surfaceDim: Color(0xff1a1c1e),
|
||||
surfaceBright: Color(0xff404244),
|
||||
surfaceContainerLowest: Color(0xff0f1113),
|
||||
surfaceContainerLow: Color(0xff1f2123),
|
||||
surfaceContainer: Color(0xff23252a),
|
||||
surfaceContainerHigh: Color(0xff2d2f35),
|
||||
surfaceContainerHighest: Color(0xff383940),
|
||||
);
|
||||
}
|
||||
|
||||
ThemeData dark() {
|
||||
return theme(darkScheme());
|
||||
}
|
||||
|
||||
static ColorScheme darkMediumContrastScheme() {
|
||||
return const ColorScheme(
|
||||
brightness: Brightness.dark,
|
||||
primary: Color(0xffcbddff),
|
||||
surfaceTint: Color(0xffa6c8ff),
|
||||
onPrimary: Color(0xff00264d),
|
||||
primaryContainer: Color(0xff7192c6),
|
||||
onPrimaryContainer: Color(0xff000000),
|
||||
secondary: Color(0xffd3ddf2),
|
||||
onSecondary: Color(0xff1c2636),
|
||||
secondaryContainer: Color(0xff8791a5),
|
||||
onSecondaryContainer: Color(0xff000000),
|
||||
tertiary: Color(0xfff1d2f8),
|
||||
onTertiary: Color(0xff321e3a),
|
||||
tertiaryContainer: Color(0xffa387aa),
|
||||
onTertiaryContainer: Color(0xff000000),
|
||||
error: Color(0xffffd2cc),
|
||||
onError: Color(0xff540003),
|
||||
errorContainer: Color(0xffff5449),
|
||||
onErrorContainer: Color(0xff000000),
|
||||
surface: Color(0xff111318),
|
||||
onSurface: Color(0xffffffff),
|
||||
onSurfaceVariant: Color(0xffdadce5),
|
||||
outline: Color(0xffafb2bb),
|
||||
outlineVariant: Color(0xff8d9099),
|
||||
shadow: Color(0xff000000),
|
||||
scrim: Color(0xff000000),
|
||||
inverseSurface: Color(0xffe1e2e9),
|
||||
inversePrimary: Color(0xff254978),
|
||||
primaryFixed: Color(0xffd5e3ff),
|
||||
onPrimaryFixed: Color(0xff001129),
|
||||
primaryFixedDim: Color(0xffa6c8ff),
|
||||
onPrimaryFixedVariant: Color(0xff0d3665),
|
||||
secondaryFixed: Color(0xffd9e3f8),
|
||||
onSecondaryFixed: Color(0xff071120),
|
||||
secondaryFixedDim: Color(0xffbdc7dc),
|
||||
onSecondaryFixedVariant: Color(0xff2d3747),
|
||||
tertiaryFixed: Color(0xfff8d8ff),
|
||||
onTertiaryFixed: Color(0xff1c0924),
|
||||
tertiaryFixedDim: Color(0xffdbbde2),
|
||||
onTertiaryFixedVariant: Color(0xff442e4c),
|
||||
surfaceDim: Color(0xff111318),
|
||||
surfaceBright: Color(0xff42444a),
|
||||
surfaceContainerLowest: Color(0xff05070c),
|
||||
surfaceContainerLow: Color(0xff1b1e22),
|
||||
surfaceContainer: Color(0xff26282d),
|
||||
surfaceContainerHigh: Color(0xff303338),
|
||||
surfaceContainerHighest: Color(0xff3b3e43),
|
||||
);
|
||||
}
|
||||
|
||||
ThemeData darkMediumContrast() {
|
||||
return theme(darkMediumContrastScheme());
|
||||
}
|
||||
|
||||
static ColorScheme darkHighContrastScheme() {
|
||||
return const ColorScheme(
|
||||
brightness: Brightness.dark,
|
||||
primary: Color(0xffeaf0ff),
|
||||
surfaceTint: Color(0xffa6c8ff),
|
||||
onPrimary: Color(0xff000000),
|
||||
primaryContainer: Color(0xffa3c4fb),
|
||||
onPrimaryContainer: Color(0xff000b1e),
|
||||
secondary: Color(0xffeaf0ff),
|
||||
onSecondary: Color(0xff000000),
|
||||
secondaryContainer: Color(0xffb9c3d8),
|
||||
onSecondaryContainer: Color(0xff030b1a),
|
||||
tertiary: Color(0xfffeeaff),
|
||||
onTertiary: Color(0xff000000),
|
||||
tertiaryContainer: Color(0xffd7b9de),
|
||||
onTertiaryContainer: Color(0xff16041e),
|
||||
error: Color(0xffffece9),
|
||||
onError: Color(0xff000000),
|
||||
errorContainer: Color(0xffffaea4),
|
||||
onErrorContainer: Color(0xff220001),
|
||||
surface: Color(0xff111318),
|
||||
onSurface: Color(0xffffffff),
|
||||
onSurfaceVariant: Color(0xffffffff),
|
||||
outline: Color(0xffedf0f9),
|
||||
outlineVariant: Color(0xffc0c2cb),
|
||||
shadow: Color(0xff000000),
|
||||
scrim: Color(0xff000000),
|
||||
inverseSurface: Color(0xffe1e2e9),
|
||||
inversePrimary: Color(0xff254978),
|
||||
primaryFixed: Color(0xffd5e3ff),
|
||||
onPrimaryFixed: Color(0xff000000),
|
||||
primaryFixedDim: Color(0xffa6c8ff),
|
||||
onPrimaryFixedVariant: Color(0xff001129),
|
||||
secondaryFixed: Color(0xffd9e3f8),
|
||||
onSecondaryFixed: Color(0xff000000),
|
||||
secondaryFixedDim: Color(0xffbdc7dc),
|
||||
onSecondaryFixedVariant: Color(0xff071120),
|
||||
tertiaryFixed: Color(0xfff8d8ff),
|
||||
onTertiaryFixed: Color(0xff000000),
|
||||
tertiaryFixedDim: Color(0xffdbbde2),
|
||||
onTertiaryFixedVariant: Color(0xff1c0924),
|
||||
surfaceDim: Color(0xff111318),
|
||||
surfaceBright: Color(0xff4e5055),
|
||||
surfaceContainerLowest: Color(0xff000000),
|
||||
surfaceContainerLow: Color(0xff1d2024),
|
||||
surfaceContainer: Color(0xff2e3035),
|
||||
surfaceContainerHigh: Color(0xff393b41),
|
||||
surfaceContainerHighest: Color(0xff45474c),
|
||||
);
|
||||
}
|
||||
|
||||
ThemeData darkHighContrast() {
|
||||
return theme(darkHighContrastScheme());
|
||||
}
|
||||
|
||||
|
||||
ThemeData theme(ColorScheme colorScheme) => ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: colorScheme.brightness,
|
||||
colorScheme: colorScheme,
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.bold),
|
||||
displayMedium: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.bold),
|
||||
displaySmall: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.bold),
|
||||
headlineLarge: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w600),
|
||||
headlineMedium: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w600),
|
||||
headlineSmall: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w600),
|
||||
titleLarge: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w600),
|
||||
titleMedium: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
|
||||
titleSmall: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
|
||||
bodyLarge: TextStyle(fontFamily: 'Montserrat'),
|
||||
bodyMedium: TextStyle(fontFamily: 'Montserrat'),
|
||||
bodySmall: TextStyle(fontFamily: 'Montserrat'),
|
||||
labelLarge: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
|
||||
labelMedium: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
|
||||
labelSmall: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
|
||||
).apply(
|
||||
bodyColor: colorScheme.onSurface,
|
||||
displayColor: colorScheme.onSurface,
|
||||
),
|
||||
fontFamily: 'Montserrat',
|
||||
scaffoldBackgroundColor: colorScheme.surface,
|
||||
canvasColor: colorScheme.surface,
|
||||
);
|
||||
|
||||
|
||||
List<ExtendedColor> get extendedColors => [
|
||||
];
|
||||
}
|
||||
|
||||
class ExtendedColor {
|
||||
final Color seed, value;
|
||||
final ColorFamily light;
|
||||
final ColorFamily lightHighContrast;
|
||||
final ColorFamily lightMediumContrast;
|
||||
final ColorFamily dark;
|
||||
final ColorFamily darkHighContrast;
|
||||
final ColorFamily darkMediumContrast;
|
||||
|
||||
const ExtendedColor({
|
||||
required this.seed,
|
||||
required this.value,
|
||||
required this.light,
|
||||
required this.lightHighContrast,
|
||||
required this.lightMediumContrast,
|
||||
required this.dark,
|
||||
required this.darkHighContrast,
|
||||
required this.darkMediumContrast,
|
||||
});
|
||||
}
|
||||
|
||||
class ColorFamily {
|
||||
const ColorFamily({
|
||||
required this.color,
|
||||
required this.onColor,
|
||||
required this.colorContainer,
|
||||
required this.onColorContainer,
|
||||
});
|
||||
|
||||
final Color color;
|
||||
final Color onColor;
|
||||
final Color colorContainer;
|
||||
final Color onColorContainer;
|
||||
}
|
||||
Reference in New Issue
Block a user