cronixui 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -27
- package/package.json +2 -1
- package/packages/flutter/lib/cronixui.dart +41 -0
- package/packages/flutter/lib/src/tokens/colors.dart +34 -0
- package/packages/flutter/lib/src/tokens/spacing.dart +54 -0
- package/packages/flutter/lib/src/tokens/theme.dart +174 -0
- package/packages/flutter/lib/src/widgets/cn_accordion.dart +254 -0
- package/packages/flutter/lib/src/widgets/cn_alert.dart +137 -0
- package/packages/flutter/lib/src/widgets/cn_avatar.dart +98 -0
- package/packages/flutter/lib/src/widgets/cn_badge.dart +80 -0
- package/packages/flutter/lib/src/widgets/cn_breadcrumb.dart +88 -0
- package/packages/flutter/lib/src/widgets/cn_button.dart +137 -0
- package/packages/flutter/lib/src/widgets/cn_card.dart +99 -0
- package/packages/flutter/lib/src/widgets/cn_checkbox.dart +77 -0
- package/packages/flutter/lib/src/widgets/cn_command_palette.dart +299 -0
- package/packages/flutter/lib/src/widgets/cn_container.dart +131 -0
- package/packages/flutter/lib/src/widgets/cn_dropdown.dart +149 -0
- package/packages/flutter/lib/src/widgets/cn_file_input.dart +113 -0
- package/packages/flutter/lib/src/widgets/cn_footer.dart +108 -0
- package/packages/flutter/lib/src/widgets/cn_header.dart +173 -0
- package/packages/flutter/lib/src/widgets/cn_input.dart +142 -0
- package/packages/flutter/lib/src/widgets/cn_list.dart +150 -0
- package/packages/flutter/lib/src/widgets/cn_modal.dart +213 -0
- package/packages/flutter/lib/src/widgets/cn_nav.dart +157 -0
- package/packages/flutter/lib/src/widgets/cn_pagination.dart +193 -0
- package/packages/flutter/lib/src/widgets/cn_progress.dart +146 -0
- package/packages/flutter/lib/src/widgets/cn_radio.dart +133 -0
- package/packages/flutter/lib/src/widgets/cn_search.dart +183 -0
- package/packages/flutter/lib/src/widgets/cn_select.dart +244 -0
- package/packages/flutter/lib/src/widgets/cn_sidebar.dart +207 -0
- package/packages/flutter/lib/src/widgets/cn_skeleton.dart +136 -0
- package/packages/flutter/lib/src/widgets/cn_slider.dart +141 -0
- package/packages/flutter/lib/src/widgets/cn_spinner.dart +85 -0
- package/packages/flutter/lib/src/widgets/cn_stat.dart +135 -0
- package/packages/flutter/lib/src/widgets/cn_table.dart +136 -0
- package/packages/flutter/lib/src/widgets/cn_tabs.dart +229 -0
- package/packages/flutter/lib/src/widgets/cn_tag.dart +185 -0
- package/packages/flutter/lib/src/widgets/cn_textarea.dart +143 -0
- package/packages/flutter/lib/src/widgets/cn_toast.dart +121 -0
- package/packages/flutter/lib/src/widgets/cn_toggle.dart +78 -0
- package/packages/flutter/lib/src/widgets/cn_tooltip.dart +118 -0
- package/packages/flutter/pubspec.yaml +20 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import '../tokens/colors.dart';
|
|
3
|
+
import '../tokens/spacing.dart';
|
|
4
|
+
|
|
5
|
+
enum CnAlertVariant { info, success, warning, error }
|
|
6
|
+
|
|
7
|
+
class CnAlert extends StatelessWidget {
|
|
8
|
+
final String message;
|
|
9
|
+
final String? title;
|
|
10
|
+
final CnAlertVariant variant;
|
|
11
|
+
final IconData? icon;
|
|
12
|
+
final bool showIcon;
|
|
13
|
+
final VoidCallback? onDismiss;
|
|
14
|
+
final Widget? action;
|
|
15
|
+
|
|
16
|
+
const CnAlert({
|
|
17
|
+
super.key,
|
|
18
|
+
required this.message,
|
|
19
|
+
this.title,
|
|
20
|
+
this.variant = CnAlertVariant.info,
|
|
21
|
+
this.icon,
|
|
22
|
+
this.showIcon = true,
|
|
23
|
+
this.onDismiss,
|
|
24
|
+
this.action,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
IconData get _defaultIcon {
|
|
28
|
+
switch (variant) {
|
|
29
|
+
case CnAlertVariant.info:
|
|
30
|
+
return Icons.info_outline;
|
|
31
|
+
case CnAlertVariant.success:
|
|
32
|
+
return Icons.check_circle_outline;
|
|
33
|
+
case CnAlertVariant.warning:
|
|
34
|
+
return Icons.warning_amber_outlined;
|
|
35
|
+
case CnAlertVariant.error:
|
|
36
|
+
return Icons.error_outline;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
Color get _backgroundColor {
|
|
41
|
+
switch (variant) {
|
|
42
|
+
case CnAlertVariant.info:
|
|
43
|
+
return CronixColors.info.withOpacity(0.1);
|
|
44
|
+
case CnAlertVariant.success:
|
|
45
|
+
return CronixColors.success.withOpacity(0.1);
|
|
46
|
+
case CnAlertVariant.warning:
|
|
47
|
+
return CronixColors.warning.withOpacity(0.1);
|
|
48
|
+
case CnAlertVariant.error:
|
|
49
|
+
return CronixColors.error.withOpacity(0.1);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Color get _borderColor {
|
|
54
|
+
switch (variant) {
|
|
55
|
+
case CnAlertVariant.info:
|
|
56
|
+
return CronixColors.info;
|
|
57
|
+
case CnAlertVariant.success:
|
|
58
|
+
return CronixColors.success;
|
|
59
|
+
case CnAlertVariant.warning:
|
|
60
|
+
return CronixColors.warning;
|
|
61
|
+
case CnAlertVariant.error:
|
|
62
|
+
return CronixColors.error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
Color get _iconColor {
|
|
67
|
+
switch (variant) {
|
|
68
|
+
case CnAlertVariant.info:
|
|
69
|
+
return CronixColors.info;
|
|
70
|
+
case CnAlertVariant.success:
|
|
71
|
+
return CronixColors.success;
|
|
72
|
+
case CnAlertVariant.warning:
|
|
73
|
+
return CronixColors.warning;
|
|
74
|
+
case CnAlertVariant.error:
|
|
75
|
+
return CronixColors.error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@override
|
|
80
|
+
Widget build(BuildContext context) {
|
|
81
|
+
return Container(
|
|
82
|
+
padding: const EdgeInsets.all(16),
|
|
83
|
+
decoration: BoxDecoration(
|
|
84
|
+
color: _backgroundColor,
|
|
85
|
+
borderRadius: CronixRadius.radiusMD,
|
|
86
|
+
border: Border.all(color: _borderColor.withOpacity(0.5)),
|
|
87
|
+
),
|
|
88
|
+
child: Row(
|
|
89
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
90
|
+
children: [
|
|
91
|
+
if (showIcon) ...[
|
|
92
|
+
Icon(icon ?? _defaultIcon, color: _iconColor, size: 20),
|
|
93
|
+
const SizedBox(width: 12),
|
|
94
|
+
],
|
|
95
|
+
Expanded(
|
|
96
|
+
child: Column(
|
|
97
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
98
|
+
children: [
|
|
99
|
+
if (title != null) ...[
|
|
100
|
+
Text(
|
|
101
|
+
title!,
|
|
102
|
+
style: const TextStyle(
|
|
103
|
+
color: CronixColors.text,
|
|
104
|
+
fontSize: 14,
|
|
105
|
+
fontWeight: FontWeight.w600,
|
|
106
|
+
),
|
|
107
|
+
),
|
|
108
|
+
const SizedBox(height: 4),
|
|
109
|
+
],
|
|
110
|
+
Text(
|
|
111
|
+
message,
|
|
112
|
+
style: const TextStyle(
|
|
113
|
+
color: CronixColors.text,
|
|
114
|
+
fontSize: 14,
|
|
115
|
+
),
|
|
116
|
+
),
|
|
117
|
+
if (action != null) ...[
|
|
118
|
+
const SizedBox(height: 8),
|
|
119
|
+
action!,
|
|
120
|
+
],
|
|
121
|
+
],
|
|
122
|
+
),
|
|
123
|
+
),
|
|
124
|
+
if (onDismiss != null)
|
|
125
|
+
GestureDetector(
|
|
126
|
+
onTap: onDismiss,
|
|
127
|
+
child: const Icon(
|
|
128
|
+
Icons.close,
|
|
129
|
+
color: CronixColors.textSecondary,
|
|
130
|
+
size: 18,
|
|
131
|
+
),
|
|
132
|
+
),
|
|
133
|
+
],
|
|
134
|
+
),
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import '../tokens/colors.dart';
|
|
3
|
+
import '../tokens/spacing.dart';
|
|
4
|
+
|
|
5
|
+
class CnAvatar extends StatelessWidget {
|
|
6
|
+
final String? imageUrl;
|
|
7
|
+
final String? name;
|
|
8
|
+
final double size;
|
|
9
|
+
final Color? backgroundColor;
|
|
10
|
+
final Color? textColor;
|
|
11
|
+
final VoidCallback? onTap;
|
|
12
|
+
final IconData? fallbackIcon;
|
|
13
|
+
|
|
14
|
+
const CnAvatar({
|
|
15
|
+
super.key,
|
|
16
|
+
this.imageUrl,
|
|
17
|
+
this.name,
|
|
18
|
+
this.size = 40,
|
|
19
|
+
this.backgroundColor,
|
|
20
|
+
this.textColor,
|
|
21
|
+
this.onTap,
|
|
22
|
+
this.fallbackIcon,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
String get _initials {
|
|
26
|
+
if (name == null || name!.isEmpty) return '';
|
|
27
|
+
final parts = name!.trim().split(' ');
|
|
28
|
+
if (parts.length >= 2) {
|
|
29
|
+
return '${parts[0][0]}${parts[1][0]}'.toUpperCase();
|
|
30
|
+
}
|
|
31
|
+
return parts[0][0].toUpperCase();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@override
|
|
35
|
+
Widget build(BuildContext context) {
|
|
36
|
+
Widget content;
|
|
37
|
+
|
|
38
|
+
if (imageUrl != null && imageUrl!.isNotEmpty) {
|
|
39
|
+
content = ClipRRect(
|
|
40
|
+
borderRadius: BorderRadius.circular(size / 2),
|
|
41
|
+
child: Image.network(
|
|
42
|
+
imageUrl!,
|
|
43
|
+
width: size,
|
|
44
|
+
height: size,
|
|
45
|
+
fit: BoxFit.cover,
|
|
46
|
+
errorBuilder: (_, __, ___) => _buildFallback(),
|
|
47
|
+
),
|
|
48
|
+
);
|
|
49
|
+
} else if (_initials.isNotEmpty) {
|
|
50
|
+
content = Container(
|
|
51
|
+
width: size,
|
|
52
|
+
height: size,
|
|
53
|
+
decoration: BoxDecoration(
|
|
54
|
+
color: backgroundColor ?? CronixColors.accent,
|
|
55
|
+
borderRadius: BorderRadius.circular(size / 2),
|
|
56
|
+
),
|
|
57
|
+
child: Center(
|
|
58
|
+
child: Text(
|
|
59
|
+
_initials,
|
|
60
|
+
style: TextStyle(
|
|
61
|
+
color: textColor ?? CronixColors.text,
|
|
62
|
+
fontSize: size * 0.4,
|
|
63
|
+
fontWeight: FontWeight.w600,
|
|
64
|
+
),
|
|
65
|
+
),
|
|
66
|
+
),
|
|
67
|
+
);
|
|
68
|
+
} else {
|
|
69
|
+
content = _buildFallback();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (onTap != null) {
|
|
73
|
+
return GestureDetector(
|
|
74
|
+
onTap: onTap,
|
|
75
|
+
child: content,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return content;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
Widget _buildFallback() {
|
|
83
|
+
return Container(
|
|
84
|
+
width: size,
|
|
85
|
+
height: size,
|
|
86
|
+
decoration: BoxDecoration(
|
|
87
|
+
color: backgroundColor ?? CronixColors.surfaceLight,
|
|
88
|
+
borderRadius: BorderRadius.circular(size / 2),
|
|
89
|
+
border: Border.all(color: CronixColors.border),
|
|
90
|
+
),
|
|
91
|
+
child: Icon(
|
|
92
|
+
fallbackIcon ?? Icons.person,
|
|
93
|
+
size: size * 0.5,
|
|
94
|
+
color: CronixColors.textSecondary,
|
|
95
|
+
),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import '../tokens/colors.dart';
|
|
3
|
+
import '../tokens/spacing.dart';
|
|
4
|
+
|
|
5
|
+
enum CnBadgeVariant { default_, success, warning, error, info }
|
|
6
|
+
|
|
7
|
+
class CnBadge extends StatelessWidget {
|
|
8
|
+
final String label;
|
|
9
|
+
final CnBadgeVariant variant;
|
|
10
|
+
final IconData? icon;
|
|
11
|
+
final double fontSize;
|
|
12
|
+
final EdgeInsetsGeometry? padding;
|
|
13
|
+
|
|
14
|
+
const CnBadge({
|
|
15
|
+
super.key,
|
|
16
|
+
required this.label,
|
|
17
|
+
this.variant = CnBadgeVariant.default_,
|
|
18
|
+
this.icon,
|
|
19
|
+
this.fontSize = 12,
|
|
20
|
+
this.padding,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
@override
|
|
24
|
+
Widget build(BuildContext context) {
|
|
25
|
+
return Container(
|
|
26
|
+
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
27
|
+
decoration: BoxDecoration(
|
|
28
|
+
color: _getBackgroundColor(),
|
|
29
|
+
borderRadius: CronixRadius.radiusFull,
|
|
30
|
+
),
|
|
31
|
+
child: Row(
|
|
32
|
+
mainAxisSize: MainAxisSize.min,
|
|
33
|
+
children: [
|
|
34
|
+
if (icon != null) ...[
|
|
35
|
+
Icon(icon, size: fontSize, color: _getTextColor()),
|
|
36
|
+
const SizedBox(width: 4),
|
|
37
|
+
],
|
|
38
|
+
Text(
|
|
39
|
+
label,
|
|
40
|
+
style: TextStyle(
|
|
41
|
+
color: _getTextColor(),
|
|
42
|
+
fontSize: fontSize,
|
|
43
|
+
fontWeight: FontWeight.w500,
|
|
44
|
+
),
|
|
45
|
+
),
|
|
46
|
+
],
|
|
47
|
+
),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Color _getBackgroundColor() {
|
|
52
|
+
switch (variant) {
|
|
53
|
+
case CnBadgeVariant.default_:
|
|
54
|
+
return CronixColors.surfaceLight;
|
|
55
|
+
case CnBadgeVariant.success:
|
|
56
|
+
return CronixColors.success.withOpacity(0.2);
|
|
57
|
+
case CnBadgeVariant.warning:
|
|
58
|
+
return CronixColors.warning.withOpacity(0.2);
|
|
59
|
+
case CnBadgeVariant.error:
|
|
60
|
+
return CronixColors.error.withOpacity(0.2);
|
|
61
|
+
case CnBadgeVariant.info:
|
|
62
|
+
return CronixColors.info.withOpacity(0.2);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
Color _getTextColor() {
|
|
67
|
+
switch (variant) {
|
|
68
|
+
case CnBadgeVariant.default_:
|
|
69
|
+
return CronixColors.text;
|
|
70
|
+
case CnBadgeVariant.success:
|
|
71
|
+
return CronixColors.success;
|
|
72
|
+
case CnBadgeVariant.warning:
|
|
73
|
+
return CronixColors.warning;
|
|
74
|
+
case CnBadgeVariant.error:
|
|
75
|
+
return CronixColors.error;
|
|
76
|
+
case CnBadgeVariant.info:
|
|
77
|
+
return CronixColors.info;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import '../tokens/colors.dart';
|
|
3
|
+
import '../tokens/spacing.dart';
|
|
4
|
+
|
|
5
|
+
class CnBreadcrumbItem {
|
|
6
|
+
final String label;
|
|
7
|
+
final VoidCallback? onTap;
|
|
8
|
+
final IconData? icon;
|
|
9
|
+
|
|
10
|
+
const CnBreadcrumbItem({
|
|
11
|
+
required this.label,
|
|
12
|
+
this.onTap,
|
|
13
|
+
this.icon,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class CnBreadcrumb extends StatelessWidget {
|
|
18
|
+
final List<CnBreadcrumbItem> items;
|
|
19
|
+
final IconData separator;
|
|
20
|
+
final double fontSize;
|
|
21
|
+
final Color? textColor;
|
|
22
|
+
final Color? activeColor;
|
|
23
|
+
|
|
24
|
+
const CnBreadcrumb({
|
|
25
|
+
super.key,
|
|
26
|
+
required this.items,
|
|
27
|
+
this.separator = Icons.chevron_right,
|
|
28
|
+
this.fontSize = 14,
|
|
29
|
+
this.textColor,
|
|
30
|
+
this.activeColor,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
@override
|
|
34
|
+
Widget build(BuildContext context) {
|
|
35
|
+
return SingleChildScrollView(
|
|
36
|
+
scrollDirection: Axis.horizontal,
|
|
37
|
+
child: Row(
|
|
38
|
+
children: items.asMap().entries.map((entry) {
|
|
39
|
+
final index = entry.key;
|
|
40
|
+
final item = entry.value;
|
|
41
|
+
final isLast = index == items.length - 1;
|
|
42
|
+
|
|
43
|
+
return Row(
|
|
44
|
+
children: [
|
|
45
|
+
if (index > 0)
|
|
46
|
+
Padding(
|
|
47
|
+
padding: const EdgeInsets.symmetric(horizontal: 4),
|
|
48
|
+
child: Icon(
|
|
49
|
+
separator,
|
|
50
|
+
size: 16,
|
|
51
|
+
color: CronixColors.textMuted,
|
|
52
|
+
),
|
|
53
|
+
),
|
|
54
|
+
InkWell(
|
|
55
|
+
onTap: isLast ? null : item.onTap,
|
|
56
|
+
borderRadius: CronixRadius.radiusSM,
|
|
57
|
+
child: Row(
|
|
58
|
+
children: [
|
|
59
|
+
if (item.icon != null) ...[
|
|
60
|
+
Icon(
|
|
61
|
+
item.icon,
|
|
62
|
+
size: 16,
|
|
63
|
+
color: isLast
|
|
64
|
+
? CronixColors.text
|
|
65
|
+
: CronixColors.textSecondary,
|
|
66
|
+
),
|
|
67
|
+
const SizedBox(width: 6),
|
|
68
|
+
],
|
|
69
|
+
Text(
|
|
70
|
+
item.label,
|
|
71
|
+
style: TextStyle(
|
|
72
|
+
color: isLast
|
|
73
|
+
? (activeColor ?? CronixColors.text)
|
|
74
|
+
: (textColor ?? CronixColors.textSecondary),
|
|
75
|
+
fontSize: fontSize,
|
|
76
|
+
fontWeight: isLast ? FontWeight.w500 : FontWeight.w400,
|
|
77
|
+
),
|
|
78
|
+
),
|
|
79
|
+
],
|
|
80
|
+
),
|
|
81
|
+
),
|
|
82
|
+
],
|
|
83
|
+
);
|
|
84
|
+
}).toList(),
|
|
85
|
+
),
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import '../tokens/colors.dart';
|
|
3
|
+
import '../tokens/spacing.dart';
|
|
4
|
+
|
|
5
|
+
enum CnButtonVariant { primary, ghost, outline, danger, success }
|
|
6
|
+
|
|
7
|
+
class CnButton extends StatelessWidget {
|
|
8
|
+
final String? label;
|
|
9
|
+
final Widget? child;
|
|
10
|
+
final CnButtonVariant variant;
|
|
11
|
+
final VoidCallback? onPressed;
|
|
12
|
+
final bool disabled;
|
|
13
|
+
final bool loading;
|
|
14
|
+
final IconData? icon;
|
|
15
|
+
final IconData? trailingIcon;
|
|
16
|
+
final double? width;
|
|
17
|
+
final double height;
|
|
18
|
+
final EdgeInsetsGeometry? padding;
|
|
19
|
+
|
|
20
|
+
const CnButton({
|
|
21
|
+
super.key,
|
|
22
|
+
this.label,
|
|
23
|
+
this.child,
|
|
24
|
+
this.variant = CnButtonVariant.primary,
|
|
25
|
+
this.onPressed,
|
|
26
|
+
this.disabled = false,
|
|
27
|
+
this.loading = false,
|
|
28
|
+
this.icon,
|
|
29
|
+
this.trailingIcon,
|
|
30
|
+
this.width,
|
|
31
|
+
this.height = 40,
|
|
32
|
+
this.padding,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
@override
|
|
36
|
+
Widget build(BuildContext context) {
|
|
37
|
+
final effectiveOnPressed = (disabled || loading) ? null : onPressed;
|
|
38
|
+
|
|
39
|
+
Widget content;
|
|
40
|
+
if (loading) {
|
|
41
|
+
content = Row(
|
|
42
|
+
mainAxisSize: MainAxisSize.min,
|
|
43
|
+
children: [
|
|
44
|
+
SizedBox(
|
|
45
|
+
width: 16,
|
|
46
|
+
height: 16,
|
|
47
|
+
child: CircularProgressIndicator(
|
|
48
|
+
strokeWidth: 2,
|
|
49
|
+
valueColor: AlwaysStoppedAnimation<Color>(_getTextColor()),
|
|
50
|
+
),
|
|
51
|
+
),
|
|
52
|
+
if (label != null) ...[
|
|
53
|
+
const SizedBox(width: 8),
|
|
54
|
+
Text(label!, style: TextStyle(color: _getTextColor())),
|
|
55
|
+
],
|
|
56
|
+
],
|
|
57
|
+
);
|
|
58
|
+
} else if (child != null) {
|
|
59
|
+
content = child!;
|
|
60
|
+
} else {
|
|
61
|
+
content = Row(
|
|
62
|
+
mainAxisSize: MainAxisSize.min,
|
|
63
|
+
children: [
|
|
64
|
+
if (icon != null) ...[
|
|
65
|
+
Icon(icon, size: 18, color: _getTextColor()),
|
|
66
|
+
const SizedBox(width: 8),
|
|
67
|
+
],
|
|
68
|
+
if (label != null) Text(label!, style: TextStyle(color: _getTextColor())),
|
|
69
|
+
if (trailingIcon != null) ...[
|
|
70
|
+
const SizedBox(width: 8),
|
|
71
|
+
Icon(trailingIcon, size: 18, color: _getTextColor()),
|
|
72
|
+
],
|
|
73
|
+
],
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return SizedBox(
|
|
78
|
+
width: width,
|
|
79
|
+
height: height,
|
|
80
|
+
child: Material(
|
|
81
|
+
color: Colors.transparent,
|
|
82
|
+
child: InkWell(
|
|
83
|
+
onTap: effectiveOnPressed,
|
|
84
|
+
borderRadius: CronixRadius.radiusMD,
|
|
85
|
+
child: Container(
|
|
86
|
+
padding: padding ?? const EdgeInsets.symmetric(horizontal: 16),
|
|
87
|
+
decoration: BoxDecoration(
|
|
88
|
+
color: _getBackgroundColor(),
|
|
89
|
+
border: _getBorder(),
|
|
90
|
+
borderRadius: CronixRadius.radiusMD,
|
|
91
|
+
),
|
|
92
|
+
child: Center(child: content),
|
|
93
|
+
),
|
|
94
|
+
),
|
|
95
|
+
),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
Color _getBackgroundColor() {
|
|
100
|
+
if (disabled) return CronixColors.surfaceLight;
|
|
101
|
+
switch (variant) {
|
|
102
|
+
case CnButtonVariant.primary:
|
|
103
|
+
return CronixColors.accent;
|
|
104
|
+
case CnButtonVariant.ghost:
|
|
105
|
+
return Colors.transparent;
|
|
106
|
+
case CnButtonVariant.outline:
|
|
107
|
+
return Colors.transparent;
|
|
108
|
+
case CnButtonVariant.danger:
|
|
109
|
+
return CronixColors.error;
|
|
110
|
+
case CnButtonVariant.success:
|
|
111
|
+
return CronixColors.success;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
Color _getTextColor() {
|
|
116
|
+
if (disabled) return CronixColors.textMuted;
|
|
117
|
+
switch (variant) {
|
|
118
|
+
case CnButtonVariant.primary:
|
|
119
|
+
case CnButtonVariant.danger:
|
|
120
|
+
case CnButtonVariant.success:
|
|
121
|
+
return CronixColors.text;
|
|
122
|
+
case CnButtonVariant.ghost:
|
|
123
|
+
case CnButtonVariant.outline:
|
|
124
|
+
return CronixColors.text;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
BoxBorder? _getBorder() {
|
|
129
|
+
if (disabled) return null;
|
|
130
|
+
switch (variant) {
|
|
131
|
+
case CnButtonVariant.outline:
|
|
132
|
+
return Border.all(color: CronixColors.borderLight);
|
|
133
|
+
default:
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import '../tokens/colors.dart';
|
|
3
|
+
import '../tokens/spacing.dart';
|
|
4
|
+
|
|
5
|
+
class CnCard extends StatelessWidget {
|
|
6
|
+
final Widget? header;
|
|
7
|
+
final Widget? body;
|
|
8
|
+
final Widget? footer;
|
|
9
|
+
final EdgeInsetsGeometry? padding;
|
|
10
|
+
final EdgeInsetsGeometry? headerPadding;
|
|
11
|
+
final EdgeInsetsGeometry? footerPadding;
|
|
12
|
+
final Color? backgroundColor;
|
|
13
|
+
final double? width;
|
|
14
|
+
final double? elevation;
|
|
15
|
+
final VoidCallback? onTap;
|
|
16
|
+
final bool borderless;
|
|
17
|
+
|
|
18
|
+
const CnCard({
|
|
19
|
+
super.key,
|
|
20
|
+
this.header,
|
|
21
|
+
this.body,
|
|
22
|
+
this.footer,
|
|
23
|
+
this.padding,
|
|
24
|
+
this.headerPadding,
|
|
25
|
+
this.footerPadding,
|
|
26
|
+
this.backgroundColor,
|
|
27
|
+
this.width,
|
|
28
|
+
this.elevation,
|
|
29
|
+
this.onTap,
|
|
30
|
+
this.borderless = false,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
@override
|
|
34
|
+
Widget build(BuildContext context) {
|
|
35
|
+
Widget content = Column(
|
|
36
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
37
|
+
mainAxisSize: MainAxisSize.min,
|
|
38
|
+
children: [
|
|
39
|
+
if (header != null)
|
|
40
|
+
Container(
|
|
41
|
+
padding: headerPadding ?? const EdgeInsets.all(16),
|
|
42
|
+
decoration: const BoxDecoration(
|
|
43
|
+
border: Border(
|
|
44
|
+
bottom: BorderSide(color: CronixColors.border),
|
|
45
|
+
),
|
|
46
|
+
),
|
|
47
|
+
child: header,
|
|
48
|
+
),
|
|
49
|
+
if (body != null)
|
|
50
|
+
Flexible(
|
|
51
|
+
child: Padding(
|
|
52
|
+
padding: padding ?? const EdgeInsets.all(16),
|
|
53
|
+
child: body,
|
|
54
|
+
),
|
|
55
|
+
),
|
|
56
|
+
if (footer != null)
|
|
57
|
+
Container(
|
|
58
|
+
padding: footerPadding ?? const EdgeInsets.all(16),
|
|
59
|
+
decoration: const BoxDecoration(
|
|
60
|
+
border: Border(
|
|
61
|
+
top: BorderSide(color: CronixColors.border),
|
|
62
|
+
),
|
|
63
|
+
),
|
|
64
|
+
child: footer,
|
|
65
|
+
),
|
|
66
|
+
],
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (onTap != null) {
|
|
70
|
+
content = InkWell(
|
|
71
|
+
onTap: onTap,
|
|
72
|
+
borderRadius: CronixRadius.radiusLG,
|
|
73
|
+
child: content,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return Container(
|
|
78
|
+
width: width,
|
|
79
|
+
decoration: BoxDecoration(
|
|
80
|
+
color: backgroundColor ?? CronixColors.surface,
|
|
81
|
+
borderRadius: CronixRadius.radiusLG,
|
|
82
|
+
border: borderless ? null : Border.all(color: CronixColors.border),
|
|
83
|
+
boxShadow: elevation != null
|
|
84
|
+
? [
|
|
85
|
+
BoxShadow(
|
|
86
|
+
color: Colors.black.withOpacity(0.2),
|
|
87
|
+
blurRadius: elevation!,
|
|
88
|
+
offset: Offset(0, elevation! / 2),
|
|
89
|
+
),
|
|
90
|
+
]
|
|
91
|
+
: null,
|
|
92
|
+
),
|
|
93
|
+
child: ClipRRect(
|
|
94
|
+
borderRadius: CronixRadius.radiusLG,
|
|
95
|
+
child: content,
|
|
96
|
+
),
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import '../tokens/colors.dart';
|
|
3
|
+
import '../tokens/spacing.dart';
|
|
4
|
+
|
|
5
|
+
class CnCheckbox extends StatelessWidget {
|
|
6
|
+
final bool value;
|
|
7
|
+
final ValueChanged<bool?>? onChanged;
|
|
8
|
+
final String? label;
|
|
9
|
+
final bool enabled;
|
|
10
|
+
final Color? activeColor;
|
|
11
|
+
|
|
12
|
+
const CnCheckbox({
|
|
13
|
+
super.key,
|
|
14
|
+
required this.value,
|
|
15
|
+
this.onChanged,
|
|
16
|
+
this.label,
|
|
17
|
+
this.enabled = true,
|
|
18
|
+
this.activeColor,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
@override
|
|
22
|
+
Widget build(BuildContext context) {
|
|
23
|
+
final checkbox = GestureDetector(
|
|
24
|
+
onTap: enabled && onChanged != null
|
|
25
|
+
? () => onChanged!(!value)
|
|
26
|
+
: null,
|
|
27
|
+
child: Container(
|
|
28
|
+
width: 20,
|
|
29
|
+
height: 20,
|
|
30
|
+
decoration: BoxDecoration(
|
|
31
|
+
color: value
|
|
32
|
+
? (activeColor ?? CronixColors.accent)
|
|
33
|
+
: Colors.transparent,
|
|
34
|
+
borderRadius: CronixRadius.radiusSM,
|
|
35
|
+
border: Border.all(
|
|
36
|
+
color: value
|
|
37
|
+
? (activeColor ?? CronixColors.accent)
|
|
38
|
+
: CronixColors.borderLight,
|
|
39
|
+
width: 2,
|
|
40
|
+
),
|
|
41
|
+
),
|
|
42
|
+
child: value
|
|
43
|
+
? const Icon(
|
|
44
|
+
Icons.check,
|
|
45
|
+
size: 14,
|
|
46
|
+
color: CronixColors.text,
|
|
47
|
+
)
|
|
48
|
+
: null,
|
|
49
|
+
),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
if (label != null) {
|
|
53
|
+
return InkWell(
|
|
54
|
+
onTap: enabled && onChanged != null
|
|
55
|
+
? () => onChanged!(!value)
|
|
56
|
+
: null,
|
|
57
|
+
borderRadius: CronixRadius.radiusSM,
|
|
58
|
+
child: Row(
|
|
59
|
+
mainAxisSize: MainAxisSize.min,
|
|
60
|
+
children: [
|
|
61
|
+
checkbox,
|
|
62
|
+
const SizedBox(width: 8),
|
|
63
|
+
Text(
|
|
64
|
+
label!,
|
|
65
|
+
style: TextStyle(
|
|
66
|
+
color: enabled ? CronixColors.text : CronixColors.textMuted,
|
|
67
|
+
fontSize: 14,
|
|
68
|
+
),
|
|
69
|
+
),
|
|
70
|
+
],
|
|
71
|
+
),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return checkbox;
|
|
76
|
+
}
|
|
77
|
+
}
|