@@ -8,48 +8,132 @@ class CirclesHomeWidget extends StatefulWidget {
8
8
class _CircleHomeState extends State <CirclesHomeWidget > {
9
9
@override
10
10
Widget build (BuildContext context) {
11
- return Scaffold (
12
- body: ScrollableCircleGrid (),
11
+ return const Scaffold (
12
+ body: PannableCircleGrid (),
13
13
);
14
14
}
15
15
}
16
16
17
- class ScrollableCircleGrid extends StatefulWidget {
17
+ class PannableCircleGrid extends StatefulWidget {
18
+ const PannableCircleGrid ({Key ? key}) : super (key: key);
19
+
18
20
@override
19
- _ScrollableCircleGridState createState () => _ScrollableCircleGridState ();
21
+ _PannableCircleGridState createState () => _PannableCircleGridState ();
20
22
}
21
23
22
- class _ScrollableCircleGridState extends State <ScrollableCircleGrid > {
23
- int ? selectedIndex;
24
+ class _PannableCircleGridState extends State <PannableCircleGrid > {
25
+ Offset _offset = Offset .zero;
26
+ int ? _selectedIndex;
27
+ final double _circleSize = 60 ;
28
+ final double _spacing = 20 ;
29
+ final int _columns = 1000 ; // Arbitrary large number for columns
24
30
25
31
@override
26
32
Widget build (BuildContext context) {
27
- return GridView .builder (
28
- gridDelegate: SliverGridDelegateWithFixedCrossAxisCount (
29
- crossAxisCount: 5 ,
30
- childAspectRatio: 1 ,
33
+ return GestureDetector (
34
+ onPanUpdate: (details) {
35
+ setState (() {
36
+ _offset += details.delta;
37
+ });
38
+ },
39
+ child: ClipRect (
40
+ child: CustomPaint (
41
+ painter: CircleGridPainter (
42
+ offset: _offset,
43
+ circleSize: _circleSize,
44
+ spacing: _spacing,
45
+ selectedIndex: _selectedIndex,
46
+ columns: _columns,
47
+ ),
48
+ child: GestureDetector (
49
+ onTapUp: (details) {
50
+ _handleTap (details.localPosition);
51
+ },
52
+ ),
53
+ ),
31
54
),
32
- itemBuilder: (context, index) {
55
+ );
56
+ }
57
+
58
+ void _handleTap (Offset tapPosition) {
59
+ int col =
60
+ ((tapPosition.dx - _offset.dx) / (_circleSize + _spacing)).floor ();
61
+ int row =
62
+ ((tapPosition.dy - _offset.dy) / (_circleSize + _spacing)).floor ();
63
+ int index = row * _columns + col;
64
+
65
+ setState (() {
66
+ _selectedIndex = (_selectedIndex == index) ? null : index;
67
+ });
68
+ }
69
+ }
70
+
71
+ class CircleGridPainter extends CustomPainter {
72
+ final Offset offset;
73
+ final double circleSize;
74
+ final double spacing;
75
+ final int ? selectedIndex;
76
+ final int columns;
77
+
78
+ CircleGridPainter ({
79
+ required this .offset,
80
+ required this .circleSize,
81
+ required this .spacing,
82
+ required this .columns,
83
+ this .selectedIndex,
84
+ });
85
+
86
+ @override
87
+ void paint (Canvas canvas, Size size) {
88
+ final paint = Paint ()
89
+ ..color = Colors .grey
90
+ ..style = PaintingStyle .fill;
91
+
92
+ final selectedPaint = Paint ()
93
+ ..color = Colors .white
94
+ ..style = PaintingStyle .fill;
95
+
96
+ final textPainter = TextPainter (
97
+ textDirection: TextDirection .ltr,
98
+ );
99
+
100
+ final startCol = (- offset.dx / (circleSize + spacing)).floor () - 1 ;
101
+ final endCol = ((size.width - offset.dx) / (circleSize + spacing)).ceil ();
102
+ final startRow = (- offset.dy / (circleSize + spacing)).floor () - 1 ;
103
+ final endRow = ((size.height - offset.dy) / (circleSize + spacing)).ceil ();
104
+
105
+ for (int row = startRow; row <= endRow; row++ ) {
106
+ for (int col = startCol; col <= endCol; col++ ) {
107
+ final circleOffset = Offset (
108
+ col * (circleSize + spacing) + offset.dx,
109
+ row * (circleSize + spacing) + offset.dy,
110
+ );
111
+
112
+ int index = row * columns + col;
33
113
bool isSelected = selectedIndex == index;
34
- return GestureDetector (
35
- onTap: () {
36
- setState (() {
37
- selectedIndex = isSelected ? null : index;
38
- });
39
- },
40
- child: AnimatedContainer (
41
- duration: Duration (milliseconds: 300 ),
42
- margin: EdgeInsets .all (isSelected ? 8 : 4 ),
43
- decoration: BoxDecoration (
44
- shape: BoxShape .circle,
45
- color: isSelected ? Colors .white : Colors .grey,
46
- ),
47
- child: Center (
48
- child: Text ('Friend $index ' ),
49
- ),
50
- ),
114
+
115
+ canvas.drawCircle (
116
+ circleOffset,
117
+ isSelected ? circleSize * 0.6 : circleSize * 0.5 ,
118
+ isSelected ? selectedPaint : paint,
51
119
);
52
- },
53
- );
120
+
121
+ textPainter.text = TextSpan (
122
+ text: '$index ' ,
123
+ style: TextStyle (
124
+ color: isSelected ? Colors .black : Colors .white, fontSize: 12 ),
125
+ );
126
+ textPainter.layout ();
127
+ textPainter.paint (
128
+ canvas,
129
+ circleOffset - Offset (textPainter.width / 2 , textPainter.height / 2 ),
130
+ );
131
+ }
132
+ }
54
133
}
134
+
135
+ @override
136
+ bool shouldRepaint (covariant CircleGridPainter oldDelegate) =>
137
+ offset != oldDelegate.offset ||
138
+ selectedIndex != oldDelegate.selectedIndex;
55
139
}
0 commit comments