• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Duit-Foundation / flutter_duit / 14680368801

26 Apr 2025 10:29AM UTC coverage: 46.618% (-1.1%) from 47.732%
14680368801

push

github

web-flow
patch: added missing props for ElevatedButton attributes (#269)

9 of 173 new or added lines in 3 files covered. (5.2%)

3081 of 6609 relevant lines covered (46.62%)

6.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

21.73
/lib/src/utils/params_mapper.dart
1
import 'dart:convert';
2
import 'dart:typed_data';
3
import 'dart:ui';
4

5
import 'package:flutter/gestures.dart';
6
import 'package:flutter/material.dart';
7
import 'package:flutter/rendering.dart';
8
import 'package:flutter_duit/src/utils/index.dart';
9

10
/// A utility class for mapping parameter values to their corresponding Flutter widget properties.
11
final class AttributeValueMapper {
12
  //<editor-fold desc="Text">
13
  static TextSpan toTextSpan(JSONObject? json) {
×
14
    assert(json != null, "TextSpan json cannot be null");
×
15

16
    final children = json!['children'];
×
17

18
    List<InlineSpan> spanChildren = [];
×
19

20
    if (children != null) {
21
      for (final child in children) {
×
22
        spanChildren.add(AttributeValueMapper.toTextSpan(child));
×
23
      }
24
    }
25

26
    return TextSpan(
×
27
      text: json['text'],
×
28
      children: spanChildren.isNotEmpty ? spanChildren : null,
×
29
      style: AttributeValueMapper.toTextStyle(json['style']),
×
30
      spellOut: json['spellOut'],
×
31
    );
32
  }
33

34
  static TextBaseline? toTextBaseline(String? value) {
×
35
    if (value == null) return null;
36

37
    switch (value) {
38
      case "alphabetic":
×
39
        return TextBaseline.alphabetic;
40
      case "ideographic":
×
41
        return TextBaseline.ideographic;
42
    }
43

44
    return null;
45
  }
46

47
  static TextWidthBasis? toTextWidthBasis(String? value) {
×
48
    if (value == null) return null;
49

50
    switch (value) {
51
      case "parent":
×
52
        return TextWidthBasis.parent;
53
      case "longestLine":
×
54
        return TextWidthBasis.longestLine;
55
    }
56

57
    return null;
58
  }
59

60
  static TextLeadingDistribution toTextLeadingDistribution(
×
61
    String? value,
62
  ) {
63
    if (value == null) return TextLeadingDistribution.proportional;
64

65
    switch (value) {
66
      case "even":
×
67
        return TextLeadingDistribution.even;
68
      case "italic":
×
69
        return TextLeadingDistribution.proportional;
70
    }
71

72
    return TextLeadingDistribution.proportional;
73
  }
74

75
  static TextHeightBehavior? toTextHeightBehavior(JSONObject? value) {
×
76
    if (value == null) return null;
77

78
    return TextHeightBehavior(
×
79
      applyHeightToFirstAscent: value['applyHeightToFirstAscent'] ?? true,
×
80
      applyHeightToLastDescent: value['applyHeightToLastDescent'] ?? true,
×
81
      leadingDistribution:
82
          toTextLeadingDistribution(value['leadingDistribution']),
×
83
    );
84
  }
85

86
  static TextScaler toTextScaler(JSONObject? value) {
×
87
    if (value == null) return TextScaler.noScaling;
88

89
    return TextScaler.linear(NumUtils.toDoubleWithNullReplacement(
×
90
      value['textScaleFactor'],
×
91
      1,
92
    ));
93
  }
94

95
  static StrutStyle? toStrutStyle(JSONObject? value) {
×
96
    if (value == null) return null;
97

98
    return StrutStyle(
×
99
      fontFamily: value['fontFamily'],
×
100
      fontSize: NumUtils.toDouble(value['fontSize']),
×
101
      height: NumUtils.toDouble(value['height']),
×
102
      leading: NumUtils.toDouble(value['leading']),
×
103
      fontWeight: toFontWeight(value['fontWeight']),
×
104
      fontStyle: toFontStyle(value['fontStyle']),
×
105
      forceStrutHeight: value['forceStrutHeight'],
×
106
      debugLabel: value['debugLabel'],
×
107
    );
108
  }
109

110
  static FontStyle? toFontStyle(String? value) {
×
111
    if (value == null) return null;
112

113
    switch (value) {
114
      case "normal":
×
115
        return FontStyle.normal;
116
      case "italic":
×
117
        return FontStyle.italic;
118
    }
119

120
    return null;
121
  }
122

123
  /// Converts a string value to a [TextAlign] value.
124
  ///
125
  /// Returns the corresponding [TextAlign] value for the given string [value].
126
  /// If [value] is `null`, returns `null`.
127
  static TextAlign? toTextAlign(String? value) {
38✔
128
    if (value == null) return null;
129

130
    switch (value) {
131
      case "left":
×
132
        return TextAlign.left;
133
      case "right":
×
134
        return TextAlign.right;
135
      case "center":
×
136
        return TextAlign.center;
137
      case "justify":
×
138
        return TextAlign.justify;
139
      case "end":
×
140
        return TextAlign.end;
141
      case "start":
×
142
        return TextAlign.start;
143
    }
144

145
    return null;
146
  }
147

148
  static TextDecoration? toTextDecoration(String? value) {
12✔
149
    if (value == null) return null;
150

151
    switch (value) {
152
      case 'none':
×
153
        return TextDecoration.none;
154
      case 'underline':
×
155
        return TextDecoration.underline;
156
      case 'overline':
×
157
        return TextDecoration.overline;
158
      case 'lineThrough':
×
159
        return TextDecoration.lineThrough;
160
      default:
161
        return null;
162
    }
163
  }
164

165
  static TextDecorationStyle? toTextDecorationStyle(String? value) {
12✔
166
    if (value == null) return null;
167

168
    switch (value) {
169
      case 'solid':
×
170
        return TextDecorationStyle.solid;
171
      case 'double':
×
172
        return TextDecorationStyle.double;
173
      case 'dotted':
×
174
        return TextDecorationStyle.dotted;
175
      case 'dashed':
×
176
        return TextDecorationStyle.dashed;
177
      case 'wavy':
×
178
        return TextDecorationStyle.wavy;
179
      default:
180
        return null;
181
    }
182
  }
183

184
  /// Converts an integer value to a [FontWeight] value.
185
  ///
186
  /// Returns the corresponding [FontWeight] value for the given integer [value].
187
  /// If [value] is `null`, returns `null`.
188
  static FontWeight? toFontWeight(int? value) {
12✔
189
    if (value == null) return null;
190

191
    switch (value) {
192
      case 100:
12✔
193
        return FontWeight.w100;
194
      case 200:
12✔
195
        return FontWeight.w200;
196
      case 300:
12✔
197
        return FontWeight.w300;
198
      case 400:
12✔
199
        return FontWeight.w400;
200
      case 500:
10✔
201
        return FontWeight.w500;
202
      case 600:
8✔
203
        return FontWeight.w600;
204
      case 700:
8✔
205
        return FontWeight.w700;
206
      case 800:
2✔
207
        return FontWeight.w800;
208
      case 900:
×
209
        return FontWeight.w900;
210
    }
211

212
    return null;
213
  }
214

215
  /// Converts a string value to a [TextOverflow] value.
216
  ///
217
  /// Returns the corresponding [TextOverflow] value for the given string [value].
218
  /// If [value] is `null`, returns `null`.
219
  static TextOverflow? toTextOverflow(String? value) {
38✔
220
    if (value == null) return null;
221

222
    switch (value) {
223
      case "fade":
×
224
        return TextOverflow.fade;
225
      case "ellipsis":
×
226
        return TextOverflow.ellipsis;
227
      case "clip":
×
228
        return TextOverflow.clip;
229
      case "visible":
×
230
        return TextOverflow.visible;
231
    }
232

233
    return null;
234
  }
235

236
  /// Converts a map of style values to a [TextStyle] object.
237
  ///
238
  /// Returns a [TextStyle] object with the properties specified in the given [styleMap].
239
  /// The [styleMap] should be a map with the following keys:
240
  /// - 'fontSize': the font size as a double value.
241
  /// - 'fontWeight': the font weight as an integer value.
242
  /// - 'fontStyle': the font style as a [FontStyle] value (e.g., 'normal', 'italic').
243
  /// - 'letterSpacing': the letter spacing as a double value.
244
  /// - 'wordSpacing': the word spacing as a double value.
245
  /// - 'color': the color as a [Color] value.
246
  /// - 'backgroundColor': the background color as a [Color] value.
247
  /// - 'decoration': the text decoration as a [TextDecoration] value.
248
  /// - 'decorationColor': the decoration color as a [Color] value.
249
  /// - 'decorationStyle': the decoration style as a [TextDecorationStyle] value.
250
  /// - 'decorationThickness': the decoration thickness as a double value.
251
  ///
252
  /// If any of the properties are missing or invalid, they will be ignored.
253
  /// If [styleMap] is `null` or empty, returns `null`.
254
  static TextStyle? toTextStyle(dynamic json) {
40✔
255
    if (json == null) return null;
256

257
    if (json is TextStyle) return json;
12✔
258

259
    final size = json["fontSize"] as num?;
12✔
260

261
    return TextStyle(
12✔
262
      color: ColorUtils.tryParseColor(json["color"]),
24✔
263
      fontFamily: json["fontFamily"],
12✔
264
      fontWeight: toFontWeight(json["fontWeight"]),
24✔
265
      fontSize: size?.toDouble(),
12✔
266
      letterSpacing: NumUtils.toDouble(json["letterSpacing"]),
24✔
267
      wordSpacing: NumUtils.toDouble(json["wordSpacing"]),
24✔
268
      height: NumUtils.toDouble(json["height"]),
24✔
269
      decoration: toTextDecoration(json["decoration"]),
24✔
270
      decorationColor:
271
          ColorUtils.tryParseNullableColor(json['decorationColor']),
24✔
272
      decorationStyle: toTextDecorationStyle(json['decorationStyle']),
24✔
273
      decorationThickness: NumUtils.toDouble(json['decorationThickness']),
24✔
274
    );
275
  }
276

277
  /// Converts a string value to a [TextDirection] value.
278
  ///
279
  /// Returns the corresponding [TextDirection] value for the given string [value].
280
  /// If [value] is `null`, returns `null`.
281
  /// If [value] is not a valid text direction, returns [TextDirection.ltr] as the default.
282
  static TextDirection? toTextDirection(String? value) {
42✔
283
    if (value == null) return null;
284

285
    switch (value) {
286
      case "rtl":
×
287
        return TextDirection.rtl;
288
      case "ltr":
×
289
        return TextDirection.ltr;
290
    }
291

292
    return null;
293
  }
294

295
  //</editor-fold>
296

297
  //<editor-fold desc="Flex and container props">
298
  static Size toSize(dynamic json) {
2✔
299
    if (json == null) return Size.zero;
300

301
    if (json is Size) return json;
2✔
302

303
    return Size(
2✔
304
      NumUtils.toDoubleWithNullReplacement(
2✔
305
        json["width"],
2✔
306
        double.infinity,
307
      ),
308
      NumUtils.toDoubleWithNullReplacement(
2✔
309
        json["height"],
2✔
310
        double.infinity,
311
      ),
312
    );
313
  }
314

315
  static Axis toAxis(String? value, [Axis? defaultValue]) {
8✔
316
    if (value == null) return defaultValue ?? Axis.vertical;
317

318
    switch (value) {
319
      case "horizontal":
×
320
        return Axis.horizontal;
321
      case "vertical":
×
322
        return Axis.vertical;
323
    }
324

325
    return Axis.vertical;
326
  }
327

328
  static WrapCrossAlignment? toWrapCrossAlignment(String? value) {
×
329
    if (value == null) return null;
330

331
    switch (value) {
332
      case "start":
×
333
        return WrapCrossAlignment.start;
334
      case "end":
×
335
        return WrapCrossAlignment.end;
336
      case "center":
×
337
        return WrapCrossAlignment.center;
338
    }
339

340
    return null;
341
  }
342

343
  static WrapAlignment? toWrapAlignment(String? value) {
×
344
    if (value == null) return null;
345
    switch (value) {
346
      case "start":
×
347
        return WrapAlignment.start;
348
      case "end":
×
349
        return WrapAlignment.end;
350
      case "center":
×
351
        return WrapAlignment.center;
352
      case "spaceBetween":
×
353
        return WrapAlignment.spaceBetween;
354
      case "spaceAround":
×
355
        return WrapAlignment.spaceAround;
356
      case "spaceEvenly":
×
357
        return WrapAlignment.spaceEvenly;
358
    }
359

360
    return null;
361
  }
362

363
  /// Converts a map of constraint values to a [BoxConstraints] object.
364
  ///
365
  /// Returns a [BoxConstraints] object with the properties specified in the given [constraintsMap].
366
  /// The [constraintsMap] should be a map with the following keys:
367
  /// - 'minWidth': the minimum width as a double value.
368
  /// - 'maxWidth': the maximum width as a double value.
369
  /// - 'minHeight': the minimum height as a double value.
370
  /// - 'maxHeight': the maximum height as a double value.
371
  ///
372
  /// If any of the properties are missing or invalid, they will be ignored.
373
  /// If [constraintsMap] is `null` or empty, returns `null`.
374
  static BoxConstraints toBoxConstraints(dynamic json) {
32✔
375
    if (json == null) return const BoxConstraints();
376

377
    if (json is BoxConstraints) return json;
4✔
378

379
    return BoxConstraints(
4✔
380
      minWidth: NumUtils.toDoubleWithNullReplacement(
4✔
381
        json["minWidth"],
4✔
382
        0.0,
383
      ),
384
      maxWidth: NumUtils.toDoubleWithNullReplacement(
4✔
385
        json["maxWidth"],
4✔
386
        double.infinity,
387
      ),
388
      minHeight: NumUtils.toDoubleWithNullReplacement(
4✔
389
        json["minHeight"],
4✔
390
        0.0,
391
      ),
392
      maxHeight: NumUtils.toDoubleWithNullReplacement(
4✔
393
        json["maxHeight"],
4✔
394
        double.infinity,
395
      ),
396
    );
397
  }
398

399
  /// Converts a string value to a [StackFit] value.
400
  ///
401
  /// Returns the corresponding [StackFit] value for the given string [value].
402
  /// If [value] is `null`, returns `null`.
403
  /// If [value] is not a valid stack fit, returns [StackFit.loose] as the default.
404
  static StackFit toStackFit(String? value) {
4✔
405
    if (value == null) return StackFit.loose;
406

407
    switch (value) {
408
      case "expand":
×
409
        return StackFit.expand;
410
      case "passthrough":
×
411
        return StackFit.passthrough;
412
      case "loose":
×
413
        return StackFit.loose;
414
    }
415

416
    return StackFit.loose;
417
  }
418

419
  static OverflowBoxFit toOverflowBoxFit(String? value) {
×
420
    if (value == null) return OverflowBoxFit.max;
421

422
    switch (value) {
423
      case "max":
×
424
        return OverflowBoxFit.max;
425
      case "deferToChild":
×
426
        return OverflowBoxFit.deferToChild;
427
    }
428

429
    return OverflowBoxFit.max;
430
  }
431

432
  /// Converts a string value to an [Alignment] value.
433
  ///
434
  /// Returns the corresponding [Alignment] value for the given string [value].
435
  /// If [value] is `null`, returns `null`.
436
  /// If [value] is not a valid alignment, returns [Alignment.center] as the default.
437
  static Alignment toAlignment(dynamic value, [Alignment? defaultValue]) {
12✔
438
    if (value == null) return defaultValue ?? Alignment.topLeft;
439

440
    if (value is Alignment) return value;
6✔
441

442
    switch (value) {
443
      case "topCenter":
6✔
444
        return Alignment.topCenter;
445
      case "topLeft":
6✔
446
        return Alignment.topLeft;
447
      case "topRight":
6✔
448
        return Alignment.topRight;
449
      case "bottomCenter":
6✔
450
        return Alignment.bottomCenter;
451
      case "bottomLeft":
6✔
452
        return Alignment.bottomLeft;
453
      case "bottomRight":
6✔
454
        return Alignment.bottomRight;
455
      case "center":
2✔
456
        return Alignment.center;
457
      case "centerLeft":
2✔
458
        return Alignment.centerLeft;
459
      case "centerRight":
2✔
460
        return Alignment.centerRight;
461
    }
462

463
    return defaultValue ?? Alignment.topLeft;
464
  }
465

466
  /// Converts a string value to an [AlignmentDirectional] value.
467
  ///
468
  /// Returns the corresponding [AlignmentDirectional] value for the given string [value].
469
  /// If [value] is `null`, returns `null`.
470
  /// If [value] is not a valid alignment, returns [AlignmentDirectional.centerStart] as the default.
471
  static AlignmentDirectional toAlignmentDirectional(String? value) {
34✔
472
    if (value == null) return AlignmentDirectional.topStart;
473

474
    switch (value) {
475
      case "topStart":
×
476
        return AlignmentDirectional.topStart;
477
      case "topCenter":
×
478
        return AlignmentDirectional.topCenter;
479
      case "topEnd":
×
480
        return AlignmentDirectional.topEnd;
481
      case "bottomCenter":
×
482
        return AlignmentDirectional.bottomCenter;
483
      case "bottomEnd":
×
484
        return AlignmentDirectional.bottomEnd;
485
      case "bottomStart":
×
486
        return AlignmentDirectional.bottomStart;
487
      case "center":
×
488
        return AlignmentDirectional.center;
489
      case "centerStart":
×
490
        return AlignmentDirectional.centerStart;
491
      case "centerEnd":
×
492
        return AlignmentDirectional.centerEnd;
493
    }
494

495
    return AlignmentDirectional.topStart;
496
  }
497

498
  /// Converts a string value to a [MainAxisAlignment] value.
499
  ///
500
  /// Returns the corresponding [MainAxisAlignment] value for the given string [value].
501
  /// If [value] is `null`, returns `null`.
502
  /// If [value] is not a valid main axis alignment, returns [MainAxisAlignment.start] as the default.
503
  static MainAxisAlignment? toMainAxisAlignment(String? value) {
6✔
504
    if (value == null) return null;
505

506
    switch (value) {
507
      case "center":
×
508
        return MainAxisAlignment.center;
509
      case "start":
×
510
        return MainAxisAlignment.start;
511
      case "end":
×
512
        return MainAxisAlignment.end;
513
      case "spaceAround":
×
514
        return MainAxisAlignment.spaceAround;
515
      case "spaceEvenly":
×
516
        return MainAxisAlignment.spaceEvenly;
517
      case "spaceBetween":
×
518
        return MainAxisAlignment.spaceBetween;
519
    }
520

521
    return null;
522
  }
523

524
  /// Converts a string value to a [CrossAxisAlignment] value.
525
  ///
526
  /// Returns the corresponding [CrossAxisAlignment] value for the given string [value].
527
  /// If [value] is `null`, returns `null`.
528
  /// If [value] is not a valid cross axis alignment, returns [CrossAxisAlignment.start] as the default.
529
  static CrossAxisAlignment? toCrossAxisAlignment(String? value) {
6✔
530
    if (value == null) return null;
531

532
    switch (value) {
533
      case "center":
×
534
        return CrossAxisAlignment.center;
535
      case "baseline":
×
536
        return CrossAxisAlignment.baseline;
537
      case "stretch":
×
538
        return CrossAxisAlignment.stretch;
539
      case "start":
×
540
        return CrossAxisAlignment.start;
541
      case "end":
×
542
        return CrossAxisAlignment.end;
543
    }
544

545
    return null;
546
  }
547

548
  /// Converts a string value to a [Clip] value.
549
  ///
550
  /// Returns the corresponding [Clip] value for the given string [value].
551
  /// If [value] is `null`, returns `null`.
552
  /// If [value] is not a valid clip value, returns [Clip.none] as the default.
553
  static Clip toClip(String? value, [Clip? defaultValue]) {
44✔
554
    if (value == null) return defaultValue ?? Clip.hardEdge;
555

556
    switch (value) {
557
      case "hardEdge":
×
558
        return Clip.hardEdge;
559
      case "antiAlias":
×
560
        return Clip.antiAlias;
561
      case "antiAliasWithSaveLayer":
×
562
        return Clip.antiAliasWithSaveLayer;
563
      case "none":
×
564
        return Clip.none;
565
    }
566

567
    return defaultValue ?? Clip.hardEdge;
568
  }
569

570
  static Clip toClipNonNull(String? value, Clip? defaultValue) {
2✔
571
    if (value == null) return defaultValue ?? Clip.hardEdge;
572

573
    switch (value) {
574
      case "hardEdge":
×
575
        return Clip.hardEdge;
576
      case "antiAlias":
×
577
        return Clip.antiAlias;
578
      case "antiAliasWithSaveLayer":
×
579
        return Clip.antiAliasWithSaveLayer;
580
      case "none":
×
581
        return Clip.none;
582
    }
583

584
    return defaultValue ?? Clip.hardEdge;
585
  }
586

587
  /// Converts a string value to a [MainAxisSize] value.
588
  ///
589
  /// Returns the corresponding [MainAxisSize] value for the given string [value].
590
  /// If [value] is `null`, returns `null`.
591
  /// If [value] is not a valid main axis size, returns [MainAxisSize.max] as the default.
592
  static MainAxisSize? toMainAxisSize(String? value) {
6✔
593
    if (value == null) return null;
594

595
    switch (value) {
596
      case "min":
×
597
        return MainAxisSize.min;
598
      case "max":
×
599
        return MainAxisSize.max;
600
    }
601

602
    return null;
603
  }
604

605
  //</editor-fold>
606

607
  //<editor-fold desc="Basic">
608
  static SliderInteraction? toSliderInteraction(String? value) {
×
609
    if (value == null) return null;
610

611
    switch (value) {
612
      case "tapOnly":
×
613
        return SliderInteraction.tapOnly;
614
      case "tapAndSlide":
×
615
        return SliderInteraction.tapAndSlide;
616
      case "slideOnly":
×
617
        return SliderInteraction.slideOnly;
618
      case "slideThumb":
×
619
        return SliderInteraction.slideThumb;
620
    }
621

622
    return null;
623
  }
624

625
  static MaterialTapTargetSize? toMaterialTapTargetSize(String? value) {
×
626
    if (value == null) return null;
627

628
    switch (value) {
629
      case "shrinkWrap":
×
630
        return MaterialTapTargetSize.shrinkWrap;
631
      case "padded":
×
632
        return MaterialTapTargetSize.padded;
633
    }
634

635
    return null;
636
  }
637

638
  /// Converts a string value to a [FilterQuality] value.
639
  ///
640
  /// Returns the corresponding [FilterQuality] value for the given string [value].
641
  /// If [value] is `null`, returns `null`.
642
  /// If [value] is not a valid filter quality, returns [FilterQuality.low] as the default.
643
  static FilterQuality toFilterQuality(String? value) {
6✔
644
    if (value == null) return FilterQuality.low;
645

646
    switch (value) {
647
      case "none":
×
648
        return FilterQuality.none;
649
      case "low":
×
650
        return FilterQuality.low;
651
      case "medium":
×
652
        return FilterQuality.medium;
653
      case "high":
×
654
        return FilterQuality.high;
655
    }
656

657
    return FilterQuality.low;
658
  }
659

660
  /// Converts a string value to an [ImageRepeat] value.
661
  ///
662
  /// Returns the corresponding [ImageRepeat] value for the given string [value].
663
  /// If [value] is `null`, returns `null`.
664
  /// If [value] is not a valid image repeat value, returns [ImageRepeat.noRepeat] as the default.
665
  static ImageRepeat toImageRepeat(String? value) {
×
666
    if (value == null) return ImageRepeat.noRepeat;
667

668
    switch (value) {
669
      case "noRepeat":
×
670
        return ImageRepeat.noRepeat;
671
      case "repeat":
×
672
        return ImageRepeat.repeat;
673
      case "repeatX":
×
674
        return ImageRepeat.repeatX;
675
      case "repeatY":
×
676
        return ImageRepeat.repeatY;
677
    }
678

679
    return ImageRepeat.noRepeat;
680
  }
681

682
  /// Converts a list of integers to a [Uint8List].
683
  ///
684
  /// Returns a [Uint8List] containing the values from the given [list].
685
  /// If [list] is `null`, returns `null`.
686
  /// If any value in the [list] is not a valid unsigned 8-bit integer (0-255), it will be clamped to that range.
687
  static Uint8List toUint8List(dynamic value) {
×
688
    if (value == null) return Uint8List(0);
×
689

690
    final bytes = value["data"];
×
691

692
    if (bytes != null && bytes is List) {
×
693
      final data = List.castFrom<dynamic, int>(bytes);
×
694
      return Uint8List.fromList(data);
×
695
    }
696

697
    if (bytes is String) {
×
698
      return base64Decode(bytes);
×
699
    }
700

701
    return Uint8List(0);
×
702
  }
703

704
  /// Converts a string value to an [ImageType] value.
705
  ///
706
  /// Returns the corresponding [ImageType] value for the given string [value].
707
  /// If [value] is `null`, returns `null`.
708
  /// If [value] is not a valid image type, returns [ImageType.png] as the default.
709
  static ImageType toImageType(String? value) {
×
710
    if (value == null) return ImageType.network;
711

712
    switch (value) {
713
      case "asset":
×
714
        return ImageType.asset;
715
      case "network":
×
716
        return ImageType.network;
717
      case "memory":
×
718
        return ImageType.memory;
719
    }
720

721
    return ImageType.network;
722
  }
723

724
  /// Converts a string value to a [BoxFit] value.
725
  ///
726
  /// Returns the corresponding [BoxFit] value for the given string [value].
727
  /// If [value] is `null`, returns `null`.
728
  /// If [value] is not a valid box fit value, returns [BoxFit.contain] as the default.
729
  static BoxFit? toBoxFit(String? value) {
×
730
    if (value == null) return null;
731

732
    switch (value) {
733
      case "fill":
×
734
        return BoxFit.fill;
735
      case "contain":
×
736
        return BoxFit.contain;
737
      case "cover":
×
738
        return BoxFit.cover;
739
      case "fitHeight":
×
740
        return BoxFit.fitHeight;
741
      case "fitWidth":
×
742
        return BoxFit.fitWidth;
743
      case "none":
×
744
        return BoxFit.none;
745
      case "scaleDown":
×
746
        return BoxFit.scaleDown;
747
    }
748

749
    return null;
750
  }
751

752
  /// Converts a string value to a [BlendMode] value.
753
  ///
754
  /// Returns the corresponding [BlendMode] value for the given string [value].
755
  /// If [value] is `null`, returns `null`.
756
  /// If [value] is not a valid blend mode, returns [BlendMode.srcOver] as the default.
757
  static BlendMode toBlendMode(String? value) {
2✔
758
    if (value == null) return BlendMode.src;
759

760
    switch (value) {
761
      case "clear":
×
762
        return BlendMode.clear;
763
      case "src":
×
764
        return BlendMode.src;
765
      case "dst":
×
766
        return BlendMode.dst;
767
      case "srcOver":
×
768
        return BlendMode.srcOver;
769
      case "dstOver":
×
770
        return BlendMode.dstOver;
771
      case "srcIn":
×
772
        return BlendMode.srcIn;
773
      case "dstIn":
×
774
        return BlendMode.dstIn;
775
      case "srcOut":
×
776
        return BlendMode.srcOut;
777
      case "dstOut":
×
778
        return BlendMode.dstOut;
779
      case "srcATop":
×
780
        return BlendMode.srcATop;
781
      case "dstATop":
×
782
        return BlendMode.dstATop;
783
      case "xor":
×
784
        return BlendMode.xor;
785
      case "plus":
×
786
        return BlendMode.plus;
787
      case "modulate":
×
788
        return BlendMode.modulate;
789
      case "screen":
×
790
        return BlendMode.screen;
791
      case "overlay":
×
792
        return BlendMode.overlay;
793
      case "darken":
×
794
        return BlendMode.darken;
795
      case "lighten":
×
796
        return BlendMode.lighten;
797
      case "colorDodge":
×
798
        return BlendMode.colorDodge;
799
      case "colorBurn":
×
800
        return BlendMode.colorBurn;
801
      case "hardLight":
×
802
        return BlendMode.hardLight;
803
      case "softLight":
×
804
        return BlendMode.softLight;
805
      case "difference":
×
806
        return BlendMode.difference;
807
      case "exclusion":
×
808
        return BlendMode.exclusion;
809
      case "multiply":
×
810
        return BlendMode.multiply;
811
      case "hue":
×
812
        return BlendMode.hue;
813
      case "saturation":
×
814
        return BlendMode.saturation;
815
      case "color":
×
816
        return BlendMode.color;
817
      case "luminosity":
×
818
        return BlendMode.luminosity;
819
    }
820

821
    return BlendMode.src;
822
  }
823

824
  static ImageFilter toImageFilter(dynamic value) {
2✔
825
    if (value == null) return ImageFilter.blur();
2✔
826

827
    if (value is ImageFilter) return value;
2✔
828

829
    if (value is Map) {
2✔
830
      final fType = value["type"];
2✔
831

832
      return switch (fType) {
833
        "blur" || 0 => ImageFilter.blur(
6✔
834
            sigmaX: NumUtils.toDoubleWithNullReplacement(value["radiusX"]),
4✔
835
            sigmaY: NumUtils.toDoubleWithNullReplacement(value["radiusY"]),
4✔
836
            tileMode: value["tileMode"] != null
2✔
837
                ? TileMode.values.byName(value["tileMode"])
4✔
838
                : TileMode.clamp,
839
          ),
840
        "compose" || 1 => () {
6✔
841
            final outerFilter =
842
                value.containsKey("outer") ? value["outer"] : const {};
4✔
843
            final innerFilter =
844
                value.containsKey("inner") ? value["inner"] : const {};
4✔
845

846
            return ImageFilter.compose(
2✔
847
              outer: toImageFilter(outerFilter["filter"]),
4✔
848
              inner: toImageFilter(innerFilter["filter"]),
4✔
849
            );
850
          }(),
2✔
851
        "dilate" || 2 => ImageFilter.dilate(
6✔
852
            radiusX: NumUtils.toDoubleWithNullReplacement(value["radiusX"]),
4✔
853
            radiusY: NumUtils.toDoubleWithNullReplacement(value["radiusY"]),
4✔
854
          ),
855
        "erode" || 3 => ImageFilter.erode(
6✔
856
            radiusX: NumUtils.toDoubleWithNullReplacement(value["radiusX"]),
4✔
857
            radiusY: NumUtils.toDoubleWithNullReplacement(value["radiusY"]),
4✔
858
          ),
859
        "matrix" || 4 => ImageFilter.matrix(
6✔
860
            Float64List.fromList(value["matrix4"] as List<double>),
4✔
861
            filterQuality: toFilterQuality(value["filterQuality"]),
4✔
862
          ),
863
        Object() ||
×
864
        null ||
865
        String() =>
×
866
          throw ArgumentError.value(fType, "type", "Invalid ImageFilter type"),
×
867
      };
868
    }
869

870
    return ImageFilter.blur();
×
871
  }
872

873
  /// Converts a string value to a [VerticalDirection] value.
874
  ///
875
  /// Returns the corresponding [VerticalDirection] value for the given string [value].
876
  /// If [value] is `null`, returns `null`.
877
  /// If [value] is not a valid vertical direction, returns [VerticalDirection.down] as the default.
878
  static VerticalDirection? toVerticalDirection(String? value) {
6✔
879
    if (value == null) return null;
880

881
    switch (value) {
882
      case "up":
×
883
        return VerticalDirection.up;
884
      case "down":
×
885
        return VerticalDirection.down;
886
    }
887

888
    return null;
889
  }
890

891
  //</editor-fold>
892

893
  //<editor-fold desc="Decoration">
894

895
  /// Converts a string value to a [BoxShape] value.
896
  ///
897
  /// Returns the corresponding [BoxShape] value for the given string [value].
898
  /// If [value] is `null`, returns `null`.
899
  /// If [value] is not a valid box shape, returns [BoxShape.rectangle] as the default.
900
  static BoxShape toBoxShape(String? value) {
4✔
901
    if (value == null) return BoxShape.rectangle;
902

903
    switch (value) {
904
      case "circle":
×
905
        return BoxShape.circle;
906
      case "rectangle":
×
907
        return BoxShape.rectangle;
908
    }
909

910
    return BoxShape.rectangle;
911
  }
912

913
  /// Converts a list of two double values to an [Offset].
914
  ///
915
  /// Returns an [Offset] with the given [x] and [y] values.
916
  /// If [list] is `null` or does not contain exactly two elements, returns `null`.
917
  static Offset toOffset(JSONObject? json) {
×
918
    if (json == null) return Offset.zero;
919

920
    return Offset(
×
921
      NumUtils.toDoubleWithNullReplacement(json["dx"], 0.0),
×
922
      NumUtils.toDoubleWithNullReplacement(json["dy"], 0.0),
×
923
    );
924
  }
925

926
  /// Converts a string value to a [BlurStyle] value.
927
  ///
928
  /// Returns the corresponding [BlurStyle] value for the given string [value].
929
  /// If [value] is `null`, returns `null`.
930
  /// If [value] is not a valid blur style, returns [BlurStyle.normal] as the default.
931
  static BlurStyle toBlurStyle(String? value) {
×
932
    if (value == null) return BlurStyle.normal;
933

934
    switch (value) {
935
      case "normal":
×
936
        return BlurStyle.normal;
937
      case "solid":
×
938
        return BlurStyle.solid;
939
      case "outer":
×
940
        return BlurStyle.outer;
941
      case "inner":
×
942
        return BlurStyle.inner;
943
    }
944

945
    return BlurStyle.normal;
946
  }
947

948
  /// Converts a list of values to a [BoxShadow].
949
  ///
950
  /// Returns a [BoxShadow] with the given [color], [offset], [blurRadius], and [spreadRadius].
951
  /// If any of the values are `null` or if the list does not contain exactly four elements,
952
  /// returns `null`.
953
  static List<BoxShadow>? toBoxShadow(dynamic json) {
4✔
954
    if (json == null) return null;
955

956
    List<BoxShadow> arr = [];
×
957
    //
958
    for (var value in json) {
×
959
      final boxShadow = BoxShadow(
×
960
        color: ColorUtils.tryParseColor(value["color"]),
×
961
        blurRadius:
962
            NumUtils.toDoubleWithNullReplacement(value["blurRadius"], 0.0),
×
963
        spreadRadius:
964
            NumUtils.toDoubleWithNullReplacement(value["spreadRadius"], 0.0),
×
965
        offset: toOffset(value["offset"]),
×
966
        blurStyle: toBlurStyle(value["blurStyle"]),
×
967
      );
968
      arr.add(boxShadow);
×
969
    }
970

971
    return arr;
972
  }
973

974
  /// Converts a list of values to a [Gradient].
975
  ///
976
  /// Returns a [Gradient] with the given [type] and [colors].
977
  /// If [type] is `null` or not a valid gradient type, or if [colors] is `null`,
978
  /// returns `null`.
979
  static Gradient? toGradient(JSONObject? json) {
4✔
980
    if (json == null) return null;
981

982
    final stops = json["stops"];
×
983
    final rotationAngle = json["rotationAngle"] as num?;
×
984
    final colors = json["colors"] as List?;
×
985
    final begin = toAlignment(json["begin"]);
×
986
    final end = toAlignment(json["end"]);
×
987

988
    final List<Color> dColors = [];
×
989

990
    if (colors != null) {
991
      for (var color in colors) {
×
992
        dColors.add(ColorUtils.tryParseColor(color));
×
993
      }
994
    }
995

996
    return LinearGradient(
×
997
      begin: begin,
998
      end: end,
999
      colors: dColors,
1000
      stops: stops,
1001
      transform: rotationAngle != null
1002
          ? GradientRotation(rotationAngle.toDouble())
×
1003
          : null,
1004
    );
1005
  }
1006

1007
  /// Converts a map of values to a [Decoration].
1008
  ///
1009
  /// Returns a [Decoration] with the given properties specified in the [map].
1010
  /// If the [map] is `null` or does not contain valid properties, returns `null`.
1011
  /// You can provide any combination of these properties in the [map].
1012
  static Decoration? toDecoration(dynamic json) {
32✔
1013
    if (json == null) return null;
1014

1015
    if (json is Decoration) return json;
4✔
1016

1017
    return BoxDecoration(
4✔
1018
      color: ColorUtils.tryParseNullableColor(json["color"]),
8✔
1019
      border: toBorder(json["border"]),
8✔
1020
      borderRadius: json["borderRadius"] != null
4✔
1021
          ? BorderRadius.circular(
×
1022
              NumUtils.toDoubleWithNullReplacement(json["borderRadius"], 4.0))
×
1023
          : null,
1024
      shape: toBoxShape(json["shape"]),
8✔
1025
      boxShadow: toBoxShadow(json["boxShadow"]),
8✔
1026
      gradient: toGradient(json["gradient"]),
8✔
1027
    );
1028
  }
1029

1030
  /// Converts a map of values to a [Border].
1031
  ///
1032
  /// Returns a [Border] with the given properties specified in the [map].
1033
  static Border? toBorder(dynamic json) {
4✔
1034
    if (json == null) return null;
1035

1036
    if (json is Border) return json;
2✔
1037

1038
    return Border.fromBorderSide(
2✔
1039
      toBorderSide(json),
2✔
1040
    );
1041
  }
1042

1043
  /// Converts a map of values to an [InputDecoration].
1044
  ///
1045
  /// Returns an [InputDecoration] with the given properties specified in the [map].
1046
  static InputDecoration? toInputDecoration(JSONObject? json) {
×
1047
    if (json == null) return null;
1048

1049
    return InputDecoration(
×
1050
      labelText: json["labelText"],
×
1051
      labelStyle: AttributeValueMapper.toTextStyle(json["labelStyle"]),
×
1052
      floatingLabelStyle: AttributeValueMapper.toTextStyle(json["labelStyle"]),
×
1053
      helperText: json["helperText"],
×
1054
      helperMaxLines: json["helperMaxLines"],
×
1055
      helperStyle: AttributeValueMapper.toTextStyle(json["helperStyle"]),
×
1056
      hintText: json["hintText"],
×
1057
      hintStyle: AttributeValueMapper.toTextStyle(json["hintStyle"]),
×
1058
      hintMaxLines: json["hintMaxLines"],
×
1059
      errorText: json["errorText"],
×
1060
      errorMaxLines: json["errorMaxLines"],
×
1061
      errorStyle: AttributeValueMapper.toTextStyle(json["errorStyle"]),
×
1062
      enabledBorder: AttributeValueMapper.toInputBorder(json["enabledBorder"]),
×
1063
      border: AttributeValueMapper.toInputBorder(json["border"]),
×
1064
      errorBorder: AttributeValueMapper.toInputBorder(json["errorBorder"]),
×
1065
      focusedBorder: AttributeValueMapper.toInputBorder(json["focusedBorder"]),
×
1066
      focusedErrorBorder:
1067
          AttributeValueMapper.toInputBorder(json["focusedErrorBorder"]),
×
1068
      enabled: json["enabled"] ?? true,
×
1069
      isCollapsed: json["isCollapsed"] ?? false,
×
1070
      isDense: json["isDense"],
×
1071
      suffixText: json["suffixText"],
×
1072
      suffixStyle: AttributeValueMapper.toTextStyle(json["suffixStyle"]),
×
1073
      prefixText: json["prefixText"],
×
1074
      prefixStyle: AttributeValueMapper.toTextStyle(json["prefixStyle"]),
×
1075
      counterText: json["counterText"],
×
1076
      counterStyle: AttributeValueMapper.toTextStyle(json["prefixStyle"]),
×
1077
      alignLabelWithHint: json["alignLabelWithHint"],
×
1078
      filled: json["filled"],
×
1079
      fillColor: ColorUtils.tryParseColor(json["fillColor"]),
×
1080
      contentPadding: toEdgeInsets(json["contentPadding"]),
×
1081
    );
1082
  }
1083

1084
  /// Converts a string value to a [TextInputType].
1085
  ///
1086
  /// Returns the corresponding [TextInputType] value for the given string [value].
1087
  /// If [value] is `null` or not a valid text input type, returns [TextInputType.text] as the default.
1088
  static TextInputType toTextInputType(String? value) {
×
1089
    if (value == null) return TextInputType.text;
1090
    // TextInputType.
1091
    switch (value) {
1092
      case "text":
×
1093
        return TextInputType.text;
1094
      case "name":
×
1095
        return TextInputType.name;
1096
      case "none":
×
1097
        return TextInputType.none;
1098
      case "url":
×
1099
        return TextInputType.url;
1100
      case "emailAddress":
×
1101
        return TextInputType.emailAddress;
1102
      case "datetime":
×
1103
        return TextInputType.datetime;
1104
      case "streetAddress":
×
1105
        return TextInputType.streetAddress;
1106
      case "number":
×
1107
        return TextInputType.number;
1108
      case "phone":
×
1109
        return TextInputType.phone;
1110
      case "multiline":
×
1111
        return TextInputType.multiline;
1112
    }
1113

1114
    return TextInputType.text;
1115
  }
1116

1117
  /// Converts a string value to a [BorderStyle].
1118
  ///
1119
  /// Returns the corresponding [BorderStyle] value for the given string [value].
1120
  /// If [value] is `null` or not a valid border style, returns [BorderStyle.solid] as the default.
1121
  static BorderStyle toBorderStyle(JSONObject? json) {
2✔
1122
    if (json == null) return BorderStyle.solid;
1123

1124
    switch (json["style"]) {
×
1125
      case "solid":
×
1126
        return BorderStyle.solid;
1127
      case "none":
×
1128
        return BorderStyle.none;
1129
    }
1130

1131
    return BorderStyle.solid;
1132
  }
1133

1134
  /// Converts a string value to a [VisualDensity].
1135
  ///
1136
  /// Returns the corresponding [VisualDensity] value for the given string [value].
1137
  /// If [value] is `null` or not a valid visual density, returns [VisualDensity.standard] as the default.
1138
  static VisualDensity toVisualDensity(JSONObject? json) {
×
1139
    if (json == null) return const VisualDensity();
1140

1141
    return VisualDensity(
×
1142
      horizontal: json["horizontal"],
×
1143
      vertical: json["vertical"],
×
1144
    );
1145
  }
1146

1147
  /// Converts a JSON string to a [MaterialStateProperty<Color>].
1148
  ///
1149
  /// Returns a [MaterialStateProperty<Color>] with colors based on the provided JSON string.
1150
  /// If the [json] is `null`, returns `null`.
1151
  /// The JSON object should contain color values for different states, such as "activeColor" and "defaultColor".
1152
  /// You can customize the implementation to handle different states and map them to the corresponding colors.
1153
  /// This method uses `MaterialStateProperty.resolveWith` to dynamically resolve colors based on the provided states.
1154
  ///
1155
  /// Example JSON string:
1156
  /// ```
1157
  /// {
1158
  ///   "activeColor": 4294901760,
1159
  ///   "defaultColor": 4278190080
1160
  /// }
1161
  /// ```
1162
  ///
1163
  /// Example usage:
1164
  /// ```
1165
  /// final jsonString = '{"activeColor": 4294901760, "defaultColor": 4278190080}';
1166
  /// final mspColor = convertToMSPColor(jsonString);
1167
  /// ```
1168
  static WidgetStateProperty<Color>? toMSPColor(JSONObject? json) {
4✔
1169
    if (json == null) return null;
1170

1171
    return WidgetStateProperty.resolveWith((states) {
×
1172
      if (states.contains(WidgetState.disabled)) {
×
1173
        return ColorUtils.tryParseColor(json[WidgetState.disabled.name]);
×
1174
      }
1175

1176
      if (states.contains(WidgetState.selected)) {
×
1177
        return ColorUtils.tryParseColor(json[WidgetState.selected.name]);
×
1178
      }
1179

1180
      if (states.contains(WidgetState.error)) {
×
1181
        return ColorUtils.tryParseColor(json[WidgetState.error.name]);
×
1182
      }
1183

1184
      if (states.contains(WidgetState.pressed)) {
×
1185
        return ColorUtils.tryParseColor(json[WidgetState.pressed.name]);
×
1186
      }
1187

1188
      if (states.contains(WidgetState.hovered)) {
×
1189
        return ColorUtils.tryParseColor(json[WidgetState.hovered.name]);
×
1190
      }
1191

1192
      if (states.contains(WidgetState.focused)) {
×
1193
        return ColorUtils.tryParseColor(json[WidgetState.focused.name]);
×
1194
      }
1195

1196
      if (states.contains(WidgetState.dragged)) {
×
1197
        return ColorUtils.tryParseColor(json[WidgetState.dragged.name]);
×
1198
      }
1199

1200
      return Colors.black;
1201
    });
1202
  }
1203

1204
  static WidgetStateProperty<double>? toMSPDouble(JSONObject? json) {
×
1205
    if (json == null) return null;
1206

1207
    return WidgetStateProperty.resolveWith((states) {
×
1208
      if (states.contains(WidgetState.disabled)) {
×
1209
        return NumUtils.toDoubleWithNullReplacement(
×
1210
          json[WidgetState.disabled.name],
×
1211
        );
1212
      }
1213

1214
      if (states.contains(WidgetState.selected)) {
×
1215
        return NumUtils.toDoubleWithNullReplacement(
×
1216
          json[WidgetState.selected.name],
×
1217
        );
1218
      }
1219

1220
      if (states.contains(WidgetState.error)) {
×
1221
        return NumUtils.toDoubleWithNullReplacement(
×
1222
          json[WidgetState.error.name],
×
1223
        );
1224
      }
1225

1226
      if (states.contains(WidgetState.pressed)) {
×
1227
        return NumUtils.toDoubleWithNullReplacement(
×
1228
          json[WidgetState.pressed.name],
×
1229
        );
1230
      }
1231

1232
      if (states.contains(WidgetState.hovered)) {
×
1233
        return NumUtils.toDoubleWithNullReplacement(
×
1234
          json[WidgetState.hovered.name],
×
1235
        );
1236
      }
1237

1238
      if (states.contains(WidgetState.focused)) {
×
1239
        return NumUtils.toDoubleWithNullReplacement(
×
1240
          json[WidgetState.focused.name],
×
1241
        );
1242
      }
1243

1244
      if (states.contains(WidgetState.dragged)) {
×
1245
        return NumUtils.toDoubleWithNullReplacement(
×
1246
          json[WidgetState.dragged.name],
×
1247
        );
1248
      }
1249

1250
      return 0;
1251
    });
1252
  }
1253

1254
  static BorderSide toBorderSide(JSONObject? json) {
4✔
1255
    if (json == null) return BorderSide.none;
1256

1257
    return BorderSide(
2✔
1258
      color: ColorUtils.tryParseColor(json["color"]),
4✔
1259
      width: NumUtils.toDoubleWithNullReplacement(json["width"], 1.0),
4✔
1260
      style: toBorderStyle(json["style"]),
4✔
1261
    );
1262
  }
1263

1264
  static InputBorder? toInputBorder(JSONObject? json) {
×
1265
    if (json == null) return null;
1266

1267
    final type = json["type"] as String;
×
1268
    final borderOptions = json["options"];
×
1269
    final borderSide = toBorderSide(borderOptions["borderSide"]);
×
1270

1271
    switch (type) {
1272
      case "outline":
×
1273
        return OutlineInputBorder(
×
1274
          borderSide: borderSide,
1275
          gapPadding: borderOptions?["gapPadding"] ?? 4.0,
×
1276
          borderRadius:
1277
              BorderRadius.circular(borderOptions?["borderRadius"] ?? 4),
×
1278
        );
1279
      case "underline":
×
1280
        return UnderlineInputBorder(
×
1281
          borderSide: borderSide,
1282
        );
1283
    }
1284

1285
    return null;
1286
  }
1287

1288
  //</editor-fold>
1289

1290
  //<editor-fold desc="Gestures">
1291
  static ScrollViewKeyboardDismissBehavior toKeyboardDismissBehavior(
6✔
1292
      String? value) {
1293
    if (value == null) return ScrollViewKeyboardDismissBehavior.manual;
1294

1295
    switch (value) {
1296
      case "manual":
×
1297
        return ScrollViewKeyboardDismissBehavior.manual;
1298
      case "onDrag":
×
1299
        return ScrollViewKeyboardDismissBehavior.onDrag;
1300
    }
1301

1302
    return ScrollViewKeyboardDismissBehavior.manual;
1303
  }
1304

1305
  static ScrollPhysics toScrollPhysics(String? value) {
6✔
1306
    if (value == null) return const AlwaysScrollableScrollPhysics();
1307

1308
    switch (value) {
1309
      case "alwaysScrollableScrollPhysics":
×
1310
        return const AlwaysScrollableScrollPhysics();
1311
      case "bouncingScrollPhysics":
×
1312
        return const BouncingScrollPhysics();
1313
      case "clampingScrollPhysics":
×
1314
        return const ClampingScrollPhysics();
1315
      case "fixedExtentScrollPhysics":
×
1316
        return const FixedExtentScrollPhysics();
1317
      case "neverScrollableScrollPhysics":
×
1318
        return const NeverScrollableScrollPhysics();
1319
      //Not use this physics
1320
      // case "PageScrollPhysics":
1321
      //   return const PageScrollPhysics();
1322
    }
1323

1324
    return const AlwaysScrollableScrollPhysics();
1325
  }
1326

1327
  static DragStartBehavior toDragStartBehavior(String? behavior) {
8✔
1328
    if (behavior == null) return DragStartBehavior.start;
1329

1330
    switch (behavior) {
1331
      case "start":
×
1332
        return DragStartBehavior.start;
1333
      case "down":
×
1334
        return DragStartBehavior.down;
1335
    }
1336

1337
    return DragStartBehavior.start;
1338
  }
1339

1340
  static HitTestBehavior toHitTestBehavior(String? behavior,
4✔
1341
      [HitTestBehavior? defaultValue]) {
1342
    if (behavior == null) return defaultValue ?? HitTestBehavior.deferToChild;
1343

1344
    switch (behavior) {
1345
      case "deferToChild":
×
1346
        return HitTestBehavior.deferToChild;
1347
      case "opaque":
×
1348
        return HitTestBehavior.opaque;
1349
      case "translucent":
×
1350
        return HitTestBehavior.translucent;
1351
    }
1352

1353
    return HitTestBehavior.deferToChild;
1354
  }
1355

1356
//</editor-fold>
1357

1358
  //<editor-fold desc="Shape and insets">
1359

1360
  /// Converts a JSON object to an [EdgeInsets].
1361
  ///
1362
  /// Returns an [EdgeInsets] with values based on the provided JSON object.
1363
  /// If the [json] is `null` or does not contain valid properties, returns [EdgeInsets.zero] as the default.
1364
  /// The supported properties are:
1365
  /// - "top" (double): The top edge inset.
1366
  /// - "right" (double): The right edge inset.
1367
  /// - "bottom" (double): The bottom edge inset.
1368
  /// - "left" (double): The left edge inset.
1369
  /// You can provide any combination of these properties in the [json] object.
1370
  ///
1371
  /// Example JSON object:
1372
  /// ```
1373
  /// {
1374
  ///   "top": 10.0,
1375
  ///   "right": 20.0,
1376
  ///   "bottom": 10.0,
1377
  ///   "left": 20.0
1378
  /// }
1379
  /// ```
1380
  ///
1381
  /// Example usage:
1382
  /// ```
1383
  /// final json = {
1384
  ///   "top": 10.0,
1385
  ///   "right": 20.0,
1386
  ///   "bottom": 10.0,
1387
  ///   "left": 20.0
1388
  /// };
1389
  /// final edgeInsets = convertToEdgeInsets(json);
1390
  /// ```
1391
  static EdgeInsets toEdgeInsets(dynamic insets) {
38✔
1392
    if (insets == null) return EdgeInsets.zero;
1393

1394
    if (insets is EdgeInsets) return insets;
8✔
1395

1396
    if (insets is num) {
8✔
1397
      return EdgeInsets.all(insets.toDouble());
4✔
1398
    }
1399

1400
    if (insets is List) {
6✔
1401
      final list = List.castFrom<dynamic, num>(insets);
6✔
1402

1403
      if (list.length == 2) {
12✔
1404
        return EdgeInsets.symmetric(
2✔
1405
            vertical: insets[0].toDouble(), horizontal: insets[1].toDouble());
8✔
1406
      }
1407

1408
      if (list.length == 4) {
8✔
1409
        return EdgeInsets.only(
4✔
1410
            left: insets[0].toDouble(),
8✔
1411
            top: insets[1].toDouble(),
8✔
1412
            right: insets[2].toDouble(),
8✔
1413
            bottom: insets[3].toDouble());
8✔
1414
      }
1415
    }
1416

1417
    return EdgeInsets.zero;
1418
  }
1419

1420
  static EdgeInsets? toNullableEdgeInsets(dynamic insets) {
2✔
1421
    if (insets == null) return null;
1422

1423
    if (insets is num) {
×
1424
      return EdgeInsets.all(insets.toDouble());
×
1425
    }
1426

1427
    if (insets is List) {
×
1428
      final list = List.castFrom<dynamic, num>(insets);
×
1429

1430
      if (list.length == 2) {
×
1431
        return EdgeInsets.symmetric(
×
1432
            vertical: insets[0].toDouble(), horizontal: insets[1].toDouble());
×
1433
      }
1434

1435
      if (list.length == 4) {
×
1436
        return EdgeInsets.only(
×
1437
            left: insets[0].toDouble(),
×
1438
            top: insets[1].toDouble(),
×
1439
            right: insets[2].toDouble(),
×
1440
            bottom: insets[3].toDouble());
×
1441
      }
1442
    }
1443

1444
    return null;
1445
  }
1446

1447
//</editor-fold>
1448

1449
  //<editor-fold desc="Animations">
1450
  static Curve toCurve(String? value) {
24✔
1451
    if (value == null) return Curves.linear;
1452

1453
    return switch (value) {
1454
      "linear" => Curves.linear,
6✔
1455
      "fastEaseInToSlowEaseOut" => Curves.fastEaseInToSlowEaseOut,
×
1456
      "bounceIn" => Curves.bounceIn,
×
1457
      "bounceInOut" => Curves.bounceInOut,
×
1458
      "bounceOut" => Curves.bounceOut,
×
1459
      "decelerate" => Curves.decelerate,
×
1460
      "ease" => Curves.ease,
×
1461
      "easeIn" => Curves.easeIn,
×
1462
      "easeInBack" => Curves.easeInBack,
×
1463
      "easeInCirc" => Curves.easeInCirc,
×
1464
      "easeInSine" => Curves.easeInSine,
×
1465
      "easeInCubic" => Curves.easeInCubic,
×
1466
      "easeInExpo" => Curves.easeInExpo,
×
1467
      "easeInOutCubicEmphasized" => Curves.easeInOutCubicEmphasized,
×
1468
      "easeInOutBack" => Curves.easeInOutBack,
×
1469
      "easeInOutCirc" => Curves.easeInOutCirc,
×
1470
      "easeInOutExpo" => Curves.easeInOutExpo,
×
1471
      "easeInOutQuad" => Curves.easeInOutQuad,
×
1472
      "easeInOutQuart" => Curves.easeInOutQuart,
×
1473
      "easeInOutQuint" => Curves.easeInOutQuint,
×
1474
      "easeInOutSine" => Curves.easeInOutSine,
×
1475
      "easeInToLinear" => Curves.easeInToLinear,
×
1476
      "easeOutSine" => Curves.easeOutSine,
×
1477
      "easeOutBack" => Curves.easeOutBack,
×
1478
      "easeOutCirc" => Curves.easeOutCirc,
×
1479
      "easeOutCubic" => Curves.easeOutCubic,
×
1480
      "easeOutExpo" => Curves.easeOutExpo,
×
1481
      "easeOutQuad" => Curves.easeOutQuad,
×
1482
      "easeOutQuart" => Curves.easeOutQuart,
×
1483
      "easeOutQuint" => Curves.easeOutQuint,
×
1484
      "linearToEaseOut" => Curves.linearToEaseOut,
×
1485
      "slowMiddle" => Curves.slowMiddle,
×
1486
      "fastOutSlowIn" => Curves.fastOutSlowIn,
×
1487
      "elasticIn" => Curves.elasticIn,
×
1488
      "elasticInOut" => Curves.elasticInOut,
×
1489
      "elasticOut" => Curves.elasticOut,
×
1490
      String() => Curves.linear,
1491
    };
1492
  }
1493

1494
  static Duration toDuration(num? value) {
26✔
1495
    if (value == null) return Duration.zero;
1496

1497
    return Duration(milliseconds: value.toInt());
48✔
1498
  }
1499
//</editor-fold>
1500

1501
  static BorderRadius? toBorderRadius(JSONObject? json) {
4✔
1502
    if (json == null) return null;
1503

1504
    return BorderRadius.only(
2✔
1505
      topLeft:
1506
          Radius.circular(NumUtils.toDouble(json['topLeft']?['radius']) ?? 0),
8✔
1507
      topRight:
1508
          Radius.circular(NumUtils.toDouble(json['topRight']?['radius']) ?? 0),
6✔
1509
      bottomLeft: Radius.circular(
2✔
1510
          NumUtils.toDouble(json['bottomLeft']?['radius']) ?? 0),
4✔
1511
      bottomRight: Radius.circular(
2✔
1512
          NumUtils.toDouble(json['bottomRight']?['radius']) ?? 0),
6✔
1513
    );
1514
  }
1515

1516
  static ShapeBorder? toShapeBorder(JSONObject? json) {
10✔
1517
    if (json == null) return null;
1518

1519
    final type = json['type'] as String?;
2✔
1520
    if (type == null) return null;
1521

1522
    switch (type) {
1523
      case 'RoundedRectangleBorder':
2✔
1524
        return RoundedRectangleBorder(
2✔
1525
          borderRadius:
1526
              toBorderRadius(json['borderRadius']) ?? BorderRadius.zero,
4✔
1527
          side: toBorderSide(json['side']),
4✔
1528
        );
1529
      case 'CircleBorder':
×
1530
        return CircleBorder(
×
1531
          side: toBorderSide(json['side']),
×
1532
        );
1533
      case 'StadiumBorder':
×
1534
        return StadiumBorder(
×
1535
          side: toBorderSide(json['side']),
×
1536
        );
1537
      case 'BeveledRectangleBorder':
×
1538
        return BeveledRectangleBorder(
×
1539
          borderRadius:
1540
              toBorderRadius(json['borderRadius']) ?? BorderRadius.zero,
×
1541
          side: toBorderSide(json['side']),
×
1542
        );
1543
      case 'ContinuousRectangleBorder':
×
1544
        return ContinuousRectangleBorder(
×
1545
          borderRadius:
1546
              toBorderRadius(json['borderRadius']) ?? BorderRadius.zero,
×
1547
          side: toBorderSide(json['side']),
×
1548
        );
1549
      default:
1550
        return null;
1551
    }
1552
  }
1553

1554
  static FloatingActionButtonLocation? toFABLocation(dynamic value) {
2✔
1555
    if (value == null) return null;
1556

1557
    return switch (value) {
1558
      "centerDocked" || 0 => FloatingActionButtonLocation.centerDocked,
×
1559
      "centerFloat" || 1 => FloatingActionButtonLocation.centerFloat,
×
1560
      "centerTop" || 2 => FloatingActionButtonLocation.centerTop,
×
1561
      "endDocked" || 3 => FloatingActionButtonLocation.endDocked,
×
1562
      "endFloat" || 4 => FloatingActionButtonLocation.endFloat,
×
1563
      "endTop" || 5 => FloatingActionButtonLocation.endTop,
×
1564
      "startDocked" || 6 => FloatingActionButtonLocation.startDocked,
×
1565
      "startFloat" || 7 => FloatingActionButtonLocation.startFloat,
×
1566
      "startTop" || 8 => FloatingActionButtonLocation.startTop,
×
1567
      "miniCenterDocked" || 9 => FloatingActionButtonLocation.miniCenterDocked,
×
1568
      "miniCenterFloat" || 10 => FloatingActionButtonLocation.miniCenterFloat,
×
1569
      "miniCenterTop" || 11 => FloatingActionButtonLocation.miniCenterTop,
×
1570
      "miniEndDocked" || 12 => FloatingActionButtonLocation.miniEndDocked,
×
1571
      "miniEndFloat" || 13 => FloatingActionButtonLocation.miniEndFloat,
×
1572
      "miniEndTop" || 14 => FloatingActionButtonLocation.miniEndTop,
×
1573
      "miniStartDocked" || 15 => FloatingActionButtonLocation.miniStartDocked,
×
1574
      "miniStartFloat" || 16 => FloatingActionButtonLocation.miniStartFloat,
×
1575
      "miniStartTop" || 17 => FloatingActionButtonLocation.miniStartTop,
×
1576
      _ => null,
1577
    };
1578
  }
1579

NEW
1580
  static WidgetStateProperty<TextStyle?> toWSPTextStyle(
×
1581
      Map<String, dynamic> value) {
NEW
1582
    return WidgetStateProperty.resolveWith((states) {
×
NEW
1583
      if (states.contains(WidgetState.disabled)) {
×
NEW
1584
        return toTextStyle(value[WidgetState.disabled.name]);
×
1585
      }
1586

NEW
1587
      if (states.contains(WidgetState.selected)) {
×
NEW
1588
        return toTextStyle(value[WidgetState.selected.name]);
×
1589
      }
1590

NEW
1591
      if (states.contains(WidgetState.error)) {
×
NEW
1592
        return toTextStyle(value[WidgetState.error.name]);
×
1593
      }
1594

NEW
1595
      if (states.contains(WidgetState.pressed)) {
×
NEW
1596
        return toTextStyle(value[WidgetState.pressed.name]);
×
1597
      }
1598

NEW
1599
      if (states.contains(WidgetState.hovered)) {
×
NEW
1600
        return toTextStyle(value[WidgetState.hovered.name]);
×
1601
      }
1602

NEW
1603
      if (states.contains(WidgetState.focused)) {
×
NEW
1604
        return toTextStyle(value[WidgetState.focused.name]);
×
1605
      }
1606

NEW
1607
      if (states.contains(WidgetState.dragged)) {
×
NEW
1608
        return toTextStyle(value[WidgetState.dragged.name]);
×
1609
      }
1610

1611
      return null;
1612
    });
1613
  }
1614

NEW
1615
  static WidgetStateProperty<T?> toWidgetStateProperty<T>(
×
1616
    Map<String, dynamic> value,
1617
  ) {
NEW
1618
    return WidgetStateProperty.resolveWith((states) {
×
NEW
1619
      if (states.contains(WidgetState.disabled)) {
×
1620
        return switch (T) {
NEW
1621
          Color => ColorUtils.tryParseNullableColor(
×
NEW
1622
              value[WidgetState.disabled.name],
×
1623
            ),
NEW
1624
          EdgeInsetsGeometry => toEdgeInsets(
×
NEW
1625
              value[WidgetState.disabled.name],
×
1626
            ),
NEW
1627
          Size => toSize(
×
NEW
1628
              value[WidgetState.disabled.name],
×
1629
            ),
NEW
1630
          double => NumUtils.toDouble(
×
NEW
1631
              value[WidgetState.disabled.name],
×
1632
            ),
NEW
1633
          OutlinedBorder => toShapeBorder(
×
NEW
1634
              value[WidgetState.disabled.name],
×
1635
            ),
NEW
1636
          TextStyle => toTextStyle(
×
NEW
1637
              value[WidgetState.disabled.name],
×
1638
            ),
NEW
1639
          BorderSide => toBorderSide(
×
NEW
1640
              value[WidgetState.disabled.name],
×
1641
            ),
1642
          _ => null,
1643
        } as T?;
1644
      }
1645

NEW
1646
      if (states.contains(WidgetState.selected)) {
×
1647
        return switch (T) {
NEW
1648
          Color => ColorUtils.tryParseNullableColor(
×
NEW
1649
              value[WidgetState.selected.name],
×
1650
            ),
NEW
1651
          EdgeInsetsGeometry => toEdgeInsets(
×
NEW
1652
              value[WidgetState.selected.name],
×
1653
            ),
NEW
1654
          Size => toSize(
×
NEW
1655
              value[WidgetState.selected.name],
×
1656
            ),
NEW
1657
          double => NumUtils.toDouble(
×
NEW
1658
              value[WidgetState.selected.name],
×
1659
            ),
NEW
1660
          OutlinedBorder => toShapeBorder(
×
NEW
1661
              value[WidgetState.selected.name],
×
1662
            ),
NEW
1663
          TextStyle => toTextStyle(
×
NEW
1664
              value[WidgetState.selected.name],
×
1665
            ),
NEW
1666
          BorderSide => toBorderSide(
×
NEW
1667
              value[WidgetState.selected.name],
×
1668
            ),
1669
          _ => null,
1670
        } as T?;
1671
      }
1672

NEW
1673
      if (states.contains(WidgetState.error)) {
×
1674
        return switch (T) {
NEW
1675
          Color => ColorUtils.tryParseNullableColor(
×
NEW
1676
              value[WidgetState.error.name],
×
1677
            ),
NEW
1678
          EdgeInsetsGeometry => toEdgeInsets(
×
NEW
1679
              value[WidgetState.error.name],
×
1680
            ),
NEW
1681
          Size => toSize(
×
NEW
1682
              value[WidgetState.error.name],
×
1683
            ),
NEW
1684
          double => NumUtils.toDouble(
×
NEW
1685
              value[WidgetState.error.name],
×
1686
            ),
NEW
1687
          OutlinedBorder => toShapeBorder(
×
NEW
1688
              value[WidgetState.error.name],
×
1689
            ),
NEW
1690
          TextStyle => toTextStyle(
×
NEW
1691
              value[WidgetState.error.name],
×
1692
            ),
NEW
1693
          BorderSide => toBorderSide(
×
NEW
1694
              value[WidgetState.error.name],
×
1695
            ),
1696
          _ => null,
1697
        } as T?;
1698
      }
1699

NEW
1700
      if (states.contains(WidgetState.pressed)) {
×
1701
        return switch (T) {
NEW
1702
          Color => ColorUtils.tryParseNullableColor(
×
NEW
1703
              value[WidgetState.pressed.name],
×
1704
            ),
NEW
1705
          EdgeInsetsGeometry => toEdgeInsets(
×
NEW
1706
              value[WidgetState.pressed.name],
×
1707
            ),
NEW
1708
          Size => toSize(
×
NEW
1709
              value[WidgetState.pressed.name],
×
1710
            ),
NEW
1711
          double => NumUtils.toDouble(
×
NEW
1712
              value[WidgetState.pressed.name],
×
1713
            ),
NEW
1714
          OutlinedBorder => toShapeBorder(
×
NEW
1715
              value[WidgetState.pressed.name],
×
1716
            ),
NEW
1717
          TextStyle => toTextStyle(
×
NEW
1718
              value[WidgetState.pressed.name],
×
1719
            ),
NEW
1720
          BorderSide => toBorderSide(
×
NEW
1721
              value[WidgetState.pressed.name],
×
1722
            ),
1723
          _ => null,
1724
        } as T?;
1725
      }
1726

NEW
1727
      if (states.contains(WidgetState.hovered)) {
×
1728
        return switch (T) {
NEW
1729
          Color => ColorUtils.tryParseNullableColor(
×
NEW
1730
              value[WidgetState.hovered.name],
×
1731
            ),
NEW
1732
          EdgeInsetsGeometry => toEdgeInsets(
×
NEW
1733
              value[WidgetState.hovered.name],
×
1734
            ),
NEW
1735
          Size => toSize(
×
NEW
1736
              value[WidgetState.hovered.name],
×
1737
            ),
NEW
1738
          double => NumUtils.toDouble(
×
NEW
1739
              value[WidgetState.hovered.name],
×
1740
            ),
NEW
1741
          OutlinedBorder => toShapeBorder(
×
NEW
1742
              value[WidgetState.hovered.name],
×
1743
            ),
NEW
1744
          TextStyle => toTextStyle(
×
NEW
1745
              value[WidgetState.hovered.name],
×
1746
            ),
NEW
1747
          BorderSide => toBorderSide(
×
NEW
1748
              value[WidgetState.hovered.name],
×
1749
            ),
1750
          _ => null,
1751
        } as T?;
1752
      }
1753

NEW
1754
      if (states.contains(WidgetState.focused)) {
×
1755
        return switch (T) {
NEW
1756
          Color => ColorUtils.tryParseNullableColor(
×
NEW
1757
              value[WidgetState.focused.name],
×
1758
            ),
NEW
1759
          EdgeInsetsGeometry => toEdgeInsets(
×
NEW
1760
              value[WidgetState.focused.name],
×
1761
            ),
NEW
1762
          Size => toSize(
×
NEW
1763
              value[WidgetState.focused.name],
×
1764
            ),
NEW
1765
          double => NumUtils.toDouble(
×
NEW
1766
              value[WidgetState.focused.name],
×
1767
            ),
NEW
1768
          OutlinedBorder => toShapeBorder(
×
NEW
1769
              value[WidgetState.focused.name],
×
1770
            ),
NEW
1771
          TextStyle => toTextStyle(
×
NEW
1772
              value[WidgetState.focused.name],
×
1773
            ),
NEW
1774
          BorderSide => toBorderSide(
×
NEW
1775
              value[WidgetState.focused.name],
×
1776
            ),
1777
          _ => null,
1778
        } as T?;
1779
      }
1780

NEW
1781
      if (states.contains(WidgetState.dragged)) {
×
1782
        return switch (T) {
NEW
1783
          Color => ColorUtils.tryParseNullableColor(
×
NEW
1784
              value[WidgetState.dragged.name],
×
1785
            ),
NEW
1786
          EdgeInsetsGeometry => toEdgeInsets(
×
NEW
1787
              value[WidgetState.dragged.name],
×
1788
            ),
NEW
1789
          Size => toSize(
×
NEW
1790
              value[WidgetState.dragged.name],
×
1791
            ),
NEW
1792
          double => NumUtils.toDouble(
×
NEW
1793
              value[WidgetState.dragged.name],
×
1794
            ),
NEW
1795
          OutlinedBorder => toShapeBorder(
×
NEW
1796
              value[WidgetState.dragged.name],
×
1797
            ),
NEW
1798
          TextStyle => toTextStyle(
×
NEW
1799
              value[WidgetState.dragged.name],
×
1800
            ),
NEW
1801
          BorderSide => toBorderSide(
×
NEW
1802
              value[WidgetState.dragged.name],
×
1803
            ),
1804
          _ => null,
1805
        } as T?;
1806
      }
1807

1808
      return null;
1809
    });
1810
  }
1811

1812
  static ButtonStyle? toButtonStyle(dynamic value) {
4✔
1813
    if (value == null) return null;
1814

NEW
1815
    if (value is Map) {
×
1816
      final {
NEW
1817
        "textStyle": textStyle,
×
NEW
1818
        "backgroundColor": backgroundColor,
×
NEW
1819
        "foregroundColor": foregroundColor,
×
NEW
1820
        "overlayColor": overlayColor,
×
NEW
1821
        "shadowColor": shadowColor,
×
NEW
1822
        "surfaceTintColor": surfaceTintColor,
×
NEW
1823
        "elevation": elevation,
×
NEW
1824
        "padding": padding,
×
NEW
1825
        "minimumSize": minimumSize,
×
NEW
1826
        "maximumSize": maximumSize,
×
NEW
1827
        "iconColor": iconColor,
×
NEW
1828
        "iconSize": iconSize,
×
NEW
1829
        "side": side,
×
NEW
1830
        "shape": shape,
×
NEW
1831
        "visualDensity": visualDensity,
×
NEW
1832
        "tapTargetSize": tapTargetSize,
×
NEW
1833
        "animationDuration": int? animationDuration,
×
NEW
1834
        "enableFeedback": bool? enableFeedback,
×
NEW
1835
        "alignment": alignment,
×
1836
      } = value;
1837

NEW
1838
      return ButtonStyle(
×
1839
        textStyle: textStyle == null
1840
            ? null
NEW
1841
            : toWidgetStateProperty<TextStyle>(textStyle),
×
1842
        backgroundColor: backgroundColor == null
1843
            ? null
NEW
1844
            : toWidgetStateProperty<Color>(backgroundColor),
×
1845
        foregroundColor: foregroundColor == null
1846
            ? null
NEW
1847
            : toWidgetStateProperty<Color>(foregroundColor),
×
1848
        overlayColor: overlayColor == null
1849
            ? null
NEW
1850
            : toWidgetStateProperty<Color>(overlayColor),
×
1851
        shadowColor: shadowColor == null
1852
            ? null
NEW
1853
            : toWidgetStateProperty<Color>(shadowColor),
×
1854
        surfaceTintColor: surfaceTintColor == null
1855
            ? null
NEW
1856
            : toWidgetStateProperty<Color>(surfaceTintColor),
×
1857
        elevation:
NEW
1858
            elevation == null ? null : toWidgetStateProperty<double>(elevation),
×
1859
        padding: padding == null
1860
            ? null
NEW
1861
            : toWidgetStateProperty<EdgeInsetsGeometry>(padding),
×
1862
        minimumSize: minimumSize == null
1863
            ? null
NEW
1864
            : toWidgetStateProperty<Size>(minimumSize),
×
1865
        maximumSize: maximumSize == null
1866
            ? null
NEW
1867
            : toWidgetStateProperty<Size>(maximumSize),
×
1868
        iconColor:
NEW
1869
            iconColor == null ? null : toWidgetStateProperty<Color>(iconColor),
×
1870
        iconSize:
NEW
1871
            iconSize == null ? null : toWidgetStateProperty<double>(iconSize),
×
NEW
1872
        side: side == null ? null : toWidgetStateProperty<BorderSide>(side),
×
1873
        shape:
NEW
1874
            shape == null ? null : toWidgetStateProperty<OutlinedBorder>(shape),
×
NEW
1875
        visualDensity: toVisualDensity(visualDensity),
×
NEW
1876
        tapTargetSize: toMaterialTapTargetSize(tapTargetSize),
×
1877
        animationDuration:
NEW
1878
            animationDuration == null ? null : toDuration(animationDuration),
×
1879
        enableFeedback: enableFeedback,
NEW
1880
        alignment: toAlignment(alignment),
×
1881
      );
1882
    }
1883
    return null;
1884
  }
1885
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc