getlotui 0.1.0
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 +78 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +5 -0
- package/dist/commands/add.d.ts +1 -0
- package/dist/commands/add.js +37 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +93 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +22 -0
- package/dist/templates/expo/Accordion.d.ts +14 -0
- package/dist/templates/expo/Accordion.js +118 -0
- package/dist/templates/expo/Accordion.tsx +152 -0
- package/dist/templates/expo/AlertDialog.d.ts +12 -0
- package/dist/templates/expo/AlertDialog.js +126 -0
- package/dist/templates/expo/AlertDialog.tsx +147 -0
- package/dist/templates/expo/Avatar.d.ts +8 -0
- package/dist/templates/expo/Avatar.js +81 -0
- package/dist/templates/expo/Avatar.tsx +78 -0
- package/dist/templates/expo/Badge.d.ts +6 -0
- package/dist/templates/expo/Badge.js +60 -0
- package/dist/templates/expo/Badge.tsx +67 -0
- package/dist/templates/expo/Button.d.ts +9 -0
- package/dist/templates/expo/Button.js +37 -0
- package/dist/templates/expo/Button.tsx +53 -0
- package/dist/templates/expo/Dropdown.d.ts +12 -0
- package/dist/templates/expo/Dropdown.js +91 -0
- package/dist/templates/expo/Dropdown.tsx +100 -0
- package/dist/templates/expo/Input.d.ts +11 -0
- package/dist/templates/expo/Input.js +43 -0
- package/dist/templates/expo/Input.tsx +67 -0
- package/dist/templates/expo/Toast.d.ts +16 -0
- package/dist/templates/expo/Toast.js +142 -0
- package/dist/templates/expo/Toast.tsx +161 -0
- package/dist/templates/expo/utils.d.ts +4 -0
- package/dist/templates/expo/utils.js +10 -0
- package/dist/templates/expo/utils.ts +8 -0
- package/dist/templates/flutter/Accordion.dart +142 -0
- package/dist/templates/flutter/Alert.dart +96 -0
- package/dist/templates/flutter/AlertDialog.dart +175 -0
- package/dist/templates/flutter/Avatar.dart +82 -0
- package/dist/templates/flutter/Badge.dart +89 -0
- package/dist/templates/flutter/Button.dart +116 -0
- package/dist/templates/flutter/Card.dart +91 -0
- package/dist/templates/flutter/Input.dart +73 -0
- package/dist/templates/flutter/Text.dart +87 -0
- package/dist/templates/flutter/utils.dart +13 -0
- package/dist/templates/templates/expo/Button.tsx +50 -0
- package/dist/templates/templates/expo/Input.tsx +67 -0
- package/dist/templates/web/Accordion.d.ts +7 -0
- package/dist/templates/web/Accordion.js +59 -0
- package/dist/templates/web/Accordion.tsx +64 -0
- package/dist/templates/web/Alert.d.ts +9 -0
- package/dist/templates/web/Alert.js +64 -0
- package/dist/templates/web/Alert.tsx +71 -0
- package/dist/templates/web/AlertDialog.d.ts +14 -0
- package/dist/templates/web/AlertDialog.js +85 -0
- package/dist/templates/web/AlertDialog.tsx +164 -0
- package/dist/templates/web/Avatar.d.ts +6 -0
- package/dist/templates/web/Avatar.js +50 -0
- package/dist/templates/web/Avatar.tsx +51 -0
- package/dist/templates/web/Badge.d.ts +9 -0
- package/dist/templates/web/Badge.js +59 -0
- package/dist/templates/web/Badge.tsx +38 -0
- package/dist/templates/web/Button.d.ts +10 -0
- package/dist/templates/web/Button.js +70 -0
- package/dist/templates/web/Button.tsx +60 -0
- package/dist/templates/web/Card.d.ts +9 -0
- package/dist/templates/web/Card.js +65 -0
- package/dist/templates/web/Card.tsx +92 -0
- package/dist/templates/web/Dropdown.d.ts +27 -0
- package/dist/templates/web/Dropdown.js +95 -0
- package/dist/templates/web/Dropdown.tsx +198 -0
- package/dist/templates/web/Input.d.ts +3 -0
- package/dist/templates/web/Input.js +41 -0
- package/dist/templates/web/Input.tsx +21 -0
- package/dist/templates/web/Tabs.d.ts +7 -0
- package/dist/templates/web/Tabs.js +55 -0
- package/dist/templates/web/Tabs.tsx +66 -0
- package/dist/templates/web/Toast.d.ts +15 -0
- package/dist/templates/web/Toast.js +75 -0
- package/dist/templates/web/Toast.tsx +126 -0
- package/dist/templates/web/utils.d.ts +2 -0
- package/dist/templates/web/utils.js +8 -0
- package/dist/templates/web/utils.ts +6 -0
- package/dist/utils/detect.d.ts +19 -0
- package/dist/utils/detect.js +90 -0
- package/dist/utils/fs.d.ts +5 -0
- package/dist/utils/fs.js +35 -0
- package/getlotui.config.json +4 -0
- package/package.json +31 -0
- package/src/bin.ts +5 -0
- package/src/commands/add.ts +50 -0
- package/src/commands/init.ts +108 -0
- package/src/index.ts +23 -0
- package/src/templates/expo/Accordion.tsx +152 -0
- package/src/templates/expo/AlertDialog.tsx +147 -0
- package/src/templates/expo/Avatar.tsx +78 -0
- package/src/templates/expo/Badge.tsx +67 -0
- package/src/templates/expo/Button.tsx +53 -0
- package/src/templates/expo/Dropdown.tsx +100 -0
- package/src/templates/expo/Input.tsx +67 -0
- package/src/templates/expo/Toast.tsx +161 -0
- package/src/templates/expo/utils.ts +8 -0
- package/src/templates/flutter/Accordion.dart +142 -0
- package/src/templates/flutter/Alert.dart +96 -0
- package/src/templates/flutter/AlertDialog.dart +175 -0
- package/src/templates/flutter/Avatar.dart +82 -0
- package/src/templates/flutter/Badge.dart +89 -0
- package/src/templates/flutter/Button.dart +116 -0
- package/src/templates/flutter/Card.dart +91 -0
- package/src/templates/flutter/Input.dart +73 -0
- package/src/templates/flutter/Text.dart +87 -0
- package/src/templates/flutter/utils.dart +13 -0
- package/src/templates/web/Accordion.tsx +64 -0
- package/src/templates/web/Alert.tsx +71 -0
- package/src/templates/web/AlertDialog.tsx +164 -0
- package/src/templates/web/Avatar.tsx +51 -0
- package/src/templates/web/Badge.tsx +38 -0
- package/src/templates/web/Button.tsx +60 -0
- package/src/templates/web/Card.tsx +92 -0
- package/src/templates/web/Dropdown.tsx +198 -0
- package/src/templates/web/Input.tsx +21 -0
- package/src/templates/web/Tabs.tsx +66 -0
- package/src/templates/web/Toast.tsx +126 -0
- package/src/templates/web/utils.ts +6 -0
- package/src/utils/detect.ts +81 -0
- package/src/utils/fs.ts +32 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
|
|
3
|
+
enum CrossUIAccordionType { single, multiple }
|
|
4
|
+
|
|
5
|
+
class CrossUIAccordionItem {
|
|
6
|
+
final String value;
|
|
7
|
+
final String trigger;
|
|
8
|
+
final String content;
|
|
9
|
+
|
|
10
|
+
const CrossUIAccordionItem({
|
|
11
|
+
required this.value,
|
|
12
|
+
required this.trigger,
|
|
13
|
+
required this.content,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class CrossUIAccordion extends StatefulWidget {
|
|
18
|
+
final List<CrossUIAccordionItem> items;
|
|
19
|
+
final CrossUIAccordionType type;
|
|
20
|
+
final bool collapsible;
|
|
21
|
+
final dynamic defaultValue;
|
|
22
|
+
|
|
23
|
+
const CrossUIAccordion({
|
|
24
|
+
super.key,
|
|
25
|
+
required this.items,
|
|
26
|
+
this.type = CrossUIAccordionType.single,
|
|
27
|
+
this.collapsible = false,
|
|
28
|
+
this.defaultValue,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
@override
|
|
32
|
+
State<CrossUIAccordion> createState() => _CrossUIAccordionState();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class _CrossUIAccordionState extends State<CrossUIAccordion> {
|
|
36
|
+
late Set<String> _openItems;
|
|
37
|
+
|
|
38
|
+
@override
|
|
39
|
+
void initState() {
|
|
40
|
+
super.initState();
|
|
41
|
+
_openItems = {};
|
|
42
|
+
|
|
43
|
+
if (widget.defaultValue != null) {
|
|
44
|
+
if (widget.defaultValue is String) {
|
|
45
|
+
_openItems.add(widget.defaultValue as String);
|
|
46
|
+
} else if (widget.defaultValue is List) {
|
|
47
|
+
_openItems.addAll((widget.defaultValue as List).cast<String>());
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
void _toggleItem(String value) {
|
|
53
|
+
setState(() {
|
|
54
|
+
if (widget.type == CrossUIAccordionType.single) {
|
|
55
|
+
if (_openItems.contains(value)) {
|
|
56
|
+
if (widget.collapsible) {
|
|
57
|
+
_openItems.clear();
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
_openItems.clear();
|
|
61
|
+
_openItems.add(value);
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
if (_openItems.contains(value)) {
|
|
65
|
+
_openItems.remove(value);
|
|
66
|
+
} else {
|
|
67
|
+
_openItems.add(value);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@override
|
|
74
|
+
Widget build(BuildContext context) {
|
|
75
|
+
return Column(
|
|
76
|
+
children: widget.items.asMap().entries.map((entry) {
|
|
77
|
+
final index = entry.key;
|
|
78
|
+
final item = entry.value;
|
|
79
|
+
final isOpen = _openItems.contains(item.value);
|
|
80
|
+
final isLast = index == widget.items.length - 1;
|
|
81
|
+
|
|
82
|
+
return Container(
|
|
83
|
+
decoration: BoxDecoration(
|
|
84
|
+
border: Border(
|
|
85
|
+
bottom: isLast
|
|
86
|
+
? BorderSide.none
|
|
87
|
+
: const BorderSide(color: Colors.grey, width: 1),
|
|
88
|
+
),
|
|
89
|
+
),
|
|
90
|
+
child: Column(
|
|
91
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
92
|
+
children: [
|
|
93
|
+
InkWell(
|
|
94
|
+
onTap: () => _toggleItem(item.value),
|
|
95
|
+
child: Padding(
|
|
96
|
+
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
97
|
+
child: Row(
|
|
98
|
+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
99
|
+
children: [
|
|
100
|
+
Expanded(
|
|
101
|
+
child: Text(
|
|
102
|
+
item.trigger,
|
|
103
|
+
style: const TextStyle(
|
|
104
|
+
fontSize: 16,
|
|
105
|
+
fontWeight: FontWeight.w500,
|
|
106
|
+
),
|
|
107
|
+
),
|
|
108
|
+
),
|
|
109
|
+
AnimatedRotation(
|
|
110
|
+
turns: isOpen ? 0.75 : 0.25,
|
|
111
|
+
duration: const Duration(milliseconds: 200),
|
|
112
|
+
child: const Icon(Icons.chevron_right, size: 20),
|
|
113
|
+
),
|
|
114
|
+
],
|
|
115
|
+
),
|
|
116
|
+
),
|
|
117
|
+
),
|
|
118
|
+
AnimatedCrossFade(
|
|
119
|
+
firstChild: const SizedBox.shrink(),
|
|
120
|
+
secondChild: Padding(
|
|
121
|
+
padding: const EdgeInsets.only(bottom: 16),
|
|
122
|
+
child: Text(
|
|
123
|
+
item.content,
|
|
124
|
+
style: TextStyle(
|
|
125
|
+
fontSize: 14,
|
|
126
|
+
color: Colors.grey[600],
|
|
127
|
+
height: 1.5,
|
|
128
|
+
),
|
|
129
|
+
),
|
|
130
|
+
),
|
|
131
|
+
crossFadeState: isOpen
|
|
132
|
+
? CrossFadeState.showSecond
|
|
133
|
+
: CrossFadeState.showFirst,
|
|
134
|
+
duration: const Duration(milliseconds: 200),
|
|
135
|
+
),
|
|
136
|
+
],
|
|
137
|
+
),
|
|
138
|
+
);
|
|
139
|
+
}).toList(),
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import '../theme/tokens.dart';
|
|
3
|
+
|
|
4
|
+
enum CrossUIAlertVariant { info, success, warning, destructive }
|
|
5
|
+
|
|
6
|
+
class CrossUIAlert extends StatelessWidget {
|
|
7
|
+
final String title;
|
|
8
|
+
final String? description;
|
|
9
|
+
final CrossUIAlertVariant variant;
|
|
10
|
+
final IconData? icon;
|
|
11
|
+
|
|
12
|
+
const CrossUIAlert({
|
|
13
|
+
Key? key,
|
|
14
|
+
required this.title,
|
|
15
|
+
this.description,
|
|
16
|
+
this.variant = CrossUIAlertVariant.info,
|
|
17
|
+
this.icon,
|
|
18
|
+
}) : super(key: key);
|
|
19
|
+
|
|
20
|
+
@override
|
|
21
|
+
Widget build(BuildContext context) {
|
|
22
|
+
Color borderColor;
|
|
23
|
+
Color backgroundColor;
|
|
24
|
+
Color iconColor;
|
|
25
|
+
IconData defaultIcon;
|
|
26
|
+
|
|
27
|
+
switch (variant) {
|
|
28
|
+
case CrossUIAlertVariant.success:
|
|
29
|
+
borderColor = CrossUITokens.success;
|
|
30
|
+
backgroundColor = CrossUITokens.successLight;
|
|
31
|
+
iconColor = CrossUITokens.onSuccessLight;
|
|
32
|
+
defaultIcon = Icons.check_circle_outline;
|
|
33
|
+
break;
|
|
34
|
+
case CrossUIAlertVariant.warning:
|
|
35
|
+
borderColor = CrossUITokens.warning;
|
|
36
|
+
backgroundColor = CrossUITokens.warningLight;
|
|
37
|
+
iconColor = CrossUITokens.onWarningLight;
|
|
38
|
+
defaultIcon = Icons.warning_amber_rounded;
|
|
39
|
+
break;
|
|
40
|
+
case CrossUIAlertVariant.destructive:
|
|
41
|
+
borderColor = CrossUITokens.danger;
|
|
42
|
+
backgroundColor = CrossUITokens.dangerLight;
|
|
43
|
+
iconColor = CrossUITokens.onDangerLight;
|
|
44
|
+
defaultIcon = Icons.error_outline;
|
|
45
|
+
break;
|
|
46
|
+
case CrossUIAlertVariant.info:
|
|
47
|
+
default:
|
|
48
|
+
borderColor = CrossUITokens.info;
|
|
49
|
+
backgroundColor = CrossUITokens.infoLight;
|
|
50
|
+
iconColor = CrossUITokens.onInfoLight;
|
|
51
|
+
defaultIcon = Icons.info_outline;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return Container(
|
|
56
|
+
padding: const EdgeInsets.all(CrossUITokens.spacingM),
|
|
57
|
+
decoration: BoxDecoration(
|
|
58
|
+
color: backgroundColor,
|
|
59
|
+
borderRadius: BorderRadius.circular(CrossUITokens.radiusMedium),
|
|
60
|
+
border: Border.all(color: borderColor.withOpacity(0.5)),
|
|
61
|
+
),
|
|
62
|
+
child: Row(
|
|
63
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
64
|
+
children: [
|
|
65
|
+
Icon(icon ?? defaultIcon, color: iconColor, size: 20),
|
|
66
|
+
const SizedBox(width: CrossUITokens.spacingSm),
|
|
67
|
+
Expanded(
|
|
68
|
+
child: Column(
|
|
69
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
70
|
+
children: [
|
|
71
|
+
Text(
|
|
72
|
+
title,
|
|
73
|
+
style: TextStyle(
|
|
74
|
+
fontWeight: FontWeight.bold,
|
|
75
|
+
fontSize: CrossUITokens.fontSizeBase,
|
|
76
|
+
color: iconColor,
|
|
77
|
+
),
|
|
78
|
+
),
|
|
79
|
+
if (description != null) ...[
|
|
80
|
+
const SizedBox(height: 4),
|
|
81
|
+
Text(
|
|
82
|
+
description!,
|
|
83
|
+
style: TextStyle(
|
|
84
|
+
fontSize: CrossUITokens.fontSizeSm,
|
|
85
|
+
color: iconColor.withOpacity(0.8),
|
|
86
|
+
),
|
|
87
|
+
),
|
|
88
|
+
],
|
|
89
|
+
],
|
|
90
|
+
),
|
|
91
|
+
),
|
|
92
|
+
],
|
|
93
|
+
),
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
|
|
3
|
+
enum CrossUIAlertDialogVariant { defaultVariant, destructive }
|
|
4
|
+
|
|
5
|
+
class CrossUIAlertDialog extends StatelessWidget {
|
|
6
|
+
final String title;
|
|
7
|
+
final String? description;
|
|
8
|
+
final String cancelText;
|
|
9
|
+
final String actionText;
|
|
10
|
+
final VoidCallback? onCancel;
|
|
11
|
+
final VoidCallback? onAction;
|
|
12
|
+
final CrossUIAlertDialogVariant variant;
|
|
13
|
+
final Widget? content;
|
|
14
|
+
|
|
15
|
+
const CrossUIAlertDialog({
|
|
16
|
+
super.key,
|
|
17
|
+
required this.title,
|
|
18
|
+
this.description,
|
|
19
|
+
this.cancelText = 'Cancel',
|
|
20
|
+
this.actionText = 'Continue',
|
|
21
|
+
this.onCancel,
|
|
22
|
+
this.onAction,
|
|
23
|
+
this.variant = CrossUIAlertDialogVariant.defaultVariant,
|
|
24
|
+
this.content,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
static Future<T?> show<T>({
|
|
28
|
+
required BuildContext context,
|
|
29
|
+
required String title,
|
|
30
|
+
String? description,
|
|
31
|
+
String cancelText = 'Cancel',
|
|
32
|
+
String actionText = 'Continue',
|
|
33
|
+
VoidCallback? onCancel,
|
|
34
|
+
VoidCallback? onAction,
|
|
35
|
+
CrossUIAlertDialogVariant variant =
|
|
36
|
+
CrossUIAlertDialogVariant.defaultVariant,
|
|
37
|
+
Widget? content,
|
|
38
|
+
}) {
|
|
39
|
+
return showDialog<T>(
|
|
40
|
+
context: context,
|
|
41
|
+
barrierDismissible: true,
|
|
42
|
+
builder: (BuildContext context) {
|
|
43
|
+
return CrossUIAlertDialog(
|
|
44
|
+
title: title,
|
|
45
|
+
description: description,
|
|
46
|
+
cancelText: cancelText,
|
|
47
|
+
actionText: actionText,
|
|
48
|
+
onCancel: onCancel,
|
|
49
|
+
onAction: onAction,
|
|
50
|
+
variant: variant,
|
|
51
|
+
content: content,
|
|
52
|
+
);
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@override
|
|
58
|
+
Widget build(BuildContext context) {
|
|
59
|
+
Color actionBackgroundColor;
|
|
60
|
+
Color actionTextColor;
|
|
61
|
+
|
|
62
|
+
if (variant == CrossUIAlertDialogVariant.destructive) {
|
|
63
|
+
actionBackgroundColor = Colors.red;
|
|
64
|
+
actionTextColor = Colors.white;
|
|
65
|
+
} else {
|
|
66
|
+
actionBackgroundColor = Colors.blue;
|
|
67
|
+
actionTextColor = Colors.white;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return Dialog(
|
|
71
|
+
shape: RoundedRectangleBorder(
|
|
72
|
+
borderRadius: BorderRadius.circular(16),
|
|
73
|
+
side: BorderSide(color: Colors.grey.withValues(alpha: 0.2), width: 1),
|
|
74
|
+
),
|
|
75
|
+
child: Container(
|
|
76
|
+
constraints: const BoxConstraints(maxWidth: 400),
|
|
77
|
+
padding: const EdgeInsets.all(24),
|
|
78
|
+
child: Column(
|
|
79
|
+
mainAxisSize: MainAxisSize.min,
|
|
80
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
81
|
+
children: [
|
|
82
|
+
Column(
|
|
83
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
84
|
+
children: [
|
|
85
|
+
Text(
|
|
86
|
+
title,
|
|
87
|
+
style: const TextStyle(
|
|
88
|
+
fontSize: 18,
|
|
89
|
+
fontWeight: FontWeight.w700,
|
|
90
|
+
),
|
|
91
|
+
),
|
|
92
|
+
if (description != null) ...[
|
|
93
|
+
const SizedBox(height: 8),
|
|
94
|
+
Text(
|
|
95
|
+
description!,
|
|
96
|
+
style: TextStyle(
|
|
97
|
+
fontSize: 14,
|
|
98
|
+
color: Colors.grey[600],
|
|
99
|
+
height: 1.5,
|
|
100
|
+
),
|
|
101
|
+
),
|
|
102
|
+
],
|
|
103
|
+
],
|
|
104
|
+
),
|
|
105
|
+
if (content != null) ...[const SizedBox(height: 16), content!],
|
|
106
|
+
const SizedBox(height: 24),
|
|
107
|
+
Row(
|
|
108
|
+
mainAxisAlignment: MainAxisAlignment.end,
|
|
109
|
+
children: [
|
|
110
|
+
Expanded(
|
|
111
|
+
child: InkWell(
|
|
112
|
+
onTap: () {
|
|
113
|
+
onCancel?.call();
|
|
114
|
+
Navigator.of(context).pop(false);
|
|
115
|
+
},
|
|
116
|
+
borderRadius: BorderRadius.circular(8),
|
|
117
|
+
child: Container(
|
|
118
|
+
padding: const EdgeInsets.symmetric(
|
|
119
|
+
horizontal: 16,
|
|
120
|
+
vertical: 12,
|
|
121
|
+
),
|
|
122
|
+
decoration: BoxDecoration(
|
|
123
|
+
border: Border.all(
|
|
124
|
+
color: Colors.grey.withValues(alpha: 0.3),
|
|
125
|
+
),
|
|
126
|
+
borderRadius: BorderRadius.circular(8),
|
|
127
|
+
),
|
|
128
|
+
child: const Text(
|
|
129
|
+
'Cancel',
|
|
130
|
+
textAlign: TextAlign.center,
|
|
131
|
+
style: TextStyle(
|
|
132
|
+
fontSize: 16,
|
|
133
|
+
fontWeight: FontWeight.w600,
|
|
134
|
+
),
|
|
135
|
+
),
|
|
136
|
+
),
|
|
137
|
+
),
|
|
138
|
+
),
|
|
139
|
+
const SizedBox(width: 8),
|
|
140
|
+
Expanded(
|
|
141
|
+
child: InkWell(
|
|
142
|
+
onTap: () {
|
|
143
|
+
onAction?.call();
|
|
144
|
+
Navigator.of(context).pop(true);
|
|
145
|
+
},
|
|
146
|
+
borderRadius: BorderRadius.circular(8),
|
|
147
|
+
child: Container(
|
|
148
|
+
padding: const EdgeInsets.symmetric(
|
|
149
|
+
horizontal: 16,
|
|
150
|
+
vertical: 12,
|
|
151
|
+
),
|
|
152
|
+
decoration: BoxDecoration(
|
|
153
|
+
color: actionBackgroundColor,
|
|
154
|
+
borderRadius: BorderRadius.circular(8),
|
|
155
|
+
),
|
|
156
|
+
child: Text(
|
|
157
|
+
actionText,
|
|
158
|
+
textAlign: TextAlign.center,
|
|
159
|
+
style: TextStyle(
|
|
160
|
+
fontSize: 16,
|
|
161
|
+
fontWeight: FontWeight.w600,
|
|
162
|
+
color: actionTextColor,
|
|
163
|
+
),
|
|
164
|
+
),
|
|
165
|
+
),
|
|
166
|
+
),
|
|
167
|
+
),
|
|
168
|
+
],
|
|
169
|
+
),
|
|
170
|
+
],
|
|
171
|
+
),
|
|
172
|
+
),
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
|
|
3
|
+
enum CrossUIAvatarSize { xs, sm, md, lg, xl, xxl }
|
|
4
|
+
|
|
5
|
+
class CrossUIAvatar extends StatelessWidget {
|
|
6
|
+
final String? src;
|
|
7
|
+
final String? fallback;
|
|
8
|
+
final CrossUIAvatarSize size;
|
|
9
|
+
|
|
10
|
+
const CrossUIAvatar({
|
|
11
|
+
super.key,
|
|
12
|
+
this.src,
|
|
13
|
+
this.fallback,
|
|
14
|
+
this.size = CrossUIAvatarSize.md,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
@override
|
|
18
|
+
Widget build(BuildContext context) {
|
|
19
|
+
double dimension;
|
|
20
|
+
double fontSize;
|
|
21
|
+
|
|
22
|
+
switch (size) {
|
|
23
|
+
case CrossUIAvatarSize.xs:
|
|
24
|
+
dimension = 24.0;
|
|
25
|
+
fontSize = 10.0;
|
|
26
|
+
break;
|
|
27
|
+
case CrossUIAvatarSize.sm:
|
|
28
|
+
dimension = 32.0;
|
|
29
|
+
fontSize = 12.0;
|
|
30
|
+
break;
|
|
31
|
+
case CrossUIAvatarSize.lg:
|
|
32
|
+
dimension = 48.0;
|
|
33
|
+
fontSize = 16.0;
|
|
34
|
+
break;
|
|
35
|
+
case CrossUIAvatarSize.xl:
|
|
36
|
+
dimension = 64.0;
|
|
37
|
+
fontSize = 20.0;
|
|
38
|
+
break;
|
|
39
|
+
case CrossUIAvatarSize.xxl:
|
|
40
|
+
dimension = 80.0;
|
|
41
|
+
fontSize = 24.0;
|
|
42
|
+
break;
|
|
43
|
+
case CrossUIAvatarSize.md:
|
|
44
|
+
dimension = 40.0;
|
|
45
|
+
fontSize = 14.0;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return Container(
|
|
50
|
+
width: dimension,
|
|
51
|
+
height: dimension,
|
|
52
|
+
decoration: BoxDecoration(
|
|
53
|
+
color: Colors.blue.withValues(alpha: 0.1),
|
|
54
|
+
shape: BoxShape.circle,
|
|
55
|
+
border: Border.all(color: Colors.grey.withValues(alpha: 0.2), width: 1),
|
|
56
|
+
),
|
|
57
|
+
clipBehavior: Clip.antiAlias,
|
|
58
|
+
child: src != null
|
|
59
|
+
? Image.network(
|
|
60
|
+
src!,
|
|
61
|
+
fit: BoxFit.cover,
|
|
62
|
+
errorBuilder: (context, error, stackTrace) =>
|
|
63
|
+
_buildFallback(fontSize),
|
|
64
|
+
)
|
|
65
|
+
: _buildFallback(fontSize),
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
Widget _buildFallback(double fontSize) {
|
|
70
|
+
return Center(
|
|
71
|
+
child: Text(
|
|
72
|
+
fallback?.substring(0, fallback!.length >= 2 ? 2 : 1).toUpperCase() ??
|
|
73
|
+
"??",
|
|
74
|
+
style: TextStyle(
|
|
75
|
+
color: Colors.blue,
|
|
76
|
+
fontSize: fontSize,
|
|
77
|
+
fontWeight: FontWeight.w600,
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
|
|
3
|
+
enum CrossUIBadgeVariant {
|
|
4
|
+
defaultVariant,
|
|
5
|
+
secondary,
|
|
6
|
+
outline,
|
|
7
|
+
destructive,
|
|
8
|
+
success,
|
|
9
|
+
warning,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
enum CrossUIBadgeSize { sm, md }
|
|
13
|
+
|
|
14
|
+
class CrossUIBadge extends StatelessWidget {
|
|
15
|
+
final String label;
|
|
16
|
+
final CrossUIBadgeVariant variant;
|
|
17
|
+
final CrossUIBadgeSize size;
|
|
18
|
+
|
|
19
|
+
const CrossUIBadge({
|
|
20
|
+
super.key,
|
|
21
|
+
required this.label,
|
|
22
|
+
this.variant = CrossUIBadgeVariant.defaultVariant,
|
|
23
|
+
this.size = CrossUIBadgeSize.md,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
@override
|
|
27
|
+
Widget build(BuildContext context) {
|
|
28
|
+
Color backgroundColor;
|
|
29
|
+
Color textColor;
|
|
30
|
+
BorderSide borderSide = BorderSide.none;
|
|
31
|
+
|
|
32
|
+
switch (variant) {
|
|
33
|
+
case CrossUIBadgeVariant.secondary:
|
|
34
|
+
backgroundColor = Colors.blue.withValues(alpha: 0.1);
|
|
35
|
+
textColor = Colors.blue;
|
|
36
|
+
break;
|
|
37
|
+
case CrossUIBadgeVariant.outline:
|
|
38
|
+
backgroundColor = Colors.transparent;
|
|
39
|
+
textColor = Colors.blue;
|
|
40
|
+
borderSide = const BorderSide(color: Colors.blue);
|
|
41
|
+
break;
|
|
42
|
+
case CrossUIBadgeVariant.destructive:
|
|
43
|
+
backgroundColor = Colors.red;
|
|
44
|
+
textColor = Colors.white;
|
|
45
|
+
break;
|
|
46
|
+
case CrossUIBadgeVariant.success:
|
|
47
|
+
backgroundColor = Colors.green.withValues(alpha: 0.1);
|
|
48
|
+
textColor = Colors.green;
|
|
49
|
+
break;
|
|
50
|
+
case CrossUIBadgeVariant.warning:
|
|
51
|
+
backgroundColor = Colors.amber.withValues(alpha: 0.1);
|
|
52
|
+
textColor = Colors.amber;
|
|
53
|
+
break;
|
|
54
|
+
case CrossUIBadgeVariant.defaultVariant:
|
|
55
|
+
backgroundColor = Colors.blue;
|
|
56
|
+
textColor = Colors.white;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
EdgeInsets padding;
|
|
61
|
+
double fontSize;
|
|
62
|
+
|
|
63
|
+
if (size == CrossUIBadgeSize.sm) {
|
|
64
|
+
padding = const EdgeInsets.symmetric(horizontal: 6, vertical: 2);
|
|
65
|
+
fontSize = 10;
|
|
66
|
+
} else {
|
|
67
|
+
padding = const EdgeInsets.symmetric(horizontal: 10, vertical: 4);
|
|
68
|
+
fontSize = 12;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return Container(
|
|
72
|
+
padding: padding,
|
|
73
|
+
decoration: BoxDecoration(
|
|
74
|
+
color: backgroundColor,
|
|
75
|
+
borderRadius: BorderRadius.circular(999),
|
|
76
|
+
border: Border.fromBorderSide(borderSide),
|
|
77
|
+
),
|
|
78
|
+
child: Text(
|
|
79
|
+
label.toUpperCase(),
|
|
80
|
+
style: TextStyle(
|
|
81
|
+
color: textColor,
|
|
82
|
+
fontSize: fontSize,
|
|
83
|
+
fontWeight: FontWeight.w700,
|
|
84
|
+
letterSpacing: 0.5,
|
|
85
|
+
),
|
|
86
|
+
),
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import '../theme/config.dart';
|
|
3
|
+
|
|
4
|
+
enum CrossUIButtonVariant {
|
|
5
|
+
defaultVariant,
|
|
6
|
+
secondary,
|
|
7
|
+
outline,
|
|
8
|
+
ghost,
|
|
9
|
+
destructive,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
enum CrossUIButtonSize { sm, defaultSize, lg }
|
|
13
|
+
|
|
14
|
+
class CrossUIButton extends StatelessWidget {
|
|
15
|
+
final String label;
|
|
16
|
+
final VoidCallback? onPressed;
|
|
17
|
+
final CrossUIButtonVariant variant;
|
|
18
|
+
final CrossUIButtonSize size;
|
|
19
|
+
final bool disabled;
|
|
20
|
+
final Widget? icon;
|
|
21
|
+
|
|
22
|
+
const CrossUIButton({
|
|
23
|
+
Key? key,
|
|
24
|
+
required this.label,
|
|
25
|
+
this.onPressed,
|
|
26
|
+
this.variant = CrossUIButtonVariant.defaultVariant,
|
|
27
|
+
this.size = CrossUIButtonSize.defaultSize,
|
|
28
|
+
this.disabled = false,
|
|
29
|
+
this.icon,
|
|
30
|
+
}) : super(key: key);
|
|
31
|
+
|
|
32
|
+
@override
|
|
33
|
+
Widget build(BuildContext context) {
|
|
34
|
+
Color backgroundColor;
|
|
35
|
+
Color foregroundColor;
|
|
36
|
+
BorderSide borderSide = BorderSide.none;
|
|
37
|
+
|
|
38
|
+
final themeColors = CrossUITheme.colors;
|
|
39
|
+
|
|
40
|
+
switch (variant) {
|
|
41
|
+
case CrossUIButtonVariant.secondary:
|
|
42
|
+
backgroundColor = themeColors['secondary'] ?? Colors.grey;
|
|
43
|
+
foregroundColor = Colors.white;
|
|
44
|
+
break;
|
|
45
|
+
case CrossUIButtonVariant.outline:
|
|
46
|
+
backgroundColor = Colors.transparent;
|
|
47
|
+
foregroundColor = themeColors['primary'] ?? Colors.blue;
|
|
48
|
+
borderSide = BorderSide(color: themeColors['primary'] ?? Colors.blue);
|
|
49
|
+
break;
|
|
50
|
+
case CrossUIButtonVariant.ghost:
|
|
51
|
+
backgroundColor = Colors.transparent;
|
|
52
|
+
foregroundColor = themeColors['primary'] ?? Colors.blue;
|
|
53
|
+
break;
|
|
54
|
+
case CrossUIButtonVariant.destructive:
|
|
55
|
+
backgroundColor = Colors.red;
|
|
56
|
+
foregroundColor = Colors.white;
|
|
57
|
+
break;
|
|
58
|
+
case CrossUIButtonVariant.defaultVariant:
|
|
59
|
+
default:
|
|
60
|
+
backgroundColor = themeColors['primary'] ?? Colors.blue;
|
|
61
|
+
foregroundColor = themeColors['background'] ?? Colors.white;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (disabled) {
|
|
66
|
+
backgroundColor = backgroundColor.withOpacity(0.5);
|
|
67
|
+
foregroundColor = foregroundColor.withOpacity(0.5);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
EdgeInsets padding;
|
|
71
|
+
double fontSize;
|
|
72
|
+
switch (size) {
|
|
73
|
+
case CrossUIButtonSize.sm:
|
|
74
|
+
padding = const EdgeInsets.symmetric(horizontal: 12, vertical: 8);
|
|
75
|
+
fontSize = CrossUITokens.fontSizeSm;
|
|
76
|
+
break;
|
|
77
|
+
case CrossUIButtonSize.lg:
|
|
78
|
+
padding = const EdgeInsets.symmetric(horizontal: 24, vertical: 16);
|
|
79
|
+
fontSize = CrossUITokens.fontSizeLg;
|
|
80
|
+
break;
|
|
81
|
+
case CrossUIButtonSize.defaultSize:
|
|
82
|
+
default:
|
|
83
|
+
padding = const EdgeInsets.symmetric(horizontal: 16, vertical: 12);
|
|
84
|
+
fontSize = CrossUITokens.fontSizeBase;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return InkWell(
|
|
89
|
+
onTap: disabled ? null : onPressed,
|
|
90
|
+
borderRadius: BorderRadius.circular(CrossUITokens.radiusMedium),
|
|
91
|
+
child: Container(
|
|
92
|
+
padding: padding,
|
|
93
|
+
decoration: BoxDecoration(
|
|
94
|
+
color: backgroundColor,
|
|
95
|
+
borderRadius: BorderRadius.circular(CrossUITokens.radiusMedium),
|
|
96
|
+
border: Border.fromBorderSide(borderSide),
|
|
97
|
+
),
|
|
98
|
+
child: Row(
|
|
99
|
+
mainAxisSize: MainAxisSize.min,
|
|
100
|
+
mainAxisAlignment: MainAxisAlignment.center,
|
|
101
|
+
children: [
|
|
102
|
+
if (icon != null) ...[icon!, const SizedBox(width: 8)],
|
|
103
|
+
Text(
|
|
104
|
+
label,
|
|
105
|
+
style: TextStyle(
|
|
106
|
+
color: foregroundColor,
|
|
107
|
+
fontSize: fontSize,
|
|
108
|
+
fontWeight: FontWeight.w600,
|
|
109
|
+
),
|
|
110
|
+
),
|
|
111
|
+
],
|
|
112
|
+
),
|
|
113
|
+
),
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|