Last active
May 30, 2022 21:01
-
-
Save jogboms/611fa584c0ed332bd7298e3d863b20ee to your computer and use it in GitHub Desktop.
Flutter guild presentation - Widget demo
This file contains 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/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> { | |
final GlobalKey _sliderKey = GlobalKey(); | |
final GlobalKey _knobKey = GlobalKey(); | |
final GlobalKey _knobTrackKey = GlobalKey(); | |
double _knobLeftPosition = 0.0; | |
Color _knobColor = Constants.gradientColors.first; | |
@override | |
Widget build(BuildContext context) { | |
return SizedBox.fromSize( | |
key: _sliderKey, | |
size: Constants.size, | |
child: Stack( | |
clipBehavior: Clip.none, | |
alignment: Alignment.center, | |
children: [ | |
Positioned( | |
width: Constants.knobTrackSize.width, | |
height: Constants.knobTrackSize.height, | |
child: DecoratedBox( | |
key: _knobTrackKey, | |
decoration: BoxDecoration( | |
gradient: Constants.gradient, | |
borderRadius: Constants.borderRadius, | |
), | |
), | |
), | |
Positioned( | |
top: 0, | |
bottom: 0, | |
left: _knobLeftPosition, | |
width: Constants.knobDimension, | |
child: GestureDetector( | |
key: _knobKey, | |
onHorizontalDragStart: (_) => _onDragStart(), | |
onHorizontalDragUpdate: (details) => _onDragUpdate(details.primaryDelta!), | |
child: Material( | |
shape: CircleBorder( | |
side: BorderSide( | |
color: Constants.knobBackgroundColor, | |
width: Constants.knobToTrackPadding, | |
), | |
), | |
shadowColor: Constants.knobShadowColor, | |
color: _knobColor, | |
elevation: Constants.knobElevation, | |
), | |
), | |
) | |
], | |
), | |
); | |
} | |
double _dragLeftPosition = 0.0; | |
void _onDragStart() { | |
_dragLeftPosition = _knobKey.rect.left; | |
} | |
void _onDragUpdate(double delta) { | |
final sliderRect = _sliderKey.rect; | |
final knobTrackRect = _knobTrackKey.rect; | |
final lowerLimit = knobTrackRect.left - Constants.knobRadius; | |
final upperLimit = knobTrackRect.right - Constants.knobRadius; | |
final dragLeftPosition = (_dragLeftPosition + delta).clamp( | |
lowerLimit, | |
upperLimit, | |
); | |
if (_dragLeftPosition == dragLeftPosition) { | |
return; | |
} | |
setState(() { | |
_dragLeftPosition = dragLeftPosition; | |
_knobLeftPosition = _dragLeftPosition - sliderRect.left; | |
_knobColor = Constants.gradientColors.mix(lowerLimit, upperLimit, _dragLeftPosition); | |
}); | |
} | |
} | |
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 RenderBoxRectExtension<T extends State> on GlobalKey<T> { | |
Rect get rect { | |
final renderBox = currentContext!.findRenderObject() as RenderBox; | |
return renderBox.localToGlobal(Offset.zero) & renderBox.size; | |
} | |
} | |
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