Last active
May 30, 2022 21:03
-
-
Save jogboms/4b69fcd642e0179eb32dcd86dec87656 to your computer and use it in GitHub Desktop.
Flutter guild presentation - RenderObject demo
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
import 'package:flutter/gestures.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/rendering.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 LeafRenderObjectWidget { | |
const SliderWidget({super.key}); | |
@override | |
RenderObject createRenderObject(BuildContext context) => SliderRenderObject(); | |
} | |
class SliderRenderObject extends RenderBox { | |
late final drag = HorizontalDragGestureRecognizer() | |
..onStart = _onDragStart | |
..onUpdate = _onDragUpdate; | |
late Rect knobRect; | |
double dragPosition = 0.0; | |
bool canDragKnob = false; | |
@override | |
void performLayout() { | |
size = Constants.size; | |
} | |
@override | |
bool get isRepaintBoundary => true; | |
@override | |
bool hitTestSelf(Offset position) => true; | |
@override | |
void handleEvent(PointerEvent event, BoxHitTestEntry entry) { | |
if (event is PointerDownEvent) { | |
drag.addPointer(event); | |
} | |
} | |
@override | |
void paint(PaintingContext context, Offset offset) { | |
final canvas = context.canvas; | |
final knobTrackRect = offset.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, offset.dy + size.height / 2); | |
knobRect = Rect.fromCircle(center: knobCenter, radius: Constants.knobRadius); | |
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, | |
); | |
} | |
void _onDragStart(DragStartDetails details) { | |
canDragKnob = knobRect.contains(details.localPosition); | |
} | |
void _onDragUpdate(DragUpdateDetails details) { | |
if (!canDragKnob) { | |
return; | |
} | |
dragPosition += details.primaryDelta!; | |
markNeedsPaint(); | |
} | |
} | |
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