23
23
import 'package:flutter/services.dart' ;
24
24
import 'package:flutter/widgets.dart' ;
25
25
26
+ import 'spin_controller.dart' ;
26
27
import 'spin_formatter.dart' ;
27
28
28
29
// ignore_for_file: public_member_api_docs
@@ -38,30 +39,20 @@ abstract class BaseSpinBox extends StatefulWidget {
38
39
int get decimals;
39
40
int get digits;
40
41
ValueChanged <double >? get onChanged;
41
- bool Function (double value)? get canChange;
42
- VoidCallback ? get beforeChange;
43
- VoidCallback ? get afterChange;
44
42
bool get readOnly;
45
43
FocusNode ? get focusNode;
44
+ SpinController ? get controller;
46
45
}
47
46
48
47
mixin SpinBoxMixin <T extends BaseSpinBox > on State <T > {
49
- late double _value ;
50
- late double _cachedValue ;
48
+ late final SpinController _controller ;
49
+ late final TextEditingController _editor ;
51
50
late final FocusNode _focusNode;
52
- late final TextEditingController _controller;
53
51
54
- double get value => _value ;
55
- bool get hasFocus => _focusNode.hasFocus ;
52
+ SpinController get controller => _controller ;
53
+ TextEditingController get editor => _editor ;
56
54
FocusNode get focusNode => _focusNode;
57
- TextEditingController get controller => _controller;
58
- SpinFormatter get formatter => SpinFormatter (
59
- min: widget.min, max: widget.max, decimals: widget.decimals);
60
-
61
- static double _parseValue (String text) => double .tryParse (text) ?? 0 ;
62
- String _formatText (double value) {
63
- return value.toStringAsFixed (widget.decimals).padLeft (widget.digits, '0' );
64
- }
55
+ SpinFormatter get formatter => SpinFormatter (_controller);
65
56
66
57
Map <ShortcutActivator , VoidCallback > get bindings {
67
58
return {
@@ -76,67 +67,67 @@ mixin SpinBoxMixin<T extends BaseSpinBox> on State<T> {
76
67
};
77
68
}
78
69
70
+ String _formatValue (double value) {
71
+ return formatter.formatValue (value,
72
+ decimals: widget.decimals, digits: widget.digits);
73
+ }
74
+
79
75
@override
80
76
void initState () {
81
77
super .initState ();
82
- _value = widget.value;
83
- _cachedValue = widget.value;
84
- _controller = TextEditingController (text: _formatText (_value));
85
- _controller.addListener (_updateValue);
78
+ _controller = widget.controller ??
79
+ SpinController (
80
+ min: widget.min,
81
+ max: widget.max,
82
+ value: widget.value,
83
+ decimals: widget.decimals,
84
+ );
85
+ _controller.addListener (_handleValueChange);
86
+ _editor = TextEditingController (text: _formatValue (widget.value));
87
+ _editor.addListener (_handleTextChange);
86
88
_focusNode = widget.focusNode ?? FocusNode ();
87
- _focusNode.addListener (_handleFocusChanged );
89
+ _focusNode.addListener (_handleFocusChange );
88
90
}
89
91
90
92
@override
91
93
void dispose () {
92
- _focusNode.removeListener (_handleFocusChanged );
94
+ _focusNode.removeListener (_handleFocusChange );
93
95
if (widget.focusNode == null ) {
94
96
_focusNode.dispose ();
95
97
}
96
- _controller.dispose ();
98
+ _controller.removeListener (_handleValueChange);
99
+ if (widget.controller == null ) {
100
+ _controller.dispose ();
101
+ }
102
+ _editor.dispose ();
97
103
super .dispose ();
98
104
}
99
105
100
- void _stepUp () => setValue (value + widget.step);
101
- void _stepDown () => setValue (value - widget.step);
102
-
103
- void _pageStepUp () => setValue (value + widget.pageStep! );
104
- void _pageStepDown () => setValue (value - widget.pageStep! );
106
+ void _stepUp () => controller.value += widget.step;
107
+ void _stepDown () => controller.value -= widget.step;
105
108
106
- void _updateValue () {
107
- final v = _parseValue (_controller.text);
108
- if (v == _value) return ;
109
+ void _pageStepUp () => controller.value += widget.pageStep! ;
110
+ void _pageStepDown () => controller.value -= widget.pageStep! ;
109
111
110
- if (widget.canChange? .call (v) == false ) {
111
- controller.text = _formatText (_cachedValue);
112
- setState (() {
113
- _value = _cachedValue;
114
- });
115
- return ;
116
- }
117
-
118
- setState (() => _value = v);
119
- widget.onChanged? .call (v);
112
+ void _handleValueChange () {
113
+ widget.onChanged? .call (_controller.value);
114
+ setState (() => _updateText (_controller.value));
120
115
}
121
116
122
- void setValue (double v) {
123
- final newValue = v.clamp (widget.min, widget.max);
124
- if (newValue == value) return ;
125
-
126
- if (widget.canChange? .call (newValue) == false ) return ;
127
-
128
- widget.beforeChange? .call ();
129
- setState (() => _updateController (value, newValue));
130
- widget.afterChange? .call ();
117
+ void _handleTextChange () {
118
+ final value = _controller.parse (_editor.text);
119
+ if (value != null && value >= controller.min && value <= controller.max) {
120
+ _controller.value = value;
121
+ }
131
122
}
132
123
133
- void _updateController (double oldValue, double newValue ) {
134
- final text = _formatText (newValue );
135
- final selection = _controller .selection;
136
- final oldOffset = value.isNegative ? 1 : 0 ;
137
- final newOffset = _parseValue (text).isNegative ? 1 : 0 ;
124
+ void _updateText (double value ) {
125
+ final text = _formatValue (value );
126
+ final selection = _editor .selection;
127
+ final oldOffset = _controller. value.isNegative ? 1 : 0 ;
128
+ final newOffset = _controller. parse (text)? .isNegative == true ? 1 : 0 ;
138
129
139
- _controller .value = _controller .value.copyWith (
130
+ _editor .value = _editor .value.copyWith (
140
131
text: text,
141
132
selection: selection.copyWith (
142
133
baseOffset: selection.baseOffset - oldOffset + newOffset,
@@ -146,37 +137,36 @@ mixin SpinBoxMixin<T extends BaseSpinBox> on State<T> {
146
137
}
147
138
148
139
@protected
149
- void fixupValue (String value) {
150
- final v = _parseValue (value);
151
- if (value.isEmpty || (v < widget.min || v > widget.max)) {
152
- // will trigger notify to _updateValue()
153
- _controller.text = _formatText (_cachedValue);
154
- } else {
155
- _cachedValue = _value;
140
+ void fixupValue (String text) {
141
+ final value = _controller.parse (text);
142
+ if (value == null ) {
143
+ _editor.text = _formatValue (_controller.value);
144
+ } else if (value < _controller.min || value > _controller.max) {
145
+ _controller.value = value.clamp (_controller.min, _controller.max);
156
146
}
157
147
}
158
148
159
- void _handleFocusChanged () {
160
- if (hasFocus) {
149
+ void _handleFocusChange () {
150
+ if (focusNode. hasFocus) {
161
151
setState (_selectAll);
162
152
} else {
163
- fixupValue (_controller .text);
153
+ fixupValue (_editor .text);
164
154
}
165
155
}
166
156
167
157
void _selectAll () {
168
- _controller .selection = _controller .selection
169
- .copyWith (baseOffset: 0 , extentOffset: _controller .text.length);
158
+ _editor .selection = _editor .selection
159
+ .copyWith (baseOffset: 0 , extentOffset: _editor .text.length);
170
160
}
171
161
172
162
@override
173
163
void didUpdateWidget (T oldWidget) {
174
164
super .didUpdateWidget (oldWidget);
175
165
if (oldWidget.value != widget.value) {
176
- _controller .removeListener (_updateValue );
177
- _value = _cachedValue = widget.value;
178
- _updateController (oldWidget.value, widget.value);
179
- _controller .addListener (_updateValue );
166
+ _editor .removeListener (_handleTextChange );
167
+ _controller.value = widget.value;
168
+ _updateText ( widget.value);
169
+ _editor .addListener (_handleTextChange );
180
170
}
181
171
}
182
172
}
0 commit comments