Skip to content

Instantly share code, notes, and snippets.

@sma
Last active July 12, 2023 03:00
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save sma/c6a9111d58c3deb83711106cec6152ee to your computer and use it in GitHub Desktop.
Save sma/c6a9111d58c3deb83711106cec6152ee to your computer and use it in GitHub Desktop.
A flutter example demonstrating a custom painter drawing selectable rects
// run in DartPad: <https://dartpad.dev/c6a9111d58c3deb83711106cec6152ee>
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: RectsExample()));
}
class RectsExample extends StatefulWidget {
@override
_RectsExampleState createState() => _RectsExampleState();
}
class _RectsExampleState extends State<RectsExample> {
int _index = -1;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Rects(
rects: [
Rect.fromLTRB(10, 20, 30, 40),
Rect.fromLTRB(40, 60, 80, 100),
],
selectedIndex: _index,
onSelected: (index) {
setState(() {
_index = index;
});
},
),
),
);
}
}
class Rects extends StatelessWidget {
final List<Rect> rects;
final void Function(int) onSelected;
final int selectedIndex;
const Rects({
Key key,
@required this.rects,
@required this.onSelected,
this.selectedIndex = -1,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: (details) {
RenderBox box = context.findRenderObject();
final offset = box.globalToLocal(details.globalPosition);
final index = rects.lastIndexWhere((rect) => rect.contains(offset));
if (index != -1) {
onSelected(index);
return;
}
onSelected(-1);
},
child: CustomPaint(
size: Size(320, 240),
painter: _RectPainter(rects, selectedIndex),
),
);
}
}
class _RectPainter extends CustomPainter {
static Paint _red = Paint()..color = Colors.red;
static Paint _blue = Paint()..color = Colors.blue;
final List<Rect> rects;
final int selectedIndex;
_RectPainter(this.rects, this.selectedIndex);
@override
void paint(Canvas canvas, Size size) {
var i = 0;
for (Rect rect in rects) {
canvas.drawRect(rect, i++ == selectedIndex ? _red : _blue);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
@Aj281193
Copy link

hello everyone I follow the same approach for circle. but I can't able to find the index of offset. please help me. this is my code below:-

import 'package:flutter/material.dart';

class CircleExample extends StatelessWidget {
@OverRide
Widget build(BuildContext context) {
return MaterialApp(
title: 'Painter',
home: Stack(
children: [
Circle(center: {"x": 100, "y": 400}, radius: 60, positioned: 0),
Circle(center: {"x": 180, "y": 600}, radius: 28),
Circle(center: {"x": 150, "y": 180}, radius: 40),
Circle(center: {"x": 200, "y": 400}, radius: 35),
Circle(center: {"x": 80, "y": 190}, radius: 18),
Circle(center: {"x": 200, "y": 120}, radius: 20),
Circle(center: {"x": 300, "y": 240}, radius: 36),
],
),
);
}
}

class Circle extends StatefulWidget {
final Map<String, double> center;
final double radius;
Circle({this.center, this.radius, int positioned});
@OverRide
_CircleState createState([Offset offset]) => _CircleState();
}

class _CircleState extends State with SingleTickerProviderStateMixin {
AnimationController _controller;

@OverRide
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
}

@OverRide
void dispose() {
super.dispose();
_controller.dispose();
}

@OverRide
Widget build(BuildContext context) {
return CustomPaint(
size: Size(MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height),
foregroundPainter:
DrawCircle(center: widget.center, radius: widget.radius),
);
}
}

class DrawCircle extends CustomPainter {
Map<String, double> center;
double radius;
DrawCircle({this.center, this.radius});
@OverRide
void paint(Canvas canvas, Size size) {
Paint brush = new Paint()
..color = Colors.red
..strokeCap = StrokeCap.round
..style = PaintingStyle.fill
..strokeWidth = 30;
canvas.drawCircle(Offset(center["x"], center["y"]), radius, brush);
}

@OverRide
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}

@Aj281193
Copy link

this is my code below:-

import 'package:BLEMesh/draw_circle.dart';
import 'package:flutter/material.dart';

class DrawCircleExample extends StatefulWidget {
@OverRide
_DrawCircleExampleState createState() => _DrawCircleExampleState();
}

class _DrawCircleExampleState extends State {
int _index = -1;
@OverRide
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Circles(
circls: [
Circle(center: {"x": 100, "y": 400}, radius: 60),
Circle(center: {"x": 180, "y": 600}, radius: 28),
Circle(center: {"x": 150, "y": 180}, radius: 40),
Circle(center: {"x": 200, "y": 400}, radius: 35),
Circle(center: {"x": 80, "y": 190}, radius: 18),
Circle(center: {"x": 200, "y": 120}, radius: 20),
Circle(center: {"x": 300, "y": 240}, radius: 36),
],
selectedIndex: _index,
onSelected: (index) {
setState(() {
_index = index;
});
},
),
),
);
}
}

class Circles extends StatelessWidget {
final List circls;
final void Function(int) onSelected;
final int selectedIndex;
//final Map<String, double> center;
// final double radius;
const Circles(
{Key key,
@required this.circls,
@required this.onSelected,
this.selectedIndex = -1,
}): super(key: key);
@OverRide
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: (details) {
RenderBox box = context.findRenderObject();
final offset = box.globalToLocal(details.globalPosition);
print(offset);
final index = circls.lastIndexWhere((element) => true);
if (index != -1) {
onSelected(index);
return;
}
onSelected(-1);
},
child: CustomPaint(
size: Size(MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height),
painter: DrawCircle(circls: circls, selectedIndex: selectedIndex),
),
);
}
}

class DrawCircle extends CustomPainter {
Map<String, double> center;
double radius;
final int selectedIndex;
static Paint _red = Paint()..color = Colors.red;
static Paint _blue = Paint()..color = Colors.blue;

final List circls;
DrawCircle({this.circls, this.selectedIndex});
@OverRide
void paint(Canvas canvas, Size size) {
var i = 0;
// Paint brush = new Paint()
// ..color = Colors.red
// ..strokeCap = StrokeCap.round
// ..style = PaintingStyle.fill
// ..strokeWidth = 30;
for (Circle circ in circls) {
canvas.drawCircle(
Offset(circ.center["x"], circ.center["y"]), circ.radius, i++ == selectedIndex ? _red : _blue);
}
}

@OverRide
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}

@Aj281193
Copy link

please help me to make it tappable.how can I get the index of circle.

@not-holar
Copy link

final index = circls.lastIndexWhere((element) => true);

@rudeboyas you don't seem to be doing any actual checks

@sma
Copy link
Author

sma commented Jun 29, 2020

To use circles instead of rectangles, you need to implement a different hit test (line 56) and of course a different way to display them (line 84). For example:

56: final index = rects.lastIndexWhere((rect) => (rect.center - offset).distance < rect.shortestSide / 2);
84: canvas.drawCircle(rect.center, rect.shortestSide / 2, i++ == selectedIndex ? _red : _blue);

@Aj281193
Copy link

thanks got it.

@adigujjar
Copy link

Can we detect which geometry shape is draw on screen through custom painter ?

@sma
Copy link
Author

sma commented Jan 1, 2021

No, only with some kind of model that represents the geometric forms displayed by the painter.

@shineklbm
Copy link

shineklbm commented Mar 4, 2021

@sma I need to do the same with a circle drawn with multiple arcs. I need to see which arc tapped by the user.
Please have a look at this. https://dartpad.dev/4f1dccf37910f61840d430a3947e4135. Please help me!

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