import 'package:ducafe_ui_core/ducafe_ui_core.dart';
import 'package:flutter/material.dart';
import '../index.dart';
/// 星级列表组件
class StarsListWidget extends StatelessWidget {
/// 点击事件
final Function(int value)? onTap;
/// 图标 data
final IconData? iconData;
/// 星级数量
final int starNum;
/// 选中的星级
final int value;
/// 星级大小
final double? size;
// 颜色
final Color? color;
// 选中颜色
final Color? selectedColor;
// 元素间距
final double? spacing;
// 行间距
final double? runSpacing;
// 构造
const StarsListWidget({
super.key,
this.onTap,
this.size = 24,
this.iconData,
this.color,
this.selectedColor,
this.spacing = 5,
this.runSpacing = 5,
this.starNum = 5,
this.value = 0,
});
@override
Widget build(BuildContext context) {
Color itemColor = color ?? context.colors.scheme.surfaceContainerHighest;
Color itemSelectedColor = selectedColor ?? context.colors.scheme.primary;
List<Widget> ws = [];
for (var i = 1; i <= starNum; i++) {
Widget item = IconWidget.icon(
iconData ?? Icons.star,
size: size ?? 12,
color: i <= value ? itemSelectedColor : itemColor,
);
// 绑定点击事件
item = item.onTap(() {
// 如果当前值是1,并且点击的也是1,则变成0
if (value == 1 && i == value) {
onTap?.call(0);
} else {
onTap?.call(i);
}
});
ws.add(item);
}
return ws.toWrap(
spacing: spacing ?? AppSpace.listItem,
runSpacing: runSpacing ?? AppSpace.listRow,
);
}
}
item.onTap
// 绑定点击事件
item = item.onTap(() {....});
上面其实是调用自己的扩展方法,创建点击事件
/// 手势
Widget onTap(
GestureTapCallback? onTap, {
Key? key,
HitTestBehavior? behavior,
bool excludeFromSemantics = false,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) =>
GestureDetector(
key: key,
onTap: onTap, // 在这里绑定事件
behavior: behavior ?? HitTestBehavior.opaque,
excludeFromSemantics: excludeFromSemantics,
dragStartBehavior: dragStartBehavior,
child: this, // 把原来的widget放进去
);
for代码:i是固定值
List<Widget> ws = [];
for (var i = 1; i <= starNum; i++) {
Widget item = IconWidget.icon(
iconData ?? Icons.star,
size: size ?? 12,
color: i <= value ? itemSelectedColor : itemColor,
);
// 绑定点击事件
item = item.onTap(() {
// 如果当前值是1,并且点击的也是1,则变成0
if (value == 1 && i == value) {
onTap?.call(0);
} else {
onTap?.call(i);
}
});
ws.add(item);
}
上面代码相当于以下代码:
List<Widget> ws = [
// 第 1 颗星
IconWidget.icon(
iconData ?? Icons.star,
size: size ?? 12,
color: 1 <= value ? itemSelectedColor : itemColor,
).onTap(() {
if (value == 1 && 1 == value) {
onTap?.call(0);
} else {
onTap?.call(1);
}
}),
// 第 2 颗星
IconWidget.icon(
iconData ?? Icons.star,
size: size ?? 12,
color: 2 <= value ? itemSelectedColor : itemColor,
).onTap(() {
if (value == 1 && 2 == value) {
onTap?.call(0);
} else {
onTap?.call(2);
}
}),
// 第 3 颗星
IconWidget.icon(
iconData ?? Icons.star,
size: size ?? 12,
color: 3 <= value ? itemSelectedColor : itemColor,
).onTap(() {
if (value == 1 && 3 == value) {
onTap?.call(0);
} else {
onTap?.call(3);
}
}),
// 第 4 颗星
IconWidget.icon(
iconData ?? Icons.star,
size: size ?? 12,
color: 4 <= value ? itemSelectedColor : itemColor,
).onTap(() {
if (value == 1 && 4 == value) {
onTap?.call(0);
} else {
onTap?.call(4);
}
}),
// 第 5 颗星
IconWidget.icon(
iconData ?? Icons.star,
size: size ?? 12,
color: 5 <= value ? itemSelectedColor : itemColor,
).onTap(() {
if (value == 1 && 5 == value) {
onTap?.call(0);
} else {
onTap?.call(5);
}
}),
];
实际上,生成了5个星星,每一个星星有一个绑定函数。
当 value == 1 并且 i == 1 时,说明 当前只点亮了第 1 颗星,而且又点了第 1 颗星。
这种情况下,代码会执行 onTap?.call(0);,也就是把评分重置为 0 颗星。
如果我初始化的value不等于1,评分就不会为0。
效果
(value == 1 && i == 1)这个判断实现的效果是:第二次点击一颗星会取消所有星星。
下面是绑定的点击函数
// 星级选中
void onStarTap(int value) {
starValue = value;
update(["filter_stars"]);
}
以下是绑定逻辑的代码
if (value == 1 && i == 1) {
onTap?.call(0);
} else {
onTap?.call(i);
}
完整流程可以这样理解:
- 初始状态:
starValue = 0,界面上没有星星被点亮。 - 第一次点击第 1 颗星
- 此时
value = 0,i = 1,不满足value == 1 && i == 1 - 执行
onTap?.call(1)→starValue变为1 GetBuilder触发重建 → 显示 1 颗星
- 此时
- 第二次再点击第 1 颗星
- 此时
value = 1,i = 1,满足value == 1 && i == 1 - 执行
onTap?.call(0)→starValue变为0 GetBuilder触发重建 → 所有星星熄灭
- 此时