2323import 'package:flutter/services.dart' ;
2424import 'package:flutter/widgets.dart' ;
2525
26+ import 'spin_controller.dart' ;
2627import 'spin_formatter.dart' ;
2728
2829// ignore_for_file: public_member_api_docs
@@ -38,30 +39,20 @@ abstract class BaseSpinBox extends StatefulWidget {
3839 int get decimals;
3940 int get digits;
4041 ValueChanged <double >? get onChanged;
41- bool Function (double value)? get canChange;
42- VoidCallback ? get beforeChange;
43- VoidCallback ? get afterChange;
4442 bool get readOnly;
4543 FocusNode ? get focusNode;
44+ SpinController ? get controller;
4645}
4746
4847mixin 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 ;
5150 late final FocusNode _focusNode;
52- late final TextEditingController _controller;
5351
54- double get value => _value ;
55- bool get hasFocus => _focusNode.hasFocus ;
52+ SpinController get controller => _controller ;
53+ TextEditingController get editor => _editor ;
5654 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);
6556
6657 Map <ShortcutActivator , VoidCallback > get bindings {
6758 return {
@@ -76,67 +67,67 @@ mixin SpinBoxMixin<T extends BaseSpinBox> on State<T> {
7667 };
7768 }
7869
70+ String _formatValue (double value) {
71+ return formatter.formatValue (value,
72+ decimals: widget.decimals, digits: widget.digits);
73+ }
74+
7975 @override
8076 void initState () {
8177 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);
8688 _focusNode = widget.focusNode ?? FocusNode ();
87- _focusNode.addListener (_handleFocusChanged );
89+ _focusNode.addListener (_handleFocusChange );
8890 }
8991
9092 @override
9193 void dispose () {
92- _focusNode.removeListener (_handleFocusChanged );
94+ _focusNode.removeListener (_handleFocusChange );
9395 if (widget.focusNode == null ) {
9496 _focusNode.dispose ();
9597 }
96- _controller.dispose ();
98+ _controller.removeListener (_handleValueChange);
99+ if (widget.controller == null ) {
100+ _controller.dispose ();
101+ }
102+ _editor.dispose ();
97103 super .dispose ();
98104 }
99105
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;
105108
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! ;
109111
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));
120115 }
121116
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+ }
131122 }
132123
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 ;
138129
139- _controller .value = _controller .value.copyWith (
130+ _editor .value = _editor .value.copyWith (
140131 text: text,
141132 selection: selection.copyWith (
142133 baseOffset: selection.baseOffset - oldOffset + newOffset,
@@ -146,37 +137,36 @@ mixin SpinBoxMixin<T extends BaseSpinBox> on State<T> {
146137 }
147138
148139 @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);
156146 }
157147 }
158148
159- void _handleFocusChanged () {
160- if (hasFocus) {
149+ void _handleFocusChange () {
150+ if (focusNode. hasFocus) {
161151 setState (_selectAll);
162152 } else {
163- fixupValue (_controller .text);
153+ fixupValue (_editor .text);
164154 }
165155 }
166156
167157 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);
170160 }
171161
172162 @override
173163 void didUpdateWidget (T oldWidget) {
174164 super .didUpdateWidget (oldWidget);
175165 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 );
180170 }
181171 }
182172}
0 commit comments