这是一个用于实现自定义数值增减 Stepper 的Flutter组件,它支持三种样式:system、outlined和textfield。其中,system和outlined样式分别使用IconButton实现增减按钮,而textfield样式使用TextField实现数值输入和增减按钮。
在使用该组件时,需要传入一些必要的参数,包括最小值、最大值、步长、初始值和一个回调函数,用于处理数值变化事件。根据传入的样式类型,该组件会渲染出不同的界面,并且在用户点击增减按钮或者输入框中输入数值时,会根据步长和边界条件计算出新的数值,并且通过回调函数将新的数值传递给父组件进行处理。
在该组件的实现过程中,使用了TextEditingController和FocusNode来控制输入框中的数值以及输入框的聚焦状态。同时,使用了setState方法来更新界面上的数值和按钮状态。
如果您需要使用该组件,可以根据自己的需求选择不同的样式,同时通过传入必要的参数和回调函数来实现相应的功能
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_templet_project/extensions/ddlog.dart';
enum NumberStepperStyle {
system,
outlined,
textfield,
}
///自定义数值增减 Stepper
class NumberStepper extends StatefulWidget {
NumberStepper({
required this.minValue,
required this.maxValue,
required this.stepValue,
this.iconSize = 24,
required this.value,
this.color = Colors.blue,
this.style = NumberStepperStyle.system,
this.radius = 5.0,
this.wraps = true,
required this.block,
});
final int minValue;
final int maxValue;
final int stepValue;
final double iconSize;
int value;
final bool wraps;
final Color color;
final NumberStepperStyle style;
final double radius;
void Function(int value) block;
@override
_NumberStepperState createState() => _NumberStepperState();
}
class _NumberStepperState extends State<NumberStepper> {
// 控制器
final _textController = TextEditingController();
// 焦点
final focusNode1 = FocusNode();
@override
void initState() {
// TODO: implement initState
_textController.text = "${widget.value}";
ddlog(_textController.text);
super.initState();
}
@override
Widget build(BuildContext context) {
// return buildOther(context);
switch (widget.style) {
case NumberStepperStyle.outlined:
return buildOutlinedStyle(context);
break;
case NumberStepperStyle.textfield:
return buildTexfieldStyle(context);
default:
break;
}
return buildSystemStyle(context);
}
Widget buildSystemStyle(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: widget.iconSize,
height: widget.iconSize,
decoration: BoxDecoration(
color: widget.color,
borderRadius: BorderRadius.circular(widget.radius),
border: Border.all(color: widget.color, width: 1), // 边色与边宽度
),
child: IconButton(
icon: Icon(Icons.remove, size: widget.iconSize),
// iconSize: widget.iconSize,
padding: EdgeInsets.zero,
color: Colors.white,
onPressed: () {
go(-widget.stepValue);
},
),
),
Container(
width: widget.value.toString().length*18*widget.iconSize/30,
// width: widget.iconSize + 20,
child: Text('${widget.value}',
style: TextStyle(
fontSize: widget.iconSize * 0.8,
),
textAlign: TextAlign.center,
),
),
Container(
width: widget.iconSize,
height: widget.iconSize,
decoration: BoxDecoration(
color: widget.color,
borderRadius: BorderRadius.circular(widget.radius),
border: Border.all(color: widget.color, width: 1), // 边色与边宽度
),
child: IconButton(
icon: Icon(Icons.add, size: widget.iconSize,),
// iconSize: widget.iconSize,
padding: EdgeInsets.zero,
color: Colors.white,
onPressed: () {
setState(() {
go(widget.stepValue);
});
},
),
),
],
);
}
Widget buildOutlinedStyle(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: widget.iconSize,
height: widget.iconSize,
// color: Theme.of(context).primaryColor,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(widget.radius),
border: Border.all(color: widget.color, width: 1), // 边色与边宽度
),
child: IconButton(
icon: Icon(Icons.remove, size: widget.iconSize),
// iconSize: widget.iconSize,
padding: EdgeInsets.zero,
color: widget.color,
onPressed: () {
go(-widget.stepValue);
},
),
),
Container(
width: widget.value.toString().length*18*widget.iconSize/30,
// width: widget.iconSize + 20,
child: Text('${widget.value}',
style: TextStyle(
fontSize: widget.iconSize * 0.8,
),
textAlign: TextAlign.center,
),
),
Container(
width: widget.iconSize,
height: widget.iconSize,
// color: Theme.of(context).primaryColor,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(widget.radius),
border: Border.all(color: widget.color, width: 1), // 边色与边宽度
),
child: IconButton(
icon: Icon(Icons.add, size: widget.iconSize),
// iconSize: widget.iconSize,
padding: EdgeInsets.zero,
color: widget.color,
onPressed: () {
setState(() {
go(widget.stepValue);
});
},
),
),
],
);
}
Widget buildTexfieldStyle(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: TextField(
enableInteractiveSelection: false,
toolbarOptions: ToolbarOptions(
copy:false,
paste: false,
cut: false,
selectAll: false,
//by default all are disabled 'false'
),
controller: _textController,
decoration: InputDecoration(
// labelText: "请输入内容",//输入框内无文字时提示内容,有内容时会自动浮在内容上方
// helperText: "随便输入文字或数字", //输入框底部辅助性说明文字
prefixIcon:IconButton(
icon: Icon(
Icons.remove,
size: widget.iconSize,
),
onPressed: (){
// go(-widget.stepValue);
setState(() {
go(-widget.stepValue);
_textController.text = "${widget.value}";
});
},
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4.0) //圆角大小
),
suffixIcon: IconButton(
icon: Icon(
Icons.add,
size: widget.iconSize,
),
onPressed: (){
// go(widget.stepValue);
setState(() {
// FocusScope.of(context).requestFocus(FocusNode());
go(widget.stepValue);
_textController.text = "${widget.value}";
});
},
),
contentPadding: const EdgeInsets.only(bottom:8)
),
keyboardType: TextInputType.number,
),
),
],
);
}
void go(int stepValue) {
setState(() {
if (stepValue < 0 && (widget.value == widget.minValue || widget.value + stepValue < widget.minValue)) {
ddlog("it's minValue!");
if (widget.wraps) widget.value = widget.maxValue;
widget.block(widget.value);
return;
}
if (stepValue > 0 && (widget.value == widget.maxValue || widget.value + stepValue > widget.maxValue)) {
ddlog("it's maxValue!");
if (widget.wraps) widget.value = widget.minValue;
widget.block(widget.value);
return;
}
widget.value += stepValue;
});
widget.block(widget.value);
}
}
首先,这是一个用于实现自定义数值增减 Stepper 的Flutter组件。它支持三种样式:system、outlined和textfield。在使用该组件时,需要传入一些必要的参数,包括最小值、最大值、步长、初始值和一个回调函数,用于处理数值变化事件。
接下来,让我们来看看这段代码的具体实现细节。
enum NumberStepperStyle {
system,
outlined,
textfield,
}
首先定义了一个 NumberStepperStyle 枚举类型,用于表示组件的样式类型。
class NumberStepper extends StatefulWidget {
NumberStepper({
required this.minValue,
required this.maxValue,
required this.stepValue,
this.iconSize = 24,
required this.value,
this.color = Colors.blue,
this.style = NumberStepperStyle.system,
this.radius = 5.0,
this.wraps = true,
required this.block,
});
// ...
}
接着定义了一个 NumberStepper 组件类,它继承自 StatefulWidget 类。在该组件的构造函数中,我们传入了一些必要的参数,包括最小值、最大值、步长、初始值、颜色、样式类型等,并且定义了一个回调函数 block,用于处理数值变化事件。
class _NumberStepperState extends State<NumberStepper> {
// 控制器
final _textController = TextEditingController();
// 焦点
final focusNode1 = FocusNode();
@override
void initState() {
// TODO: implement initState
_textController.text = "${widget.value}";
ddlog(_textController.text);
super.initState();
}
// ...
}
然后,在 NumberStepper 类中定义了一个 _NumberStepperState 私有类,用于管理组件的状态。在该类中,我们定义了一个控制器 _textController 和一个焦点 focusNode1,用于控制输入框中的数值以及输入框的聚焦状态。在 initState 方法中,我们将初始值设置为输入框中的默认值。
@override
Widget build(BuildContext context) {
switch (widget.style) {
case NumberStepperStyle.outlined:
return buildOutlinedStyle(context);
break;
case NumberStepperStyle.textfield:
return buildTexfieldStyle(context);
default:
break;
}
return buildSystemStyle(context);
}
在 build 方法中,根据传入的样式类型,选择不同的样式进行渲染。
void go(int stepValue) {
setState(() {
if (stepValue < 0 && (widget.value == widget.minValue || widget.value + stepValue < widget.minValue)) {
ddlog("it's minValue!");
if (widget.wraps) widget.value = widget.maxValue;
widget.block(widget.value);
return;
}
if (stepValue > 0 && (widget.value == widget.maxValue || widget.value + stepValue > widget.maxValue)) {
ddlog("it's maxValue!");
if (widget.wraps) widget.value = widget.minValue;
widget.block(widget.value);
return;
}
widget.value += stepValue;
});
widget.block(widget.value);
}
最后,在 go 方法中,根据步长和边界条件计算出新的数值,并且通过回调函数将新的数值传递给父组件进行处理。
void go(int stepValue) {
setState(() {
// 判断是否到达最小值,如果是,且设置了 wraps 属性为 true,则将值设置为最大值
if (stepValue < 0 && (widget.value == widget.minValue || widget.value + stepValue < widget.minValue)) {
ddlog("it's minValue!");
if (widget.wraps) widget.value = widget.maxValue;
widget.block(widget.value);
return; // 直接结束函数
}
// 判断是否到达最大值,如果是,且设置了 wraps 属性为 true,则将值设置为最小值
if (stepValue > 0 && (widget.value == widget.maxValue || widget.value + stepValue > widget.maxValue)) {
ddlog("it's maxValue!");
if (widget.wraps) widget.value = widget.minValue;
widget.block(widget.value);
return; // 直接结束函数
}
// 将当前值加上步长
widget.value += stepValue;
});
// 调用 block 函数通知外部组件当前的值已经发生了变化
widget.block(widget.value);
}
以上就是该组件的主要实现逻辑和代码结构。