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

wger-project / flutter / 30854254088

03 Aug 2026 09:21PM UTC coverage: 40.895% (-15.0%) from 55.93%
30854254088

Pull #1260

github

web-flow
Merge d93794de5 into e3affdd4f
Pull Request #1260: WIP: health sync

9980 of 24404 relevant lines covered (40.89%)

1.72 hits per line

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

89.47
/lib/core/widgets/decimal_input.dart
1
/*
2
 * This file is part of wger Workout Manager <https://github.com/wger-project>.
3
 * Copyright (c) 2026 - 2026 wger Team
4
 *
5
 * wger Workout Manager is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU Affero General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU Affero General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Affero General Public License
16
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18

19
import 'package:flutter/material.dart';
20
import 'package:wger/core/consts.dart';
21
import 'package:wger/core/formatting/formatting.dart';
22
import 'package:wger/core/number_input.dart';
23
import 'package:wger/l10n/generated/app_localizations.dart';
24

25
/// Locale-aware decimal text field.
26
///
27
/// Renders [value] with the active locale's number format and reports edits
28
/// through [onChanged] as a parsed [num], or null when the field is empty.
29
/// Display and parsing always go through the same NumberFormat, so a value can
30
/// never be mis-read because of a decimal-separator mismatch between locales.
31
class DecimalInputWidget extends StatelessWidget {
32
  const DecimalInputWidget({
3✔
33
    required this.value,
34
    required this.onChanged,
35
    required this.labelText,
36
    this.suffixText,
37
    this.isRequired = false,
38
    this.min,
39
    this.max,
40
    super.key,
41
  });
42

43
  /// The current value, or null when unset.
44
  final num? value;
45

46
  /// Called with the parsed value on every edit, or null when the field is
47
  /// cleared or its contents cannot be parsed.
48
  final ValueChanged<num?> onChanged;
49

50
  final String labelText;
51

52
  final String? suffixText;
53

54
  /// When true, an empty field fails validation instead of being accepted.
55
  final bool isRequired;
56

57
  /// Optional inclusive lower bound. When both [min] and [max] are set, a
58
  /// parsed value outside the range fails validation.
59
  final num? min;
60

61
  /// Optional inclusive upper bound. See [min].
62
  final num? max;
63

64
  @override
3✔
65
  Widget build(BuildContext context) {
66
    final i18n = AppLocalizations.of(context);
3✔
67
    final numberFormat = localizedNumberFormat(context);
3✔
68

69
    return TextFormField(
3✔
70
      initialValue: value == null ? '' : numberFormat.format(value),
5✔
71
      decoration: InputDecoration(labelText: labelText, suffixText: suffixText),
9✔
72
      keyboardType: textInputTypeDecimal,
73
      inputFormatters: [LocalizedDecimalInputFormatter(numberFormat.symbols.DECIMAL_SEP)],
12✔
74
      onChanged: (text) {
1✔
75
        final trimmed = text.trim();
1✔
76
        onChanged(trimmed.isEmpty ? null : numberFormat.tryParse(trimmed));
4✔
77
      },
78
      validator: (text) {
3✔
79
        final trimmed = text?.trim() ?? '';
3✔
80
        if (trimmed.isEmpty) {
3✔
81
          return isRequired ? i18n.enterValue : null;
5✔
82
        }
83
        final parsed = numberFormat.tryParse(trimmed);
2✔
84
        if (parsed == null) {
85
          return i18n.enterValidNumber;
×
86
        }
87
        if (min != null && max != null && (parsed < min! || parsed > max!)) {
12✔
88
          return i18n.formMinMaxValues(min!.toInt(), max!.toInt());
×
89
        }
90
        return null;
91
      },
92
    );
93
  }
94
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc