Last active
December 27, 2022 16:40
-
-
Save CoderNamedHendrick/ad3169274f627c907bc9dc4d1117096f to your computer and use it in GitHub Desktop.
Quad selector widget
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 'dart:math'; | |
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(const MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Demo', | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
), | |
home: const MyHomePage(), | |
); | |
} | |
} | |
class MyHomePage extends StatefulWidget { | |
const MyHomePage({Key? key}) : super(key: key); | |
@override | |
State<MyHomePage> createState() => _MyHomePageState(); | |
} | |
class _MyHomePageState extends State<MyHomePage> { | |
Quadrant? groupQuad; | |
static const size = 300.0; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: Center( | |
child: Stack( | |
children: [ | |
QuadrantWidget( | |
quadrant: Quadrant.one, | |
size: size, | |
groupQuad: groupQuad, | |
onChanged: (value) { | |
setState(() => groupQuad = value); | |
}, | |
), | |
QuadrantWidget( | |
quadrant: Quadrant.two, | |
size: size, | |
groupQuad: groupQuad, | |
onChanged: (value) { | |
setState(() => groupQuad = value); | |
}, | |
), | |
QuadrantWidget( | |
quadrant: Quadrant.three, | |
size: size, | |
groupQuad: groupQuad, | |
onChanged: (value) { | |
setState(() => groupQuad = value); | |
}, | |
), | |
QuadrantWidget( | |
quadrant: Quadrant.four, | |
size: size, | |
groupQuad: groupQuad, | |
onChanged: (value) { | |
setState(() => groupQuad = value); | |
}, | |
), | |
GestureDetector( | |
onTap: () { | |
print('Tapped Area, EmojiPainter'); | |
}, | |
child: CustomPaint( | |
painter: const EmojiPainter(size), | |
child: SizedBox( | |
height: size, | |
width: size, | |
child: Align( | |
alignment: Alignment.center, | |
child: SizedBox( | |
width: 0.381 * size, | |
height: 0.381 * size, | |
child: Stack( | |
children: [ | |
Align( | |
alignment: Alignment.center, | |
child: Container( | |
// we got the 0.381 from our scale factor in emojiPainter. | |
width: 0.381 * size - 40, | |
height: 0.381 * size - 40, | |
decoration: const BoxDecoration( | |
color: Colors.red, | |
shape: BoxShape.circle, | |
), | |
), | |
), | |
Align( | |
alignment: const Alignment(0.4, 0.65), | |
child: Container( | |
height: 20, | |
width: 20, | |
decoration: const BoxDecoration( | |
color: Colors.purple, | |
shape: BoxShape.circle, | |
), | |
), | |
) | |
], | |
), | |
), | |
), | |
), | |
), | |
), | |
], | |
), | |
), | |
); | |
} | |
} | |
enum Quadrant { one, two, three, four } | |
class QuadrantWidget extends StatelessWidget { | |
const QuadrantWidget( | |
{super.key, | |
required this.quadrant, | |
required this.size, | |
this.groupQuad, | |
this.onChanged, | |
this.iconData, | |
this.text}); | |
final Quadrant quadrant; | |
final double size; | |
final Quadrant? groupQuad; | |
final ValueChanged<Quadrant>? onChanged; | |
final IconData? iconData; | |
final String? text; | |
bool get _selected => quadrant == groupQuad; | |
void updateValue(Quadrant value) { | |
onChanged?.call(value); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return GestureDetector( | |
onTap: () => updateValue(quadrant), | |
child: CustomPaint( | |
painter: QuadrantPainter( | |
quadrant: quadrant, isSelected: _selected, paintSize: size), | |
child: SizedBox( | |
height: size, | |
width: size, | |
child: Align( | |
alignment: _columnAlignment, | |
child: Column( | |
mainAxisSize: MainAxisSize.min, | |
children: [ | |
Icon( | |
iconData ?? Icons.screen_share, | |
color: _selected ? Colors.white : null, | |
), | |
Text( | |
text ?? 'Shared', | |
style: TextStyle( | |
color: _selected ? Colors.white : null, | |
), | |
) | |
], | |
), | |
), | |
), | |
), | |
); | |
} | |
Alignment get _columnAlignment { | |
switch (quadrant) { | |
case Quadrant.one: | |
return const Alignment(0.6, -0.6); | |
case Quadrant.two: | |
return const Alignment(-0.6, -0.6); | |
case Quadrant.three: | |
return const Alignment(-0.6, 0.6); | |
case Quadrant.four: | |
return const Alignment(0.6, 0.6); | |
} | |
} | |
} | |
class QuadrantPainter extends CustomPainter { | |
const QuadrantPainter( | |
{required this.quadrant, | |
required this.paintSize, | |
this.isSelected = false}); | |
final Quadrant quadrant; | |
final bool isSelected; | |
final double paintSize; | |
static double get _sweepAngle { | |
return -pi / 2; | |
} | |
Path get _quadPath { | |
return Path() | |
..moveTo(paintSize / 2, paintSize / 2) | |
..lineTo(_movePosition.dx, _movePosition.dy) | |
..arcTo( | |
Rect.fromLTRB(0, 0, paintSize, paintSize), | |
_startPosition, | |
_sweepAngle, | |
false, | |
) | |
..close(); | |
} | |
Offset get _center { | |
return Offset(paintSize / 2, paintSize / 2); | |
} | |
@override | |
void paint(Canvas canvas, Size size) { | |
final gradient = LinearGradient( | |
colors: const [Color(0xFF9c8cfb), Color(0xFF88dffa)], | |
begin: _alignmentPoints.a, | |
end: _alignmentPoints.b); | |
final paint = Paint() | |
..style = PaintingStyle.fill | |
..color = Colors.white30; | |
final selectPaint = Paint() | |
..style = PaintingStyle.fill | |
..shader = | |
gradient.createShader(Rect.fromPoints(_center, _gradientEndOffset)); | |
if (!isSelected) { | |
final shadowPaint = Paint() | |
..color = Colors.black54.withOpacity(0.65) | |
..style = PaintingStyle.stroke | |
..maskFilter = MaskFilter.blur(BlurStyle.outer, sqrt(10)); | |
canvas.save(); | |
canvas.translate(0, 0); | |
canvas.drawPath( | |
Path() | |
..moveTo(_center.dx, _center.dy) | |
..lineTo(_movePosition.dx, _movePosition.dy), | |
shadowPaint); | |
canvas.restore(); | |
} | |
canvas.drawPath(_quadPath, isSelected ? selectPaint : paint); | |
} | |
Offset get _movePosition { | |
switch (quadrant) { | |
case Quadrant.one: | |
return Offset(paintSize, paintSize / 2); | |
case Quadrant.two: | |
return Offset(paintSize / 2, 0); | |
case Quadrant.three: | |
return Offset(0, paintSize / 2); | |
case Quadrant.four: | |
return Offset(paintSize / 2, paintSize); | |
} | |
} | |
@override | |
bool shouldRepaint(covariant CustomPainter oldDelegate) { | |
return true; | |
} | |
@override | |
bool? hitTest(Offset position) { | |
return _quadPath.contains(position); | |
} | |
double get _startPosition { | |
switch (quadrant) { | |
case Quadrant.one: | |
return 0; | |
case Quadrant.two: | |
return 3 * pi / 2; | |
case Quadrant.three: | |
return pi; | |
case Quadrant.four: | |
return pi / 2; | |
} | |
} | |
Offset get _gradientEndOffset { | |
switch (quadrant) { | |
case Quadrant.one: | |
return Offset(paintSize, 0); | |
case Quadrant.two: | |
return const Offset(0, 0); | |
case Quadrant.three: | |
return Offset(0, paintSize); | |
case Quadrant.four: | |
return Offset(paintSize, paintSize); | |
} | |
} | |
AlignmentPoints get _alignmentPoints { | |
switch (quadrant) { | |
case Quadrant.one: | |
return const AlignmentPoints(Alignment.topRight, Alignment.bottomLeft); | |
case Quadrant.two: | |
return const AlignmentPoints(Alignment.topLeft, Alignment.bottomRight); | |
case Quadrant.three: | |
return const AlignmentPoints(Alignment.bottomLeft, Alignment.topRight); | |
case Quadrant.four: | |
return const AlignmentPoints(Alignment.bottomRight, Alignment.topLeft); | |
} | |
} | |
} | |
class EmojiPainter extends CustomPainter { | |
final double paintSize; | |
const EmojiPainter(this.paintSize); | |
Offset get _center { | |
return Offset(paintSize / 2, paintSize / 2); | |
} | |
double get _radius { | |
return (0.381 * paintSize) / 2; | |
} | |
@override | |
void paint(Canvas canvas, Size size) { | |
final paint = Paint()..color = Colors.white; | |
canvas.drawCircle(_center, _radius, paint); | |
} | |
@override | |
bool shouldRepaint(covariant CustomPainter oldDelegate) { | |
return false; | |
} | |
@override | |
bool? hitTest(Offset position) { | |
Path path = Path() | |
..addOval(Rect.fromCircle(center: _center, radius: _radius)); | |
return path.contains(position); | |
} | |
} | |
class AlignmentPoints { | |
final Alignment a; | |
final Alignment b; | |
const AlignmentPoints(this.a, this.b); | |
} |
This is super dope, if the designer talk anything ,na to give her keyboard make she go build herself
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Mostly done.