You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
5.6 KiB
Dart
189 lines
5.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
typedef StepperCallbackFuture = Future<bool> Function(int apply, int total);
|
|
|
|
class StepperView extends StatefulWidget {
|
|
final double height;
|
|
final Color foregroundColor;
|
|
final Color backgroundColor;
|
|
final double buttonPadding;
|
|
|
|
final int initialNumber;
|
|
final int maxNumber;
|
|
final int minNumber;
|
|
final StepperCallbackFuture? counterCallback;
|
|
final Function? increaseCallback;
|
|
final Function? decreaseCallback;
|
|
|
|
StepperView({this.initialNumber = 1, this.minNumber = 1, required this.maxNumber, @required this.counterCallback, this.increaseCallback, this.decreaseCallback, this.height = 25, required this.foregroundColor, required this.backgroundColor, this.buttonPadding = 1}){
|
|
assert((this.initialNumber >= this.minNumber && this.initialNumber <= this.maxNumber));
|
|
}
|
|
@override
|
|
_StepperViewState createState() => _StepperViewState();
|
|
}
|
|
|
|
class _StepperViewState extends State<StepperView> {
|
|
late int _currentCount;
|
|
late StepperCallbackFuture _counterCallback;
|
|
late Function _increaseCallback;
|
|
late Function _decreaseCallback;
|
|
|
|
@override
|
|
void initState() {
|
|
_currentCount = widget.initialNumber ?? 1;
|
|
_counterCallback = widget.counterCallback!;
|
|
_increaseCallback = widget.increaseCallback ?? () {};
|
|
_decreaseCallback = widget.decreaseCallback ?? () {};
|
|
super.initState();
|
|
}
|
|
|
|
bool loadingInc = false;
|
|
bool loadingDec = false;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
padding: EdgeInsets.all(widget.buttonPadding),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular((widget.height/2) + (widget.buttonPadding*2)),
|
|
color: widget.backgroundColor,
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
children: [
|
|
_createDecrementButton(
|
|
((_currentCount > widget.minNumber) ? () => _decrement() : null) as VoidCallback,
|
|
),
|
|
Container(
|
|
width: 25,
|
|
child: Center(
|
|
child: Text(
|
|
_currentCount.toString(),
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.normal,
|
|
height: 1.5,
|
|
color: Colors.black,
|
|
)
|
|
),
|
|
)
|
|
),
|
|
_createIncrementButton(
|
|
((_currentCount < widget.maxNumber) ? () => _increment() : null) as VoidCallback
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
void _increment() async{
|
|
doInc({required bool can}){
|
|
if(can)
|
|
setState(() {
|
|
_currentCount++;
|
|
_increaseCallback();
|
|
});
|
|
}
|
|
|
|
if (_currentCount < widget.maxNumber){
|
|
if(_counterCallback == null)
|
|
doInc(can: true);
|
|
else {
|
|
setState(() => loadingInc = true);
|
|
var result = (await _counterCallback(1,_currentCount));
|
|
doInc(can: result);
|
|
setState(() => loadingInc = false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void _decrement() async{
|
|
doDec({required bool can}){
|
|
if(can)
|
|
setState(() {
|
|
_currentCount--;
|
|
_decreaseCallback();
|
|
});
|
|
}
|
|
if (_currentCount > widget.minNumber) {
|
|
if(_counterCallback == null)
|
|
doDec(can: true);
|
|
else {
|
|
setState(() => loadingDec = true);
|
|
var result = (await _counterCallback(-1,_currentCount));
|
|
doDec(can: result);
|
|
setState(() => loadingDec = false);
|
|
}
|
|
}
|
|
}
|
|
|
|
Widget _createIncrementButton(VoidCallback onPressed,) {
|
|
return Stack(
|
|
children: [
|
|
RawMaterialButton(
|
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
constraints: BoxConstraints(minWidth: widget.height, minHeight: widget.height),
|
|
onPressed: onPressed,
|
|
elevation: 2.0,
|
|
fillColor: widget.foregroundColor,
|
|
child:
|
|
Icon(
|
|
Icons.add,
|
|
color: (_currentCount < widget.maxNumber) ? Colors.grey[700] : Colors.grey[400],
|
|
size: 12.0,
|
|
),
|
|
shape: CircleBorder(),
|
|
),
|
|
|
|
if(loadingInc)
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.8),
|
|
borderRadius: BorderRadius.circular(widget.height/2),
|
|
),
|
|
height: widget.height,
|
|
width: widget.height,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: 3,
|
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.green)
|
|
)
|
|
)
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _createDecrementButton(VoidCallback onPressed) {
|
|
return Stack(
|
|
children: [
|
|
RawMaterialButton(
|
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
constraints: BoxConstraints(minWidth: widget.height, minHeight: widget.height),
|
|
onPressed: onPressed,
|
|
elevation: 2.0,
|
|
fillColor: widget.foregroundColor,
|
|
child: Icon(
|
|
Icons.remove,
|
|
color: (_currentCount > widget.minNumber) ? Colors.grey[700] : Colors.grey[400],
|
|
size: 12.0,
|
|
),
|
|
shape: CircleBorder(),
|
|
),
|
|
|
|
if(loadingDec)
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.8),
|
|
borderRadius: BorderRadius.circular(widget.height/2),
|
|
),
|
|
height: widget.height,
|
|
width: widget.height,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: 3,
|
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.red)
|
|
)
|
|
)
|
|
],
|
|
);
|
|
}
|
|
}
|