flet-datatable2 0.1.0.dev1__py3-none-any.whl
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.
Potentially problematic release.
This version of flet-datatable2 might be problematic. Click here for more details.
- flet_datatable2/__init__.py +5 -0
- flet_datatable2/datacolumn2.py +173 -0
- flet_datatable2/datarow2.py +220 -0
- flet_datatable2/datatable2.py +864 -0
- flet_datatable2-0.1.0.dev1.dist-info/METADATA +38 -0
- flet_datatable2-0.1.0.dev1.dist-info/RECORD +17 -0
- flet_datatable2-0.1.0.dev1.dist-info/WHEEL +5 -0
- flet_datatable2-0.1.0.dev1.dist-info/top_level.txt +2 -0
- flutter/flet_datatable2/CHANGELOG.md +3 -0
- flutter/flet_datatable2/LICENSE +1 -0
- flutter/flet_datatable2/README.md +6 -0
- flutter/flet_datatable2/lib/flet_datatable2.dart +3 -0
- flutter/flet_datatable2/lib/src/create_control.dart +22 -0
- flutter/flet_datatable2/lib/src/data_sources.dart +692 -0
- flutter/flet_datatable2/lib/src/datatable2.dart +309 -0
- flutter/flet_datatable2/pubspec.lock +751 -0
- flutter/flet_datatable2/pubspec.yaml +17 -0
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
// ignore_for_file: avoid_print
|
|
2
|
+
|
|
3
|
+
import 'package:flutter/material.dart';
|
|
4
|
+
import 'package:intl/intl.dart';
|
|
5
|
+
import 'package:data_table_2/data_table_2.dart';
|
|
6
|
+
|
|
7
|
+
// Copyright 2019 The Flutter team. All rights reserved.
|
|
8
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
9
|
+
// found in the LICENSE file.
|
|
10
|
+
|
|
11
|
+
// The file was extracted from GitHub: https://github.com/flutter/gallery
|
|
12
|
+
// Changes and modifications by Maxim Saplin, 2021
|
|
13
|
+
|
|
14
|
+
/// Keeps track of selected rows, feed the data into DesertsDataSource
|
|
15
|
+
class RestorableDessertSelections extends RestorableProperty<Set<int>> {
|
|
16
|
+
Set<int> _dessertSelections = {};
|
|
17
|
+
|
|
18
|
+
/// Returns whether or not a dessert row is selected by index.
|
|
19
|
+
bool isSelected(int index) => _dessertSelections.contains(index);
|
|
20
|
+
|
|
21
|
+
/// Takes a list of [Dessert]s and saves the row indices of selected rows
|
|
22
|
+
/// into a [Set].
|
|
23
|
+
void setDessertSelections(List<Dessert> desserts) {
|
|
24
|
+
final updatedSet = <int>{};
|
|
25
|
+
for (var i = 0; i < desserts.length; i += 1) {
|
|
26
|
+
var dessert = desserts[i];
|
|
27
|
+
if (dessert.selected) {
|
|
28
|
+
updatedSet.add(i);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
_dessertSelections = updatedSet;
|
|
32
|
+
notifyListeners();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@override
|
|
36
|
+
Set<int> createDefaultValue() => _dessertSelections;
|
|
37
|
+
|
|
38
|
+
@override
|
|
39
|
+
Set<int> fromPrimitives(Object? data) {
|
|
40
|
+
final selectedItemIndices = data as List<dynamic>;
|
|
41
|
+
_dessertSelections = {
|
|
42
|
+
...selectedItemIndices.map<int>((dynamic id) => id as int),
|
|
43
|
+
};
|
|
44
|
+
return _dessertSelections;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@override
|
|
48
|
+
void initWithValue(Set<int> value) {
|
|
49
|
+
_dessertSelections = value;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@override
|
|
53
|
+
Object toPrimitives() => _dessertSelections.toList();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
int _idCounter = 0;
|
|
57
|
+
|
|
58
|
+
/// Domain model entity
|
|
59
|
+
class Dessert {
|
|
60
|
+
Dessert(
|
|
61
|
+
this.name,
|
|
62
|
+
this.calories,
|
|
63
|
+
this.fat,
|
|
64
|
+
this.carbs,
|
|
65
|
+
this.protein,
|
|
66
|
+
this.sodium,
|
|
67
|
+
this.calcium,
|
|
68
|
+
this.iron,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
final int id = _idCounter++;
|
|
72
|
+
|
|
73
|
+
final String name;
|
|
74
|
+
final int calories;
|
|
75
|
+
final double fat;
|
|
76
|
+
final int carbs;
|
|
77
|
+
final double protein;
|
|
78
|
+
final int sodium;
|
|
79
|
+
final int calcium;
|
|
80
|
+
final int iron;
|
|
81
|
+
bool selected = false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// Data source implementing standard Flutter's DataTableSource abstract class
|
|
85
|
+
/// which is part of DataTable and PaginatedDataTable synchronous data fecthin API.
|
|
86
|
+
/// This class uses static collection of deserts as a data store, projects it into
|
|
87
|
+
/// DataRows, keeps track of selected items, provides sprting capability
|
|
88
|
+
class DessertDataSource extends DataTableSource {
|
|
89
|
+
DessertDataSource.empty(this.context) {
|
|
90
|
+
desserts = [];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
DessertDataSource(this.context,
|
|
94
|
+
[sortedByCalories = false,
|
|
95
|
+
this.hasRowTaps = false,
|
|
96
|
+
this.hasRowHeightOverrides = false,
|
|
97
|
+
this.hasZebraStripes = false]) {
|
|
98
|
+
desserts = _desserts;
|
|
99
|
+
if (sortedByCalories) {
|
|
100
|
+
sort((d) => d.calories, true);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
final BuildContext context;
|
|
105
|
+
late List<Dessert> desserts;
|
|
106
|
+
// Add row tap handlers and show snackbar
|
|
107
|
+
bool hasRowTaps = false;
|
|
108
|
+
// Override height values for certain rows
|
|
109
|
+
bool hasRowHeightOverrides = false;
|
|
110
|
+
// Color each Row by index's parity
|
|
111
|
+
bool hasZebraStripes = false;
|
|
112
|
+
|
|
113
|
+
void sort<T>(Comparable<T> Function(Dessert d) getField, bool ascending) {
|
|
114
|
+
desserts.sort((a, b) {
|
|
115
|
+
final aValue = getField(a);
|
|
116
|
+
final bValue = getField(b);
|
|
117
|
+
return ascending
|
|
118
|
+
? Comparable.compare(aValue, bValue)
|
|
119
|
+
: Comparable.compare(bValue, aValue);
|
|
120
|
+
});
|
|
121
|
+
notifyListeners();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
void updateSelectedDesserts(RestorableDessertSelections selectedRows) {
|
|
125
|
+
_selectedCount = 0;
|
|
126
|
+
for (var i = 0; i < desserts.length; i += 1) {
|
|
127
|
+
var dessert = desserts[i];
|
|
128
|
+
if (selectedRows.isSelected(i)) {
|
|
129
|
+
dessert.selected = true;
|
|
130
|
+
_selectedCount += 1;
|
|
131
|
+
} else {
|
|
132
|
+
dessert.selected = false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
notifyListeners();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@override
|
|
139
|
+
DataRow2 getRow(int index, [Color? color]) {
|
|
140
|
+
final format = NumberFormat.decimalPercentPattern(
|
|
141
|
+
locale: 'en',
|
|
142
|
+
decimalDigits: 0,
|
|
143
|
+
);
|
|
144
|
+
assert(index >= 0);
|
|
145
|
+
if (index >= desserts.length) throw 'index > _desserts.length';
|
|
146
|
+
final dessert = desserts[index];
|
|
147
|
+
return DataRow2.byIndex(
|
|
148
|
+
index: index,
|
|
149
|
+
selected: dessert.selected,
|
|
150
|
+
color: color != null
|
|
151
|
+
? WidgetStateProperty.all(color)
|
|
152
|
+
: (hasZebraStripes && index.isEven
|
|
153
|
+
? WidgetStateProperty.all(Theme.of(context).highlightColor)
|
|
154
|
+
: null),
|
|
155
|
+
onSelectChanged: (value) {
|
|
156
|
+
if (dessert.selected != value) {
|
|
157
|
+
_selectedCount += value! ? 1 : -1;
|
|
158
|
+
assert(_selectedCount >= 0);
|
|
159
|
+
dessert.selected = value;
|
|
160
|
+
notifyListeners();
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
onTap: hasRowTaps
|
|
164
|
+
? () => _showSnackbar(context, 'Tapped on row ${dessert.name}')
|
|
165
|
+
: null,
|
|
166
|
+
onDoubleTap: hasRowTaps
|
|
167
|
+
? () => _showSnackbar(context, 'Double Tapped on row ${dessert.name}')
|
|
168
|
+
: null,
|
|
169
|
+
onLongPress: hasRowTaps
|
|
170
|
+
? () => _showSnackbar(context, 'Long pressed on row ${dessert.name}')
|
|
171
|
+
: null,
|
|
172
|
+
onSecondaryTap: hasRowTaps
|
|
173
|
+
? () => _showSnackbar(context, 'Right clicked on row ${dessert.name}')
|
|
174
|
+
: null,
|
|
175
|
+
onSecondaryTapDown: hasRowTaps
|
|
176
|
+
? (d) =>
|
|
177
|
+
_showSnackbar(context, 'Right button down on row ${dessert.name}')
|
|
178
|
+
: null,
|
|
179
|
+
specificRowHeight:
|
|
180
|
+
hasRowHeightOverrides && dessert.fat >= 25 ? 100 : null,
|
|
181
|
+
cells: [
|
|
182
|
+
DataCell(Text(dessert.name)),
|
|
183
|
+
DataCell(Text('${dessert.calories}'),
|
|
184
|
+
onTap: () => _showSnackbar(context,
|
|
185
|
+
'Tapped on a cell with "${dessert.calories}"', Colors.red)),
|
|
186
|
+
DataCell(Text(dessert.fat.toStringAsFixed(1))),
|
|
187
|
+
DataCell(Text('${dessert.carbs}')),
|
|
188
|
+
DataCell(Text(dessert.protein.toStringAsFixed(1))),
|
|
189
|
+
DataCell(Text('${dessert.sodium}')),
|
|
190
|
+
DataCell(Text(format.format(dessert.calcium / 100))),
|
|
191
|
+
DataCell(Text(format.format(dessert.iron / 100))),
|
|
192
|
+
],
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@override
|
|
197
|
+
int get rowCount => desserts.length;
|
|
198
|
+
|
|
199
|
+
@override
|
|
200
|
+
bool get isRowCountApproximate => false;
|
|
201
|
+
|
|
202
|
+
@override
|
|
203
|
+
int get selectedRowCount => _selectedCount;
|
|
204
|
+
|
|
205
|
+
void selectAll(bool? checked) {
|
|
206
|
+
for (final dessert in desserts) {
|
|
207
|
+
dessert.selected = checked ?? false;
|
|
208
|
+
}
|
|
209
|
+
_selectedCount = (checked ?? false) ? desserts.length : 0;
|
|
210
|
+
notifyListeners();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// Async datasource for AsynPaginatedDataTabke2 example. Based on AsyncDataTableSource which
|
|
215
|
+
/// is an extension to Flutter's DataTableSource and aimed at solving
|
|
216
|
+
/// saync data fetching scenarious by paginated table (such as using Web API)
|
|
217
|
+
class DessertDataSourceAsync extends AsyncDataTableSource {
|
|
218
|
+
DessertDataSourceAsync() {
|
|
219
|
+
print('DessertDataSourceAsync created');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
DessertDataSourceAsync.empty() {
|
|
223
|
+
_empty = true;
|
|
224
|
+
print('DessertDataSourceAsync.empty created');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
DessertDataSourceAsync.error() {
|
|
228
|
+
_errorCounter = 0;
|
|
229
|
+
print('DessertDataSourceAsync.error created');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
bool _empty = false;
|
|
233
|
+
int? _errorCounter;
|
|
234
|
+
|
|
235
|
+
RangeValues? _caloriesFilter;
|
|
236
|
+
|
|
237
|
+
RangeValues? get caloriesFilter => _caloriesFilter;
|
|
238
|
+
set caloriesFilter(RangeValues? calories) {
|
|
239
|
+
_caloriesFilter = calories;
|
|
240
|
+
refreshDatasource();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
final DesertsFakeWebService _repo = DesertsFakeWebService();
|
|
244
|
+
|
|
245
|
+
String _sortColumn = "name";
|
|
246
|
+
bool _sortAscending = true;
|
|
247
|
+
|
|
248
|
+
void sort(String columnName, bool ascending) {
|
|
249
|
+
_sortColumn = columnName;
|
|
250
|
+
_sortAscending = ascending;
|
|
251
|
+
refreshDatasource();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
Future<int> getTotalRecords() {
|
|
255
|
+
return Future<int>.delayed(
|
|
256
|
+
const Duration(milliseconds: 0), () => _empty ? 0 : _dessertsX3.length);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
@override
|
|
260
|
+
Future<AsyncRowsResponse> getRows(int startIndex, int count) async {
|
|
261
|
+
print('getRows($startIndex, $count)');
|
|
262
|
+
if (_errorCounter != null) {
|
|
263
|
+
_errorCounter = _errorCounter! + 1;
|
|
264
|
+
|
|
265
|
+
if (_errorCounter! % 2 == 1) {
|
|
266
|
+
await Future.delayed(const Duration(milliseconds: 1000));
|
|
267
|
+
throw 'Error #${((_errorCounter! - 1) / 2).round() + 1} has occured';
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
final format = NumberFormat.decimalPercentPattern(
|
|
272
|
+
locale: 'en',
|
|
273
|
+
decimalDigits: 0,
|
|
274
|
+
);
|
|
275
|
+
assert(startIndex >= 0);
|
|
276
|
+
|
|
277
|
+
// List returned will be empty is there're fewer items than startingAt
|
|
278
|
+
var x = _empty
|
|
279
|
+
? await Future.delayed(const Duration(milliseconds: 2000),
|
|
280
|
+
() => DesertsFakeWebServiceResponse(0, []))
|
|
281
|
+
: await _repo.getData(
|
|
282
|
+
startIndex, count, _caloriesFilter, _sortColumn, _sortAscending);
|
|
283
|
+
|
|
284
|
+
var r = AsyncRowsResponse(
|
|
285
|
+
x.totalRecords,
|
|
286
|
+
x.data.map((dessert) {
|
|
287
|
+
return DataRow(
|
|
288
|
+
key: ValueKey<int>(dessert.id),
|
|
289
|
+
//selected: dessert.selected,
|
|
290
|
+
onSelectChanged: (value) {
|
|
291
|
+
if (value != null) {
|
|
292
|
+
setRowSelection(ValueKey<int>(dessert.id), value);
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
cells: [
|
|
296
|
+
DataCell(Text(dessert.name)),
|
|
297
|
+
DataCell(Text('${dessert.calories}')),
|
|
298
|
+
DataCell(Text(dessert.fat.toStringAsFixed(1))),
|
|
299
|
+
DataCell(Text('${dessert.carbs}')),
|
|
300
|
+
DataCell(Text(dessert.protein.toStringAsFixed(1))),
|
|
301
|
+
DataCell(Text('${dessert.sodium}')),
|
|
302
|
+
DataCell(Text(format.format(dessert.calcium / 100))),
|
|
303
|
+
DataCell(Text(format.format(dessert.iron / 100))),
|
|
304
|
+
],
|
|
305
|
+
);
|
|
306
|
+
}).toList());
|
|
307
|
+
|
|
308
|
+
return r;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
class DesertsFakeWebServiceResponse {
|
|
313
|
+
DesertsFakeWebServiceResponse(this.totalRecords, this.data);
|
|
314
|
+
|
|
315
|
+
/// THe total ammount of records on the server, e.g. 100
|
|
316
|
+
final int totalRecords;
|
|
317
|
+
|
|
318
|
+
/// One page, e.g. 10 reocrds
|
|
319
|
+
final List<Dessert> data;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
class DesertsFakeWebService {
|
|
323
|
+
int Function(Dessert, Dessert)? _getComparisonFunction(
|
|
324
|
+
String column, bool ascending) {
|
|
325
|
+
var coef = ascending ? 1 : -1;
|
|
326
|
+
switch (column) {
|
|
327
|
+
case 'name':
|
|
328
|
+
return (Dessert d1, Dessert d2) => coef * d1.name.compareTo(d2.name);
|
|
329
|
+
case 'calories':
|
|
330
|
+
return (Dessert d1, Dessert d2) => coef * (d1.calories - d2.calories);
|
|
331
|
+
case 'fat':
|
|
332
|
+
return (Dessert d1, Dessert d2) => coef * (d1.fat - d2.fat).round();
|
|
333
|
+
case 'carbs':
|
|
334
|
+
return (Dessert d1, Dessert d2) => coef * (d1.carbs - d2.carbs);
|
|
335
|
+
case 'protein':
|
|
336
|
+
return (Dessert d1, Dessert d2) =>
|
|
337
|
+
coef * (d1.protein - d2.protein).round();
|
|
338
|
+
case 'sodium':
|
|
339
|
+
return (Dessert d1, Dessert d2) => coef * (d1.sodium - d2.sodium);
|
|
340
|
+
case 'calcium':
|
|
341
|
+
return (Dessert d1, Dessert d2) => coef * (d1.calcium - d2.calcium);
|
|
342
|
+
case 'iron':
|
|
343
|
+
return (Dessert d1, Dessert d2) => coef * (d1.iron - d2.iron);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
Future<DesertsFakeWebServiceResponse> getData(int startingAt, int count,
|
|
350
|
+
RangeValues? caloriesFilter, String sortedBy, bool sortedAsc) async {
|
|
351
|
+
return Future.delayed(
|
|
352
|
+
Duration(
|
|
353
|
+
milliseconds: startingAt == 0
|
|
354
|
+
? 2650
|
|
355
|
+
: startingAt < 20
|
|
356
|
+
? 2000
|
|
357
|
+
: 400), () {
|
|
358
|
+
var result = _dessertsX3;
|
|
359
|
+
|
|
360
|
+
if (caloriesFilter != null) {
|
|
361
|
+
result = result
|
|
362
|
+
.where((e) =>
|
|
363
|
+
e.calories >= caloriesFilter.start &&
|
|
364
|
+
e.calories <= caloriesFilter.end)
|
|
365
|
+
.toList();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
result.sort(_getComparisonFunction(sortedBy, sortedAsc));
|
|
369
|
+
return DesertsFakeWebServiceResponse(
|
|
370
|
+
result.length, result.skip(startingAt).take(count).toList());
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
int _selectedCount = 0;
|
|
376
|
+
|
|
377
|
+
List<Dessert> _desserts = <Dessert>[
|
|
378
|
+
Dessert(
|
|
379
|
+
'Frozen Yogurt',
|
|
380
|
+
159,
|
|
381
|
+
6.0,
|
|
382
|
+
24,
|
|
383
|
+
4.0,
|
|
384
|
+
87,
|
|
385
|
+
14,
|
|
386
|
+
1,
|
|
387
|
+
),
|
|
388
|
+
Dessert(
|
|
389
|
+
'Ice Cream Sandwich',
|
|
390
|
+
237,
|
|
391
|
+
9.0,
|
|
392
|
+
37,
|
|
393
|
+
4.3,
|
|
394
|
+
129,
|
|
395
|
+
8,
|
|
396
|
+
1,
|
|
397
|
+
),
|
|
398
|
+
Dessert(
|
|
399
|
+
'Eclair',
|
|
400
|
+
262,
|
|
401
|
+
16.0,
|
|
402
|
+
24,
|
|
403
|
+
6.0,
|
|
404
|
+
337,
|
|
405
|
+
6,
|
|
406
|
+
7,
|
|
407
|
+
),
|
|
408
|
+
Dessert(
|
|
409
|
+
'Cupcake',
|
|
410
|
+
305,
|
|
411
|
+
3.7,
|
|
412
|
+
67,
|
|
413
|
+
4.3,
|
|
414
|
+
413,
|
|
415
|
+
3,
|
|
416
|
+
8,
|
|
417
|
+
),
|
|
418
|
+
Dessert(
|
|
419
|
+
'Gingerbread',
|
|
420
|
+
356,
|
|
421
|
+
16.0,
|
|
422
|
+
49,
|
|
423
|
+
3.9,
|
|
424
|
+
327,
|
|
425
|
+
7,
|
|
426
|
+
16,
|
|
427
|
+
),
|
|
428
|
+
Dessert(
|
|
429
|
+
'Jelly Bean',
|
|
430
|
+
375,
|
|
431
|
+
0.0,
|
|
432
|
+
94,
|
|
433
|
+
0.0,
|
|
434
|
+
50,
|
|
435
|
+
0,
|
|
436
|
+
0,
|
|
437
|
+
),
|
|
438
|
+
Dessert(
|
|
439
|
+
'Lollipop',
|
|
440
|
+
392,
|
|
441
|
+
0.2,
|
|
442
|
+
98,
|
|
443
|
+
0.0,
|
|
444
|
+
38,
|
|
445
|
+
0,
|
|
446
|
+
2,
|
|
447
|
+
),
|
|
448
|
+
Dessert(
|
|
449
|
+
'Honeycomb',
|
|
450
|
+
408,
|
|
451
|
+
3.2,
|
|
452
|
+
87,
|
|
453
|
+
6.5,
|
|
454
|
+
562,
|
|
455
|
+
0,
|
|
456
|
+
45,
|
|
457
|
+
),
|
|
458
|
+
Dessert(
|
|
459
|
+
'Donut',
|
|
460
|
+
452,
|
|
461
|
+
25.0,
|
|
462
|
+
51,
|
|
463
|
+
4.9,
|
|
464
|
+
326,
|
|
465
|
+
2,
|
|
466
|
+
22,
|
|
467
|
+
),
|
|
468
|
+
Dessert(
|
|
469
|
+
'Apple Pie',
|
|
470
|
+
518,
|
|
471
|
+
26.0,
|
|
472
|
+
65,
|
|
473
|
+
7.0,
|
|
474
|
+
54,
|
|
475
|
+
12,
|
|
476
|
+
6,
|
|
477
|
+
),
|
|
478
|
+
Dessert(
|
|
479
|
+
'Frozen Yougurt with sugar',
|
|
480
|
+
168,
|
|
481
|
+
6.0,
|
|
482
|
+
26,
|
|
483
|
+
4.0,
|
|
484
|
+
87,
|
|
485
|
+
14,
|
|
486
|
+
1,
|
|
487
|
+
),
|
|
488
|
+
Dessert(
|
|
489
|
+
'Ice Cream Sandwich with sugar',
|
|
490
|
+
246,
|
|
491
|
+
9.0,
|
|
492
|
+
39,
|
|
493
|
+
4.3,
|
|
494
|
+
129,
|
|
495
|
+
8,
|
|
496
|
+
1,
|
|
497
|
+
),
|
|
498
|
+
Dessert(
|
|
499
|
+
'Eclair with sugar',
|
|
500
|
+
271,
|
|
501
|
+
16.0,
|
|
502
|
+
26,
|
|
503
|
+
6.0,
|
|
504
|
+
337,
|
|
505
|
+
6,
|
|
506
|
+
7,
|
|
507
|
+
),
|
|
508
|
+
Dessert(
|
|
509
|
+
'Cupcake with sugar',
|
|
510
|
+
314,
|
|
511
|
+
3.7,
|
|
512
|
+
69,
|
|
513
|
+
4.3,
|
|
514
|
+
413,
|
|
515
|
+
3,
|
|
516
|
+
8,
|
|
517
|
+
),
|
|
518
|
+
Dessert(
|
|
519
|
+
'Gingerbread with sugar',
|
|
520
|
+
345,
|
|
521
|
+
16.0,
|
|
522
|
+
51,
|
|
523
|
+
3.9,
|
|
524
|
+
327,
|
|
525
|
+
7,
|
|
526
|
+
16,
|
|
527
|
+
),
|
|
528
|
+
Dessert(
|
|
529
|
+
'Jelly Bean with sugar',
|
|
530
|
+
364,
|
|
531
|
+
0.0,
|
|
532
|
+
96,
|
|
533
|
+
0.0,
|
|
534
|
+
50,
|
|
535
|
+
0,
|
|
536
|
+
0,
|
|
537
|
+
),
|
|
538
|
+
Dessert(
|
|
539
|
+
'Lollipop with sugar',
|
|
540
|
+
401,
|
|
541
|
+
0.2,
|
|
542
|
+
100,
|
|
543
|
+
0.0,
|
|
544
|
+
38,
|
|
545
|
+
0,
|
|
546
|
+
2,
|
|
547
|
+
),
|
|
548
|
+
Dessert(
|
|
549
|
+
'Honeycomd with sugar',
|
|
550
|
+
417,
|
|
551
|
+
3.2,
|
|
552
|
+
89,
|
|
553
|
+
6.5,
|
|
554
|
+
562,
|
|
555
|
+
0,
|
|
556
|
+
45,
|
|
557
|
+
),
|
|
558
|
+
Dessert(
|
|
559
|
+
'Donut with sugar',
|
|
560
|
+
461,
|
|
561
|
+
25.0,
|
|
562
|
+
53,
|
|
563
|
+
4.9,
|
|
564
|
+
326,
|
|
565
|
+
2,
|
|
566
|
+
22,
|
|
567
|
+
),
|
|
568
|
+
Dessert(
|
|
569
|
+
'Apple pie with sugar',
|
|
570
|
+
527,
|
|
571
|
+
26.0,
|
|
572
|
+
67,
|
|
573
|
+
7.0,
|
|
574
|
+
54,
|
|
575
|
+
12,
|
|
576
|
+
6,
|
|
577
|
+
),
|
|
578
|
+
Dessert(
|
|
579
|
+
'Forzen yougurt with honey',
|
|
580
|
+
223,
|
|
581
|
+
6.0,
|
|
582
|
+
36,
|
|
583
|
+
4.0,
|
|
584
|
+
87,
|
|
585
|
+
14,
|
|
586
|
+
1,
|
|
587
|
+
),
|
|
588
|
+
Dessert(
|
|
589
|
+
'Ice Cream Sandwich with honey',
|
|
590
|
+
301,
|
|
591
|
+
9.0,
|
|
592
|
+
49,
|
|
593
|
+
4.3,
|
|
594
|
+
129,
|
|
595
|
+
8,
|
|
596
|
+
1,
|
|
597
|
+
),
|
|
598
|
+
Dessert(
|
|
599
|
+
'Eclair with honey',
|
|
600
|
+
326,
|
|
601
|
+
16.0,
|
|
602
|
+
36,
|
|
603
|
+
6.0,
|
|
604
|
+
337,
|
|
605
|
+
6,
|
|
606
|
+
7,
|
|
607
|
+
),
|
|
608
|
+
Dessert(
|
|
609
|
+
'Cupcake with honey',
|
|
610
|
+
369,
|
|
611
|
+
3.7,
|
|
612
|
+
79,
|
|
613
|
+
4.3,
|
|
614
|
+
413,
|
|
615
|
+
3,
|
|
616
|
+
8,
|
|
617
|
+
),
|
|
618
|
+
Dessert(
|
|
619
|
+
'Gignerbread with hone',
|
|
620
|
+
420,
|
|
621
|
+
16.0,
|
|
622
|
+
61,
|
|
623
|
+
3.9,
|
|
624
|
+
327,
|
|
625
|
+
7,
|
|
626
|
+
16,
|
|
627
|
+
),
|
|
628
|
+
Dessert(
|
|
629
|
+
'Jelly Bean with honey',
|
|
630
|
+
439,
|
|
631
|
+
0.0,
|
|
632
|
+
106,
|
|
633
|
+
0.0,
|
|
634
|
+
50,
|
|
635
|
+
0,
|
|
636
|
+
0,
|
|
637
|
+
),
|
|
638
|
+
Dessert(
|
|
639
|
+
'Lollipop with honey',
|
|
640
|
+
456,
|
|
641
|
+
0.2,
|
|
642
|
+
110,
|
|
643
|
+
0.0,
|
|
644
|
+
38,
|
|
645
|
+
0,
|
|
646
|
+
2,
|
|
647
|
+
),
|
|
648
|
+
Dessert(
|
|
649
|
+
'Honeycomd with honey',
|
|
650
|
+
472,
|
|
651
|
+
3.2,
|
|
652
|
+
99,
|
|
653
|
+
6.5,
|
|
654
|
+
562,
|
|
655
|
+
0,
|
|
656
|
+
45,
|
|
657
|
+
),
|
|
658
|
+
Dessert(
|
|
659
|
+
'Donut with honey',
|
|
660
|
+
516,
|
|
661
|
+
25.0,
|
|
662
|
+
63,
|
|
663
|
+
4.9,
|
|
664
|
+
326,
|
|
665
|
+
2,
|
|
666
|
+
22,
|
|
667
|
+
),
|
|
668
|
+
Dessert(
|
|
669
|
+
'Apple pie with honey',
|
|
670
|
+
582,
|
|
671
|
+
26.0,
|
|
672
|
+
77,
|
|
673
|
+
7.0,
|
|
674
|
+
54,
|
|
675
|
+
12,
|
|
676
|
+
6,
|
|
677
|
+
),
|
|
678
|
+
];
|
|
679
|
+
|
|
680
|
+
List<Dessert> _dessertsX3 = _desserts.toList()
|
|
681
|
+
..addAll(_desserts.map((i) => Dessert('${i.name} x2', i.calories, i.fat,
|
|
682
|
+
i.carbs, i.protein, i.sodium, i.calcium, i.iron)))
|
|
683
|
+
..addAll(_desserts.map((i) => Dessert('${i.name} x3', i.calories, i.fat,
|
|
684
|
+
i.carbs, i.protein, i.sodium, i.calcium, i.iron)));
|
|
685
|
+
|
|
686
|
+
_showSnackbar(BuildContext context, String text, [Color? color]) {
|
|
687
|
+
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
|
688
|
+
backgroundColor: color,
|
|
689
|
+
duration: const Duration(seconds: 1),
|
|
690
|
+
content: Text(text),
|
|
691
|
+
));
|
|
692
|
+
}
|