neoagent 2.3.1-beta.60 → 2.3.1-beta.61
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/flutter_app/lib/features/onboarding/onboarding_chrome.dart +5 -3
- package/flutter_app/lib/features/onboarding/onboarding_messaging_step.dart +182 -64
- package/flutter_app/lib/features/onboarding/onboarding_model_step.dart +245 -112
- package/package.json +1 -1
- package/server/public/.last_build_id +1 -1
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +50602 -50467
|
@@ -104,12 +104,14 @@ class OnboardingOptionCard extends StatelessWidget {
|
|
|
104
104
|
required this.selected,
|
|
105
105
|
this.onTap,
|
|
106
106
|
this.accent,
|
|
107
|
+
this.compact = false,
|
|
107
108
|
});
|
|
108
109
|
|
|
109
110
|
final Widget child;
|
|
110
111
|
final bool selected;
|
|
111
112
|
final VoidCallback? onTap;
|
|
112
113
|
final Color? accent;
|
|
114
|
+
final bool compact;
|
|
113
115
|
|
|
114
116
|
@override
|
|
115
117
|
Widget build(BuildContext context) {
|
|
@@ -118,16 +120,16 @@ class OnboardingOptionCard extends StatelessWidget {
|
|
|
118
120
|
color: Colors.transparent,
|
|
119
121
|
child: InkWell(
|
|
120
122
|
onTap: onTap,
|
|
121
|
-
borderRadius: BorderRadius.circular(28),
|
|
123
|
+
borderRadius: BorderRadius.circular(compact ? 22 : 28),
|
|
122
124
|
child: AnimatedContainer(
|
|
123
125
|
duration: const Duration(milliseconds: 260),
|
|
124
126
|
curve: Curves.easeOutCubic,
|
|
125
|
-
padding:
|
|
127
|
+
padding: EdgeInsets.all(compact ? 16 : 22),
|
|
126
128
|
decoration: BoxDecoration(
|
|
127
129
|
color: selected
|
|
128
130
|
? highlight.withValues(alpha: 0.16)
|
|
129
131
|
: Colors.white.withValues(alpha: 0.055),
|
|
130
|
-
borderRadius: BorderRadius.circular(28),
|
|
132
|
+
borderRadius: BorderRadius.circular(compact ? 22 : 28),
|
|
131
133
|
border: Border.all(
|
|
132
134
|
color: selected
|
|
133
135
|
? highlight.withValues(alpha: 0.92)
|
|
@@ -27,6 +27,9 @@ class _OnboardingMessagingStepState extends State<OnboardingMessagingStep> {
|
|
|
27
27
|
final platforms = messagingPlatforms.length > 5
|
|
28
28
|
? messagingPlatforms.take(5).toList()
|
|
29
29
|
: messagingPlatforms;
|
|
30
|
+
final width = MediaQuery.sizeOf(context).width;
|
|
31
|
+
final useGrid = width >= 700;
|
|
32
|
+
final columns = width >= 1050 ? 3 : (useGrid ? 2 : 1);
|
|
30
33
|
|
|
31
34
|
return OnboardingScaffold(
|
|
32
35
|
step: 2,
|
|
@@ -74,84 +77,199 @@ class _OnboardingMessagingStepState extends State<OnboardingMessagingStep> {
|
|
|
74
77
|
),
|
|
75
78
|
],
|
|
76
79
|
),
|
|
77
|
-
child:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
child: useGrid
|
|
81
|
+
? GridView.builder(
|
|
82
|
+
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
83
|
+
crossAxisCount: columns,
|
|
84
|
+
crossAxisSpacing: 14,
|
|
85
|
+
mainAxisSpacing: 14,
|
|
86
|
+
childAspectRatio: width >= 1050 ? 1.5 : 1.35,
|
|
87
|
+
),
|
|
88
|
+
itemCount: platforms.length,
|
|
89
|
+
itemBuilder: (context, index) {
|
|
90
|
+
final platform = platforms[index];
|
|
91
|
+
return _MessagingPlatformCard(
|
|
92
|
+
platform: platform,
|
|
93
|
+
selected: _selectedPlatform?.id == platform.id,
|
|
94
|
+
compact: true,
|
|
95
|
+
onTap: () => setState(() => _selectedPlatform = platform),
|
|
96
|
+
)
|
|
97
|
+
.animate()
|
|
98
|
+
.fadeIn(duration: 420.ms, delay: (180 + (index * 80)).ms)
|
|
99
|
+
.slideY(begin: 0.16, end: 0);
|
|
100
|
+
},
|
|
101
|
+
)
|
|
102
|
+
: ListView.separated(
|
|
103
|
+
itemCount: platforms.length,
|
|
104
|
+
separatorBuilder: (_, __) => const SizedBox(height: 14),
|
|
105
|
+
itemBuilder: (context, index) {
|
|
106
|
+
final platform = platforms[index];
|
|
107
|
+
return _MessagingPlatformCard(
|
|
108
|
+
platform: platform,
|
|
109
|
+
selected: _selectedPlatform?.id == platform.id,
|
|
110
|
+
onTap: () => setState(() => _selectedPlatform = platform),
|
|
111
|
+
)
|
|
112
|
+
.animate()
|
|
113
|
+
.fadeIn(duration: 420.ms, delay: (180 + (index * 80)).ms)
|
|
114
|
+
.slideY(begin: 0.16, end: 0);
|
|
115
|
+
},
|
|
116
|
+
),
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
class _MessagingPlatformCard extends StatelessWidget {
|
|
122
|
+
const _MessagingPlatformCard({
|
|
123
|
+
required this.platform,
|
|
124
|
+
required this.selected,
|
|
125
|
+
required this.onTap,
|
|
126
|
+
this.compact = false,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
final MessagingPlatformDescriptor platform;
|
|
130
|
+
final bool selected;
|
|
131
|
+
final VoidCallback onTap;
|
|
132
|
+
final bool compact;
|
|
84
133
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
134
|
+
@override
|
|
135
|
+
Widget build(BuildContext context) {
|
|
136
|
+
final iconSize = compact ? 24.0 : 30.0;
|
|
137
|
+
final shellSize = compact ? 48.0 : 58.0;
|
|
138
|
+
return OnboardingOptionCard(
|
|
139
|
+
selected: selected,
|
|
140
|
+
accent: platform.accent,
|
|
141
|
+
compact: compact,
|
|
142
|
+
onTap: onTap,
|
|
143
|
+
child: compact
|
|
144
|
+
? Column(
|
|
145
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
146
|
+
children: <Widget>[
|
|
147
|
+
Row(
|
|
92
148
|
children: <Widget>[
|
|
93
149
|
Container(
|
|
94
|
-
width:
|
|
95
|
-
height:
|
|
150
|
+
width: shellSize,
|
|
151
|
+
height: shellSize,
|
|
96
152
|
decoration: BoxDecoration(
|
|
97
153
|
color: platform.accent.withValues(alpha: 0.18),
|
|
98
|
-
borderRadius: BorderRadius.circular(
|
|
154
|
+
borderRadius: BorderRadius.circular(16),
|
|
99
155
|
),
|
|
100
156
|
child: Icon(
|
|
101
157
|
platform.icon,
|
|
102
158
|
color: platform.accent,
|
|
103
|
-
size:
|
|
104
|
-
),
|
|
105
|
-
),
|
|
106
|
-
const SizedBox(width: 18),
|
|
107
|
-
Expanded(
|
|
108
|
-
child: Column(
|
|
109
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
110
|
-
children: <Widget>[
|
|
111
|
-
Text(
|
|
112
|
-
platform.label,
|
|
113
|
-
style: const TextStyle(
|
|
114
|
-
color: Colors.white,
|
|
115
|
-
fontSize: 20,
|
|
116
|
-
fontWeight: FontWeight.w800,
|
|
117
|
-
),
|
|
118
|
-
),
|
|
119
|
-
const SizedBox(height: 5),
|
|
120
|
-
Text(
|
|
121
|
-
platform.subtitle,
|
|
122
|
-
style: TextStyle(
|
|
123
|
-
color: Colors.white.withValues(alpha: 0.68),
|
|
124
|
-
fontSize: 14,
|
|
125
|
-
height: 1.45,
|
|
126
|
-
),
|
|
127
|
-
),
|
|
128
|
-
],
|
|
159
|
+
size: iconSize,
|
|
129
160
|
),
|
|
130
161
|
),
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
key: ValueKey<String>(platform.id),
|
|
137
|
-
color: platform.accent,
|
|
138
|
-
size: 28,
|
|
139
|
-
)
|
|
140
|
-
: Icon(
|
|
141
|
-
Icons.add_circle_outline_rounded,
|
|
142
|
-
key: ValueKey<String>('idle-${platform.id}'),
|
|
143
|
-
color: Colors.white.withValues(alpha: 0.26),
|
|
144
|
-
size: 28,
|
|
145
|
-
),
|
|
162
|
+
const Spacer(),
|
|
163
|
+
_SelectionIcon(
|
|
164
|
+
selected: selected,
|
|
165
|
+
color: platform.accent,
|
|
166
|
+
id: platform.id,
|
|
146
167
|
),
|
|
147
168
|
],
|
|
148
169
|
),
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
170
|
+
const SizedBox(height: 14),
|
|
171
|
+
Text(
|
|
172
|
+
platform.label,
|
|
173
|
+
maxLines: 1,
|
|
174
|
+
overflow: TextOverflow.ellipsis,
|
|
175
|
+
style: const TextStyle(
|
|
176
|
+
color: Colors.white,
|
|
177
|
+
fontSize: 18,
|
|
178
|
+
fontWeight: FontWeight.w800,
|
|
179
|
+
),
|
|
180
|
+
),
|
|
181
|
+
const SizedBox(height: 4),
|
|
182
|
+
Text(
|
|
183
|
+
platform.subtitle,
|
|
184
|
+
maxLines: 2,
|
|
185
|
+
overflow: TextOverflow.ellipsis,
|
|
186
|
+
style: TextStyle(
|
|
187
|
+
color: Colors.white.withValues(alpha: 0.68),
|
|
188
|
+
fontSize: 13,
|
|
189
|
+
height: 1.35,
|
|
190
|
+
),
|
|
191
|
+
),
|
|
192
|
+
],
|
|
193
|
+
)
|
|
194
|
+
: Row(
|
|
195
|
+
children: <Widget>[
|
|
196
|
+
Container(
|
|
197
|
+
width: shellSize,
|
|
198
|
+
height: shellSize,
|
|
199
|
+
decoration: BoxDecoration(
|
|
200
|
+
color: platform.accent.withValues(alpha: 0.18),
|
|
201
|
+
borderRadius: BorderRadius.circular(18),
|
|
202
|
+
),
|
|
203
|
+
child: Icon(
|
|
204
|
+
platform.icon,
|
|
205
|
+
color: platform.accent,
|
|
206
|
+
size: iconSize,
|
|
207
|
+
),
|
|
208
|
+
),
|
|
209
|
+
const SizedBox(width: 18),
|
|
210
|
+
Expanded(
|
|
211
|
+
child: Column(
|
|
212
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
213
|
+
children: <Widget>[
|
|
214
|
+
Text(
|
|
215
|
+
platform.label,
|
|
216
|
+
style: const TextStyle(
|
|
217
|
+
color: Colors.white,
|
|
218
|
+
fontSize: 20,
|
|
219
|
+
fontWeight: FontWeight.w800,
|
|
220
|
+
),
|
|
221
|
+
),
|
|
222
|
+
const SizedBox(height: 5),
|
|
223
|
+
Text(
|
|
224
|
+
platform.subtitle,
|
|
225
|
+
style: TextStyle(
|
|
226
|
+
color: Colors.white.withValues(alpha: 0.68),
|
|
227
|
+
fontSize: 14,
|
|
228
|
+
height: 1.45,
|
|
229
|
+
),
|
|
230
|
+
),
|
|
231
|
+
],
|
|
232
|
+
),
|
|
233
|
+
),
|
|
234
|
+
_SelectionIcon(
|
|
235
|
+
selected: selected,
|
|
236
|
+
color: platform.accent,
|
|
237
|
+
id: platform.id,
|
|
238
|
+
),
|
|
239
|
+
],
|
|
240
|
+
),
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
class _SelectionIcon extends StatelessWidget {
|
|
246
|
+
const _SelectionIcon({
|
|
247
|
+
required this.selected,
|
|
248
|
+
required this.color,
|
|
249
|
+
required this.id,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
final bool selected;
|
|
253
|
+
final Color color;
|
|
254
|
+
final String id;
|
|
255
|
+
|
|
256
|
+
@override
|
|
257
|
+
Widget build(BuildContext context) {
|
|
258
|
+
return AnimatedSwitcher(
|
|
259
|
+
duration: const Duration(milliseconds: 220),
|
|
260
|
+
child: selected
|
|
261
|
+
? Icon(
|
|
262
|
+
Icons.check_circle_rounded,
|
|
263
|
+
key: ValueKey<String>(id),
|
|
264
|
+
color: color,
|
|
265
|
+
size: 28,
|
|
266
|
+
)
|
|
267
|
+
: Icon(
|
|
268
|
+
Icons.add_circle_outline_rounded,
|
|
269
|
+
key: ValueKey<String>('idle-$id'),
|
|
270
|
+
color: Colors.white.withValues(alpha: 0.26),
|
|
271
|
+
size: 28,
|
|
272
|
+
),
|
|
155
273
|
);
|
|
156
274
|
}
|
|
157
275
|
}
|
|
@@ -74,6 +74,10 @@ class _OnboardingModelStepState extends State<OnboardingModelStep> {
|
|
|
74
74
|
|
|
75
75
|
@override
|
|
76
76
|
Widget build(BuildContext context) {
|
|
77
|
+
final width = MediaQuery.sizeOf(context).width;
|
|
78
|
+
final useGrid = width >= 720;
|
|
79
|
+
final columns = width >= 1150 ? 3 : (useGrid ? 2 : 1);
|
|
80
|
+
|
|
77
81
|
return OnboardingScaffold(
|
|
78
82
|
step: 3,
|
|
79
83
|
totalSteps: 4,
|
|
@@ -102,123 +106,44 @@ class _OnboardingModelStepState extends State<OnboardingModelStep> {
|
|
|
102
106
|
),
|
|
103
107
|
),
|
|
104
108
|
)
|
|
109
|
+
: useGrid
|
|
110
|
+
? GridView.builder(
|
|
111
|
+
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
112
|
+
crossAxisCount: columns,
|
|
113
|
+
crossAxisSpacing: 14,
|
|
114
|
+
mainAxisSpacing: 14,
|
|
115
|
+
childAspectRatio: width >= 1150 ? 1.38 : 1.18,
|
|
116
|
+
),
|
|
117
|
+
itemCount: _models.length,
|
|
118
|
+
itemBuilder: (context, index) {
|
|
119
|
+
final model = _models[index];
|
|
120
|
+
return _ModelChoiceCard(
|
|
121
|
+
model: model,
|
|
122
|
+
color: _getColorForModel(model),
|
|
123
|
+
icon: _getIconForModel(model),
|
|
124
|
+
selected: _selectedModel == model.id,
|
|
125
|
+
compact: true,
|
|
126
|
+
onTap: () => _selectModel(context, model),
|
|
127
|
+
)
|
|
128
|
+
.animate()
|
|
129
|
+
.fadeIn(
|
|
130
|
+
duration: 420.ms,
|
|
131
|
+
delay: (180 + (index.clamp(0, 5) * 70)).ms,
|
|
132
|
+
)
|
|
133
|
+
.slideY(begin: 0.16, end: 0);
|
|
134
|
+
},
|
|
135
|
+
)
|
|
105
136
|
: ListView.separated(
|
|
106
137
|
itemCount: _models.length,
|
|
107
138
|
separatorBuilder: (_, __) => const SizedBox(height: 14),
|
|
108
139
|
itemBuilder: (context, index) {
|
|
109
140
|
final model = _models[index];
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
onTap: ()
|
|
116
|
-
final previousModel = _selectedModel;
|
|
117
|
-
setState(() => _selectedModel = model.id);
|
|
118
|
-
try {
|
|
119
|
-
await widget.controller.saveSettingsPayload(
|
|
120
|
-
<String, Object?>{'default_chat_model': model.id},
|
|
121
|
-
);
|
|
122
|
-
} catch (e) {
|
|
123
|
-
if (context.mounted) {
|
|
124
|
-
ScaffoldMessenger.of(context).showSnackBar(
|
|
125
|
-
SnackBar(
|
|
126
|
-
content: Text('Failed to save selection: $e'),
|
|
127
|
-
),
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
setState(() => _selectedModel = previousModel);
|
|
131
|
-
}
|
|
132
|
-
},
|
|
133
|
-
child: Row(
|
|
134
|
-
children: <Widget>[
|
|
135
|
-
Container(
|
|
136
|
-
width: 58,
|
|
137
|
-
height: 58,
|
|
138
|
-
decoration: BoxDecoration(
|
|
139
|
-
color: color.withValues(alpha: 0.18),
|
|
140
|
-
borderRadius: BorderRadius.circular(18),
|
|
141
|
-
),
|
|
142
|
-
child: Icon(
|
|
143
|
-
_getIconForModel(model),
|
|
144
|
-
color: color,
|
|
145
|
-
size: 30,
|
|
146
|
-
),
|
|
147
|
-
),
|
|
148
|
-
const SizedBox(width: 18),
|
|
149
|
-
Expanded(
|
|
150
|
-
child: Column(
|
|
151
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
152
|
-
children: <Widget>[
|
|
153
|
-
Wrap(
|
|
154
|
-
spacing: 10,
|
|
155
|
-
runSpacing: 8,
|
|
156
|
-
crossAxisAlignment: WrapCrossAlignment.center,
|
|
157
|
-
children: <Widget>[
|
|
158
|
-
Text(
|
|
159
|
-
model.label,
|
|
160
|
-
style: const TextStyle(
|
|
161
|
-
color: Colors.white,
|
|
162
|
-
fontSize: 20,
|
|
163
|
-
fontWeight: FontWeight.w800,
|
|
164
|
-
),
|
|
165
|
-
),
|
|
166
|
-
Container(
|
|
167
|
-
padding: const EdgeInsets.symmetric(
|
|
168
|
-
horizontal: 10,
|
|
169
|
-
vertical: 5,
|
|
170
|
-
),
|
|
171
|
-
decoration: BoxDecoration(
|
|
172
|
-
color: Colors.white.withValues(
|
|
173
|
-
alpha: 0.08,
|
|
174
|
-
),
|
|
175
|
-
borderRadius: BorderRadius.circular(
|
|
176
|
-
999,
|
|
177
|
-
),
|
|
178
|
-
),
|
|
179
|
-
child: Text(
|
|
180
|
-
model.provider,
|
|
181
|
-
style: TextStyle(
|
|
182
|
-
color: Colors.white.withValues(
|
|
183
|
-
alpha: 0.72,
|
|
184
|
-
),
|
|
185
|
-
fontSize: 12,
|
|
186
|
-
fontWeight: FontWeight.w700,
|
|
187
|
-
),
|
|
188
|
-
),
|
|
189
|
-
),
|
|
190
|
-
],
|
|
191
|
-
),
|
|
192
|
-
const SizedBox(height: 6),
|
|
193
|
-
Text(
|
|
194
|
-
model.purpose,
|
|
195
|
-
style: TextStyle(
|
|
196
|
-
color: Colors.white.withValues(alpha: 0.68),
|
|
197
|
-
fontSize: 14,
|
|
198
|
-
height: 1.45,
|
|
199
|
-
),
|
|
200
|
-
),
|
|
201
|
-
],
|
|
202
|
-
),
|
|
203
|
-
),
|
|
204
|
-
AnimatedSwitcher(
|
|
205
|
-
duration: const Duration(milliseconds: 220),
|
|
206
|
-
child: selected
|
|
207
|
-
? Icon(
|
|
208
|
-
Icons.check_circle_rounded,
|
|
209
|
-
key: ValueKey<String>(model.id),
|
|
210
|
-
color: color,
|
|
211
|
-
size: 28,
|
|
212
|
-
)
|
|
213
|
-
: Icon(
|
|
214
|
-
Icons.radio_button_unchecked_rounded,
|
|
215
|
-
key: ValueKey<String>('idle-${model.id}'),
|
|
216
|
-
color: Colors.white.withValues(alpha: 0.25),
|
|
217
|
-
size: 28,
|
|
218
|
-
),
|
|
219
|
-
),
|
|
220
|
-
],
|
|
221
|
-
),
|
|
141
|
+
return _ModelChoiceCard(
|
|
142
|
+
model: model,
|
|
143
|
+
color: _getColorForModel(model),
|
|
144
|
+
icon: _getIconForModel(model),
|
|
145
|
+
selected: _selectedModel == model.id,
|
|
146
|
+
onTap: () => _selectModel(context, model),
|
|
222
147
|
)
|
|
223
148
|
.animate()
|
|
224
149
|
.fadeIn(
|
|
@@ -230,4 +155,212 @@ class _OnboardingModelStepState extends State<OnboardingModelStep> {
|
|
|
230
155
|
),
|
|
231
156
|
);
|
|
232
157
|
}
|
|
158
|
+
|
|
159
|
+
Future<void> _selectModel(BuildContext context, ModelMeta model) async {
|
|
160
|
+
final previousModel = _selectedModel;
|
|
161
|
+
setState(() => _selectedModel = model.id);
|
|
162
|
+
try {
|
|
163
|
+
await widget.controller.saveSettingsPayload(<String, Object?>{
|
|
164
|
+
'default_chat_model': model.id,
|
|
165
|
+
});
|
|
166
|
+
} catch (e) {
|
|
167
|
+
if (context.mounted) {
|
|
168
|
+
ScaffoldMessenger.of(
|
|
169
|
+
context,
|
|
170
|
+
).showSnackBar(SnackBar(content: Text('Failed to save selection: $e')));
|
|
171
|
+
}
|
|
172
|
+
setState(() => _selectedModel = previousModel);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
class _ModelChoiceCard extends StatelessWidget {
|
|
178
|
+
const _ModelChoiceCard({
|
|
179
|
+
required this.model,
|
|
180
|
+
required this.color,
|
|
181
|
+
required this.icon,
|
|
182
|
+
required this.selected,
|
|
183
|
+
required this.onTap,
|
|
184
|
+
this.compact = false,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
final ModelMeta model;
|
|
188
|
+
final Color color;
|
|
189
|
+
final IconData icon;
|
|
190
|
+
final bool selected;
|
|
191
|
+
final VoidCallback onTap;
|
|
192
|
+
final bool compact;
|
|
193
|
+
|
|
194
|
+
@override
|
|
195
|
+
Widget build(BuildContext context) {
|
|
196
|
+
final shellSize = compact ? 48.0 : 58.0;
|
|
197
|
+
final iconSize = compact ? 24.0 : 30.0;
|
|
198
|
+
final titleSize = compact ? 17.0 : 20.0;
|
|
199
|
+
final purposeSize = compact ? 13.0 : 14.0;
|
|
200
|
+
|
|
201
|
+
return OnboardingOptionCard(
|
|
202
|
+
selected: selected,
|
|
203
|
+
accent: color,
|
|
204
|
+
compact: compact,
|
|
205
|
+
onTap: onTap,
|
|
206
|
+
child: compact
|
|
207
|
+
? Column(
|
|
208
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
209
|
+
children: <Widget>[
|
|
210
|
+
Row(
|
|
211
|
+
children: <Widget>[
|
|
212
|
+
Container(
|
|
213
|
+
width: shellSize,
|
|
214
|
+
height: shellSize,
|
|
215
|
+
decoration: BoxDecoration(
|
|
216
|
+
color: color.withValues(alpha: 0.18),
|
|
217
|
+
borderRadius: BorderRadius.circular(16),
|
|
218
|
+
),
|
|
219
|
+
child: Icon(icon, color: color, size: iconSize),
|
|
220
|
+
),
|
|
221
|
+
const Spacer(),
|
|
222
|
+
AnimatedSwitcher(
|
|
223
|
+
duration: const Duration(milliseconds: 220),
|
|
224
|
+
child: selected
|
|
225
|
+
? Icon(
|
|
226
|
+
Icons.check_circle_rounded,
|
|
227
|
+
key: ValueKey<String>(model.id),
|
|
228
|
+
color: color,
|
|
229
|
+
size: 28,
|
|
230
|
+
)
|
|
231
|
+
: Icon(
|
|
232
|
+
Icons.radio_button_unchecked_rounded,
|
|
233
|
+
key: ValueKey<String>('idle-${model.id}'),
|
|
234
|
+
color: Colors.white.withValues(alpha: 0.25),
|
|
235
|
+
size: 28,
|
|
236
|
+
),
|
|
237
|
+
),
|
|
238
|
+
],
|
|
239
|
+
),
|
|
240
|
+
const SizedBox(height: 12),
|
|
241
|
+
Text(
|
|
242
|
+
model.label,
|
|
243
|
+
maxLines: 1,
|
|
244
|
+
overflow: TextOverflow.ellipsis,
|
|
245
|
+
style: TextStyle(
|
|
246
|
+
color: Colors.white,
|
|
247
|
+
fontSize: titleSize,
|
|
248
|
+
fontWeight: FontWeight.w800,
|
|
249
|
+
),
|
|
250
|
+
),
|
|
251
|
+
const SizedBox(height: 6),
|
|
252
|
+
Container(
|
|
253
|
+
padding: const EdgeInsets.symmetric(
|
|
254
|
+
horizontal: 10,
|
|
255
|
+
vertical: 5,
|
|
256
|
+
),
|
|
257
|
+
decoration: BoxDecoration(
|
|
258
|
+
color: Colors.white.withValues(alpha: 0.08),
|
|
259
|
+
borderRadius: BorderRadius.circular(999),
|
|
260
|
+
),
|
|
261
|
+
child: Text(
|
|
262
|
+
model.provider,
|
|
263
|
+
maxLines: 1,
|
|
264
|
+
overflow: TextOverflow.ellipsis,
|
|
265
|
+
style: TextStyle(
|
|
266
|
+
color: Colors.white.withValues(alpha: 0.72),
|
|
267
|
+
fontSize: 11,
|
|
268
|
+
fontWeight: FontWeight.w700,
|
|
269
|
+
),
|
|
270
|
+
),
|
|
271
|
+
),
|
|
272
|
+
const SizedBox(height: 10),
|
|
273
|
+
Text(
|
|
274
|
+
model.purpose,
|
|
275
|
+
maxLines: 3,
|
|
276
|
+
overflow: TextOverflow.ellipsis,
|
|
277
|
+
style: TextStyle(
|
|
278
|
+
color: Colors.white.withValues(alpha: 0.68),
|
|
279
|
+
fontSize: purposeSize,
|
|
280
|
+
height: 1.35,
|
|
281
|
+
),
|
|
282
|
+
),
|
|
283
|
+
],
|
|
284
|
+
)
|
|
285
|
+
: Row(
|
|
286
|
+
children: <Widget>[
|
|
287
|
+
Container(
|
|
288
|
+
width: shellSize,
|
|
289
|
+
height: shellSize,
|
|
290
|
+
decoration: BoxDecoration(
|
|
291
|
+
color: color.withValues(alpha: 0.18),
|
|
292
|
+
borderRadius: BorderRadius.circular(18),
|
|
293
|
+
),
|
|
294
|
+
child: Icon(icon, color: color, size: iconSize),
|
|
295
|
+
),
|
|
296
|
+
const SizedBox(width: 18),
|
|
297
|
+
Expanded(
|
|
298
|
+
child: Column(
|
|
299
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
300
|
+
children: <Widget>[
|
|
301
|
+
Wrap(
|
|
302
|
+
spacing: 10,
|
|
303
|
+
runSpacing: 8,
|
|
304
|
+
crossAxisAlignment: WrapCrossAlignment.center,
|
|
305
|
+
children: <Widget>[
|
|
306
|
+
Text(
|
|
307
|
+
model.label,
|
|
308
|
+
style: TextStyle(
|
|
309
|
+
color: Colors.white,
|
|
310
|
+
fontSize: titleSize,
|
|
311
|
+
fontWeight: FontWeight.w800,
|
|
312
|
+
),
|
|
313
|
+
),
|
|
314
|
+
Container(
|
|
315
|
+
padding: const EdgeInsets.symmetric(
|
|
316
|
+
horizontal: 10,
|
|
317
|
+
vertical: 5,
|
|
318
|
+
),
|
|
319
|
+
decoration: BoxDecoration(
|
|
320
|
+
color: Colors.white.withValues(alpha: 0.08),
|
|
321
|
+
borderRadius: BorderRadius.circular(999),
|
|
322
|
+
),
|
|
323
|
+
child: Text(
|
|
324
|
+
model.provider,
|
|
325
|
+
style: TextStyle(
|
|
326
|
+
color: Colors.white.withValues(alpha: 0.72),
|
|
327
|
+
fontSize: 12,
|
|
328
|
+
fontWeight: FontWeight.w700,
|
|
329
|
+
),
|
|
330
|
+
),
|
|
331
|
+
),
|
|
332
|
+
],
|
|
333
|
+
),
|
|
334
|
+
const SizedBox(height: 6),
|
|
335
|
+
Text(
|
|
336
|
+
model.purpose,
|
|
337
|
+
style: TextStyle(
|
|
338
|
+
color: Colors.white.withValues(alpha: 0.68),
|
|
339
|
+
fontSize: purposeSize,
|
|
340
|
+
height: 1.45,
|
|
341
|
+
),
|
|
342
|
+
),
|
|
343
|
+
],
|
|
344
|
+
),
|
|
345
|
+
),
|
|
346
|
+
AnimatedSwitcher(
|
|
347
|
+
duration: const Duration(milliseconds: 220),
|
|
348
|
+
child: selected
|
|
349
|
+
? Icon(
|
|
350
|
+
Icons.check_circle_rounded,
|
|
351
|
+
key: ValueKey<String>(model.id),
|
|
352
|
+
color: color,
|
|
353
|
+
size: 28,
|
|
354
|
+
)
|
|
355
|
+
: Icon(
|
|
356
|
+
Icons.radio_button_unchecked_rounded,
|
|
357
|
+
key: ValueKey<String>('idle-${model.id}'),
|
|
358
|
+
color: Colors.white.withValues(alpha: 0.25),
|
|
359
|
+
size: 28,
|
|
360
|
+
),
|
|
361
|
+
),
|
|
362
|
+
],
|
|
363
|
+
),
|
|
364
|
+
);
|
|
365
|
+
}
|
|
233
366
|
}
|