Skip to content

Instantly share code, notes, and snippets.

@iapicca
Last active November 9, 2022 14:37
Show Gist options
  • Select an option

  • Save iapicca/5fc8d8f465bfc2c2fcf9385e45788bb6 to your computer and use it in GitHub Desktop.

Select an option

Save iapicca/5fc8d8f465bfc2c2fcf9385e45788bb6 to your computer and use it in GitHub Desktop.
issue_28814 (b)
import 'package:flutter/material.dart';
import 'dart:math' show Random;
import 'dart:ui' as ui;
extension PathWrapperX on Path {
PathWrapper wrap() => PathWrapper(this);
}
class PathWrapper {
final Path path;
PathWrapper(this.path) : hashCode = path.hashCode;
@override
bool operator ==(Object other) {
if (other is! PathWrapper) {
return false;
}
debugPrint('new hashcode: ${other.hashCode}' ' ' 'old hashcode: $hashCode');
return hashCode == other.hashCode;
}
@override
final int hashCode;
}
extension PathFromSizeX on Size {
Path path(double c) => Path()
..moveTo(0, height * .5)
..quadraticBezierTo(
width * c,
height,
width,
height * c,
);
}
final path = Size.zero.path(0).hashCode;
extension RandomX on Size {
Offset randomOffset() => Offset(
Random().nextInt(width.truncate()).toDouble(),
Random().nextInt(height.truncate()).toDouble(),
);
}
void main() => runApp(const MaterialApp(home: MyHomePage()));
class MyHomePage extends StatefulWidget {
final Size size;
const MyHomePage({super.key, this.size = const Size(200, 400)});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late final ValueNotifier<int> _dotCount;
late final ValueNotifier<double> _pathCoefficient;
late final ValueNotifier<PathWrapper> _pathWrapper;
void _coefficientListener() => _pathWrapper.value = _getPathWrapper();
PathWrapper _getPathWrapper() =>
widget.size.path(_pathCoefficient.value).wrap();
void _increaseDotCount() => _dotCount.value++;
void _updatePathCx() => _pathCoefficient.value += .3;
@override
void initState() {
_dotCount = ValueNotifier(0);
_pathCoefficient = ValueNotifier(1)..addListener((_coefficientListener));
_pathWrapper = ValueNotifier(_getPathWrapper());
super.initState();
}
@override
void dispose() {
_dotCount.dispose();
_pathCoefficient
..removeListener(_coefficientListener)
..dispose();
_pathWrapper.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ValueListenableBuilder<PathWrapper>(
valueListenable: _pathWrapper,
builder: (context, pathWrapper, child) =>
ValueListenableBuilder<int>(
valueListenable: _dotCount,
builder: (context, dotCount, child) => CustomPaint(
size: widget.size,
painter: MyCustomPainter(
pathWrapper: pathWrapper,
dots: dotCount,
),
),
),
),
TextButton(
onPressed: _increaseDotCount,
child: const Icon(Icons.plus_one),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: _updatePathCx,
child: const Icon(Icons.add),
),
);
}
class MyCustomPainter extends CustomPainter {
final PathWrapper pathWrapper;
final int dots;
final Color color;
final double pathStrokeWidth;
final double dotsStrokeWidth;
const MyCustomPainter({
required this.pathWrapper,
required this.dots,
this.color = Colors.red,
this.pathStrokeWidth = .8,
this.dotsStrokeWidth = 4,
});
@override
void paint(Canvas canvas, Size size) {
canvas.drawPath(
pathWrapper.path,
Paint()
..color = color
..style = PaintingStyle.stroke
..strokeWidth = pathStrokeWidth,
);
canvas.drawPoints(
ui.PointMode.points,
[for (int i = 0; i < dots; ++i) size.randomOffset()],
Paint()
..color = Colors.black
..strokeWidth = dotsStrokeWidth
..strokeCap = StrokeCap.round,
);
}
@override
bool shouldRepaint(MyCustomPainter oldDelegate) {
final value = oldDelegate.pathWrapper != pathWrapper;
debugPrint('should repaint: $value');
return value;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment