Skip to content

Instantly share code, notes, and snippets.

@sonvp
Created June 19, 2022 14:46
Show Gist options
  • Save sonvp/85d8c405a2e560747bf676feed280a29 to your computer and use it in GitHub Desktop.
Save sonvp/85d8c405a2e560747bf676feed280a29 to your computer and use it in GitHub Desktop.
class _TrianglePainter extends CustomPainter {
final double lineSize;
_TrianglePainter({this.lineSize = 16});
@override
void paint(Canvas canvas, Size size) {
Path path = Path();
path.moveTo(0, 0);
path.lineTo(lineSize, 0);
path.lineTo(lineSize / 2, tan(pi / 3) * lineSize / 2);
path.close();
Paint paint = Paint();
paint.color = Color.fromARGB(255, 118, 165, 248);
paint.style = PaintingStyle.fill;
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
/// The controller for the ruler picker
/// init the ruler value from the controller
/// 用于 RulerPicker 的控制器,可以在构造函数里初始化默认值
class RulerPickerController extends ValueNotifier<num> {
RulerPickerController({num value = 0.0}) : super(value);
num get value => super.value;
set value(num newValue) {
super.value = newValue;
}
}
typedef void ValueChangedCallback(num value);
/// RulerPicker 标尺选择器
/// [width] 必须是具体的值,包括父级container的width,不能是 double.infinity,
/// 可以传入MediaQuery.of(context).size.width
class RulerPicker extends StatefulWidget {
final ValueChangedCallback? onValueChange;
final double width;
final double height;
final Color backgroundColor;
/// the marker on the ruler, default is a arrow
final Widget? marker;
/// the fraction digits of the picker value
final int fractionDigits;
final RulerPickerController? controller;
RulerPicker({
this.onValueChange,
required this.width,
required this.height,
this.backgroundColor = Colors.white,
this.fractionDigits = 0,
this.controller,
this.marker,
});
@override
State<StatefulWidget> createState() {
return RulerPickerState();
}
}
// todo 实现 animateTo
class RulerPickerState extends State<RulerPicker> {
final double _distance = 10;
final _scrollController = ScrollController();
late ValueNotifier<double> _scaleNotifier;
double _offset = 0;
double _widthItem = 10;
double _scale = 1;
double lastOffset = 0;
bool _isScaleEnd = true;
bool _isJumpTo = false;
bool isPosFixed = false;
double contentSize1 = 0;
double _value1 =0;
double _scale1 =1;
@override
void initState() {
super.initState();
_scaleNotifier = ValueNotifier<double>(_scale);
_scrollController.addListener(() {
if (!_isJumpTo) {
_offset = _scrollController.offset;
// print('scrollController.addListener contentSize $contentSize1');
print('scrollController.addListener _offset $_offset');
_value1 = double.parse((_scrollController.offset / _widthItem)
.toStringAsFixed(widget.fractionDigits));
if (_value1 < 0) _value1 = 0;
if (widget.onValueChange != null) {
widget.onValueChange!(_value1);
}
}
});
}
/// default mark
Widget mark() {
/// default mark arrow
Widget triangle() {
return SizedBox(
width: 16,
height: 16,
child: CustomPaint(
painter: _TrianglePainter(),
),
);
}
return SizedBox(
width: 16,
height: 34,
child: Stack(
children: <Widget>[
triangle(),
Container(
width: 3,
height: 34,
margin: const EdgeInsets.only(left: 6),
color: const Color.fromARGB(255, 118, 165, 248),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Container(
width: widget.width,
height: widget.height,
color: widget.backgroundColor,
child: GestureDetector(
onScaleStart: (details) {
_isScaleEnd = false;
print('onScaleStart');
},
onScaleUpdate: (details) async {
if (!_isScaleEnd) {
// print('_onScaleUpdate scale ${details.scale}');
if(details.scale > 1) {
if (_scale < 5) {
_scale = _scale + (details.scale - 1);
String inString = _scale.toStringAsFixed(2);
_scale = double.parse(inString);
}
} else if(details.scale < 1){
if (_scale - (1 - details.scale) >= 1) {
_scale = _scale - (1 - details.scale);
String inString = _scale.toStringAsFixed(2);
_scale = double.parse(inString);
}
}
_scaleNotifier.value = _scale ;
print('_onScaleUpdate _scale $_scale');
_isJumpTo = true;
_scrollController.jumpTo(_value1 * _distance * _scale);
_isJumpTo= false;
}
},
onScaleEnd: (details) {
_isScaleEnd = true;
// _scale = _scaleNotifier.value;
print('onScaleEnd');
},
child: ValueListenableBuilder<double>(
valueListenable: _scaleNotifier,
builder: (ctx, _value, _) {
_widthItem = _distance * _value;
// print('_widthItem $_widthItem scale $_value');
print('----------------------------');
return Stack(
children: <Widget>[
ListView.builder(
controller: _scrollController,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
// print('index $index');
return Container(
padding: index == 0
? EdgeInsets.only(
left: widget.width / 2,
)
: EdgeInsets.zero,
child: SizedBox(
width: _widthItem,
child: Stack(
clipBehavior: Clip.none,
children: <Widget>[
Container(
width: index % 10 == 0 ? 2 : 1,
height: index % 10 == 0 ? 32 : 20,
color: const Color.fromARGB(255, 188, 194, 203),
),
Positioned(
bottom: 5,
width: 50,
left: -25,
child: index % 10 == 0
? Container(
alignment: Alignment.center,
child: Text(
index.toString(),
style: const TextStyle(
color: Color.fromARGB(255, 188, 194, 203),
fontSize: 14,
),
),
)
: Container(),
),
],
),
),
);
},
),
Positioned(
left: widget.width / 2 - 6,
child: widget.marker ?? mark(),
),
],
);
},
),
),
);
}
@override
void dispose() {
super.dispose();
_scrollController.dispose();
}
void setPositionByValue(num value) {
num targetPos = value * 10;
if (targetPos < 0) targetPos = 0;
_scrollController.jumpTo(
targetPos.toDouble(),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment