Created
June 19, 2022 14:46
-
-
Save sonvp/85d8c405a2e560747bf676feed280a29 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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