Skip to content

Instantly share code, notes, and snippets.

@jogboms
Last active May 30, 2022 21:00
Show Gist options
  • Save jogboms/9a347da5ae1b6ae41d5d2075dfafa9b7 to your computer and use it in GitHub Desktop.
Save jogboms/9a347da5ae1b6ae41d5d2075dfafa9b7 to your computer and use it in GitHub Desktop.
Flutter guild presentation - CustomPainter demo
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(home: App()));
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) => const Scaffold(body: Center(child: SliderWidget()));
}
class SliderWidget extends StatefulWidget {
const SliderWidget({super.key});
@override
State<SliderWidget> createState() => _SliderWidgetState();
}
class _SliderWidgetState extends State<SliderWidget> {
@override
Widget build(BuildContext context) {
return SizedBox.fromSize(
size: Constants.size,
child: GestureDetector(
onHorizontalDragStart: (details) => _onDragStart(details.localPosition),
onHorizontalDragUpdate: (details) => _onDragUpdate(details.localPosition),
child: CustomPaint(
painter: SliderPainter(
dragPosition: _dragPosition,
),
),
),
);
}
double _dragPosition = 0.0;
bool _canDragKnob = false;
void _onDragStart(Offset position) {
final knobRect = Offset(_dragPosition, 0) & Constants.knobSize;
final knobLeftPosition = position.dx + Constants.knobRadius;
_canDragKnob = knobLeftPosition >= knobRect.left && knobLeftPosition < knobRect.right;
}
void _onDragUpdate(Offset position) {
if (!_canDragKnob) {
return;
}
setState(() {
_dragPosition = position.dx.clamp(0.0, Constants.size.width);
});
}
}
class SliderPainter extends CustomPainter {
SliderPainter({required this.dragPosition});
final double dragPosition;
@override
void paint(Canvas canvas, Size size) {
final knobTrackRect = Offset.zero.shift(Constants.knobToTrackPadding) & Constants.knobTrackSize;
canvas.drawRRect(
Constants.borderRadius.toRRect(knobTrackRect),
Paint()..shader = Constants.gradient.createShader(knobTrackRect),
);
final lowerLimit = knobTrackRect.left;
final upperLimit = knobTrackRect.right;
final knobLeftPosition = dragPosition.clamp(lowerLimit, upperLimit);
final knobColor = Constants.gradientColors.mix(lowerLimit, upperLimit, knobLeftPosition);
final knobCenter = Offset(knobLeftPosition, size.height / 2);
canvas
..drawShadow(
Path()..addOval(Rect.fromCircle(center: knobCenter, radius: Constants.knobRadius)),
Constants.knobShadowColor,
Constants.knobElevation,
true,
)
..drawCircle(
knobCenter,
Constants.knobRadius,
Paint()..color = Constants.knobBackgroundColor,
)
..drawCircle(
knobCenter,
Constants.knobRadius - Constants.knobToTrackPadding,
Paint()..color = knobColor,
);
}
@override
bool shouldRepaint(SliderPainter oldDelegate) => dragPosition != oldDelegate.dragPosition;
}
class Constants {
Constants._();
static const size = Size(720, 120);
static final knobDimension = size.height;
static final knobSize = Size.square(knobDimension);
static final knobRadius = knobDimension / 2;
static final knobToTrackPadding = knobDimension * .15;
static final knobTrackSize = Size(size.width - knobToTrackPadding * 2, size.height - knobToTrackPadding * 2);
static const knobBackgroundColor = Colors.white;
static const knobElevation = 8.0;
static const knobShadowColor = Colors.black87;
static final borderRadius = BorderRadius.circular(size.height / 2);
static const gradientColors = [Colors.blueAccent, Colors.purpleAccent];
static const gradient = LinearGradient(colors: gradientColors);
}
// https://stackoverflow.com/a/55088673/8236404
double Function(double input) interpolate({
double inputMin = 0,
double inputMax = 1,
double outputMin = 0,
double outputMax = 1,
}) {
assert(inputMin != inputMax || outputMin != outputMax);
final diff = (outputMax - outputMin) / (inputMax - inputMin);
return (input) => ((input - inputMin) * diff) + outputMin;
}
extension ShiftExtension on Offset {
Offset shift(double delta) => translate(delta, delta);
}
extension MixGradientColor on List<Color> {
Color mix(double min, double max, double value) =>
Color.lerp(first, last, interpolate(inputMin: min, inputMax: max)(value))!;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment