@@ -8,6 +8,9 @@ import 'package:dart_schema_builder/dart_schema_builder.dart';
8
8
import 'package:flutter/material.dart' ;
9
9
import 'package:flutter_genui/flutter_genui.dart' ;
10
10
11
+ import '../tools/booking/booking_service.dart' ;
12
+ import '../tools/booking/model.dart' ;
13
+
11
14
final _schema = S .object (
12
15
properties: {
13
16
'title' : S .string (
@@ -17,7 +20,13 @@ final _schema = S.object(
17
20
description: 'A list of items to display in the carousel.' ,
18
21
items: S .object (
19
22
properties: {
20
- 'title' : S .string (description: 'The title of the carousel item.' ),
23
+ 'description' : S .string (
24
+ description:
25
+ 'The short description of the carousel item. '
26
+ 'It may include the price and location if applicable. '
27
+ 'It should be very concise. '
28
+ 'Example: "The Dart Inn in Sunnyvale, CA for \$ 150"' ,
29
+ ),
21
30
'imageChildId' : S .string (
22
31
description:
23
32
'The ID of the Image widget to display as the carousel item '
@@ -30,7 +39,7 @@ final _schema = S.object(
30
39
'a list of hotels or other bookable items.' ,
31
40
),
32
41
},
33
- required : ['title ' , 'imageChildId' ],
42
+ required : ['description ' , 'imageChildId' ],
34
43
),
35
44
),
36
45
},
@@ -67,7 +76,7 @@ final travelCarousel = CatalogItem(
67
76
items: items
68
77
.map (
69
78
(e) => _TravelCarouselItemData (
70
- title : e.title ,
79
+ description : e.description ,
71
80
imageChild: buildChild (e.imageChildId),
72
81
listingSelectionId: e.listingSelectionId,
73
82
),
@@ -77,95 +86,7 @@ final travelCarousel = CatalogItem(
77
86
dispatchEvent: dispatchEvent,
78
87
);
79
88
},
80
- exampleData: [
81
- () => {
82
- 'root' : 'greece_inspiration_column' ,
83
- 'widgets' : [
84
- {
85
- 'id' : 'greece_inspiration_column' ,
86
- 'widget' : {
87
- 'Column' : {
88
- 'children' : ['inspiration_title' , 'inspiration_carousel' ],
89
- },
90
- },
91
- },
92
- {
93
- 'id' : 'inspiration_title' ,
94
- 'widget' : {
95
- 'Text' : {
96
- 'text' :
97
- "Let's plan your dream trip to Greece! "
98
- 'What kind of experience'
99
- ' are you looking for?' ,
100
- },
101
- },
102
- },
103
- {
104
- 'widget' : {
105
- 'TravelCarousel' : {
106
- 'items' : [
107
- {
108
- 'title' : 'Relaxing Beach Holiday' ,
109
- 'imageChildId' : 'santorini_beach_image' ,
110
- 'listingSelectionId' : '12345' ,
111
- },
112
- {
113
- 'imageChildId' : 'akrotiri_fresco_image' ,
114
- 'title' : 'Cultural Exploration' ,
115
- 'listingSelectionId' : '12346' ,
116
- },
117
- {
118
- 'imageChildId' : 'santorini_caldera_image' ,
119
- 'title' : 'Adventure & Outdoors' ,
120
- 'listingSelectionId' : '12347' ,
121
- },
122
- {'title' : 'Foodie Tour' , 'imageChildId' : 'greece_food_image' },
123
- ],
124
- },
125
- },
126
- 'id' : 'inspiration_carousel' ,
127
- },
128
- {
129
- 'id' : 'santorini_beach_image' ,
130
- 'widget' : {
131
- 'Image' : {
132
- 'fit' : 'cover' ,
133
- 'assetName' : 'assets/travel_images/santorini_panorama.jpg' ,
134
- },
135
- },
136
- },
137
- {
138
- 'id' : 'akrotiri_fresco_image' ,
139
- 'widget' : {
140
- 'Image' : {
141
- 'fit' : 'cover' ,
142
- 'assetName' :
143
- 'assets/travel_images/akrotiri_spring_fresco_santorini.jpg' ,
144
- },
145
- },
146
- },
147
- {
148
- 'id' : 'santorini_caldera_image' ,
149
- 'widget' : {
150
- 'Image' : {
151
- 'assetName' : 'assets/travel_images/santorini_from_space.jpg' ,
152
- 'fit' : 'cover' ,
153
- },
154
- },
155
- },
156
- {
157
- 'widget' : {
158
- 'Image' : {
159
- 'fit' : 'cover' ,
160
- 'assetName' :
161
- 'assets/travel_images/saffron_gatherers_fresco_santorini.jpg' ,
162
- },
163
- },
164
- 'id' : 'greece_food_image' ,
165
- },
166
- ],
167
- },
168
- ],
89
+ exampleData: [_inspirationExample, _hotelExample],
169
90
);
170
91
171
92
extension type _TravelCarouselData .fromMap (Map <String , Object ?> _json) {
@@ -189,16 +110,16 @@ extension type _TravelCarouselItemSchemaData.fromMap(
189
110
Map <String , Object ?> _json
190
111
) {
191
112
factory _TravelCarouselItemSchemaData ({
192
- required String title ,
113
+ required String description ,
193
114
required String imageChildId,
194
115
String ? listingSelectionId,
195
116
}) => _TravelCarouselItemSchemaData .fromMap ({
196
- 'title ' : title ,
117
+ 'description ' : description ,
197
118
'imageChildId' : imageChildId,
198
119
if (listingSelectionId != null ) 'listingSelectionId' : listingSelectionId,
199
120
});
200
121
201
- String get title => _json['title ' ] as String ;
122
+ String get description => _json['description ' ] as String ;
202
123
String get imageChildId => _json['imageChildId' ] as String ;
203
124
String ? get listingSelectionId => _json['listingSelectionId' ] as String ? ;
204
125
}
@@ -240,7 +161,7 @@ class _TravelCarousel extends StatelessWidget {
240
161
const SizedBox (height: 16.0 ),
241
162
],
242
163
SizedBox (
243
- height: 220 ,
164
+ height: 240 ,
244
165
child: ScrollConfiguration (
245
166
behavior: _DesktopAndWebScrollBehavior (),
246
167
child: ListView .separated (
@@ -264,12 +185,12 @@ class _TravelCarousel extends StatelessWidget {
264
185
}
265
186
266
187
class _TravelCarouselItemData {
267
- final String title ;
188
+ final String description ;
268
189
final Widget imageChild;
269
190
final String ? listingSelectionId;
270
191
271
192
_TravelCarouselItemData ({
272
- required this .title ,
193
+ required this .description ,
273
194
required this .imageChild,
274
195
this .listingSelectionId,
275
196
});
@@ -297,7 +218,7 @@ class _TravelCarouselItem extends StatelessWidget {
297
218
widgetId: widgetId,
298
219
eventType: 'itemSelected' ,
299
220
value: {
300
- 'title ' : data.title ,
221
+ 'description ' : data.description ,
301
222
if (data.listingSelectionId != null )
302
223
'listingSelectionId' : data.listingSelectionId,
303
224
},
@@ -312,12 +233,16 @@ class _TravelCarouselItem extends StatelessWidget {
312
233
borderRadius: BorderRadius .circular (10.0 ),
313
234
child: SizedBox (height: 150 , width: 190 , child: data.imageChild),
314
235
),
315
- Padding (
236
+ Container (
237
+ height: 90 ,
316
238
padding: const EdgeInsets .all (8.0 ),
239
+ alignment: Alignment .center,
317
240
child: Text (
318
- data.title,
241
+ data.description,
242
+ textAlign: TextAlign .center,
319
243
style: Theme .of (context).textTheme.titleMedium,
320
- maxLines: 1 ,
244
+ softWrap: true ,
245
+ maxLines: 3 ,
321
246
overflow: TextOverflow .ellipsis,
322
247
),
323
248
),
@@ -327,3 +252,141 @@ class _TravelCarouselItem extends StatelessWidget {
327
252
);
328
253
}
329
254
}
255
+
256
+ JsonMap _hotelExample () {
257
+ final hotels = BookingService .instance.listHotelsSync (
258
+ HotelSearch (
259
+ query: '' ,
260
+ checkIn: DateTime .now (),
261
+ checkOut: DateTime .now ().add (const Duration (days: 7 )),
262
+ guests: 2 ,
263
+ ),
264
+ );
265
+ final hotel1 = hotels.listings[0 ];
266
+ final hotel2 = hotels.listings[1 ];
267
+
268
+ return {
269
+ 'root' : 'hotel_carousel' ,
270
+ 'widgets' : [
271
+ {
272
+ 'widget' : {
273
+ 'TravelCarousel' : {
274
+ 'items' : [
275
+ {
276
+ 'description' : hotel1.description,
277
+ 'imageChildId' : 'image_1' ,
278
+ 'listingSelectionId' : '12345' ,
279
+ },
280
+ {
281
+ 'description' : hotel2.description,
282
+ 'imageChildId' : 'image_2' ,
283
+ 'listingSelectionId' : '12346' ,
284
+ },
285
+ ],
286
+ },
287
+ },
288
+ 'id' : 'hotel_carousel' ,
289
+ },
290
+ {
291
+ 'id' : 'image_1' ,
292
+ 'widget' : {
293
+ 'Image' : {'fit' : 'cover' , 'assetName' : hotel1.images[0 ]},
294
+ },
295
+ },
296
+ {
297
+ 'id' : 'image_2' ,
298
+ 'widget' : {
299
+ 'Image' : {'fit' : 'cover' , 'assetName' : hotel2.images[0 ]},
300
+ },
301
+ },
302
+ ],
303
+ };
304
+ }
305
+
306
+ JsonMap _inspirationExample () => {
307
+ 'root' : 'greece_inspiration_column' ,
308
+ 'widgets' : [
309
+ {
310
+ 'id' : 'greece_inspiration_column' ,
311
+ 'widget' : {
312
+ 'Column' : {
313
+ 'children' : ['inspiration_title' , 'inspiration_carousel' ],
314
+ },
315
+ },
316
+ },
317
+ {
318
+ 'id' : 'inspiration_title' ,
319
+ 'widget' : {
320
+ 'Text' : {
321
+ 'text' :
322
+ "Let's plan your dream trip to Greece! "
323
+ 'What kind of experience'
324
+ ' are you looking for?' ,
325
+ },
326
+ },
327
+ },
328
+ {
329
+ 'widget' : {
330
+ 'TravelCarousel' : {
331
+ 'items' : [
332
+ {
333
+ 'description' : 'Relaxing Beach Holiday' ,
334
+ 'imageChildId' : 'santorini_beach_image' ,
335
+ 'listingSelectionId' : '12345' ,
336
+ },
337
+ {
338
+ 'imageChildId' : 'akrotiri_fresco_image' ,
339
+ 'description' : 'Cultural Exploration' ,
340
+ 'listingSelectionId' : '12346' ,
341
+ },
342
+ {
343
+ 'imageChildId' : 'santorini_caldera_image' ,
344
+ 'description' : 'Adventure & Outdoors' ,
345
+ 'listingSelectionId' : '12347' ,
346
+ },
347
+ {'description' : 'Foodie Tour' , 'imageChildId' : 'greece_food_image' },
348
+ ],
349
+ },
350
+ },
351
+ 'id' : 'inspiration_carousel' ,
352
+ },
353
+ {
354
+ 'id' : 'santorini_beach_image' ,
355
+ 'widget' : {
356
+ 'Image' : {
357
+ 'fit' : 'cover' ,
358
+ 'assetName' : 'assets/travel_images/santorini_panorama.jpg' ,
359
+ },
360
+ },
361
+ },
362
+ {
363
+ 'id' : 'akrotiri_fresco_image' ,
364
+ 'widget' : {
365
+ 'Image' : {
366
+ 'fit' : 'cover' ,
367
+ 'assetName' :
368
+ 'assets/travel_images/akrotiri_spring_fresco_santorini.jpg' ,
369
+ },
370
+ },
371
+ },
372
+ {
373
+ 'id' : 'santorini_caldera_image' ,
374
+ 'widget' : {
375
+ 'Image' : {
376
+ 'assetName' : 'assets/travel_images/santorini_from_space.jpg' ,
377
+ 'fit' : 'cover' ,
378
+ },
379
+ },
380
+ },
381
+ {
382
+ 'widget' : {
383
+ 'Image' : {
384
+ 'fit' : 'cover' ,
385
+ 'assetName' :
386
+ 'assets/travel_images/saffron_gatherers_fresco_santorini.jpg' ,
387
+ },
388
+ },
389
+ 'id' : 'greece_food_image' ,
390
+ },
391
+ ],
392
+ };
0 commit comments