Skip to content

Instantly share code, notes, and snippets.

@medwonuola
Forked from CoderNamedHendrick/main.dart
Last active December 27, 2022 16:54
Show Gist options
  • Save medwonuola/484f4668e1ee554e4ee903eaf7a4d44b to your computer and use it in GitHub Desktop.
Save medwonuola/484f4668e1ee554e4ee903eaf7a4d44b to your computer and use it in GitHub Desktop.
Quad selector widget
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,
),
),
),
AnimatedAlign(
alignment: getAlignment(groupQuad),
duration: const Duration(milliseconds: 300),
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);
}
class Aligns{
static const one = Alignment( -.4, -.65);
static const two = Alignment( .4, -.65);
static const three = Alignment( -.4, .65);
static const four = Alignment( .4, .65);
static const none= Alignment(0, 0);
}
Alignment getAlignment(Quadrant? quad) {
switch(quad) {
case Quadrant.one:
return Aligns.two;
case Quadrant.two:
return Aligns.one;
case Quadrant.three:
return Aligns.three;
case Quadrant.four:
return Aligns.four;
default:
return Aligns.none;
}
}
@medwonuola
Copy link
Author

Made the little circle change position on change

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment