
Aman Kumar
Flutter Developer, Gigawave
Flutter Developer, Gigawave
We'll create a screen (PitchScreen
) that shows a circular cricket field. When the user taps anywhere inside this field, a line will animate from the pitch center to the tap point, and it will automatically calculate the fielding position based on the angle. A button lets users save their selected position.
GestureDetector
for tap detectionCustomPainter
atan2
AnimationController
ValueNotifier
and ValueListenableBuilder
1class PitchScreen extends StatefulWidget {
2
3 _PitchScreenState createState() => _PitchScreenState();
4}
This is the entry point. It's a stateful widget because we need to trigger animations and update the UI dynamically.
1late AnimationController _controller;
2late Animation<Offset> _animation;
3ValueNotifier<Offset> endPoint = ValueNotifier(Offset.zero);
_controller
: handles animation timing_animation
: moves a line from the center to the tap positionendPoint
: updates dynamically when the user taps1_controller = AnimationController(
2 vsync: this,
3 duration: Duration(milliseconds: 250),
4);
5
6_animation = Tween<Offset>(
7 begin: startPoint,
8 end: endPoint.value,
9).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut));
This defines a short, smooth animation from the pitch center to the user's tapped position.
1GestureDetector(
2 onTapDown: (TapDownDetails details) {
3 _selectedFieldingPosition = getFieldingPosition(
4 _startAnimation(details.localPosition, MediaQuery.of(context).size),
5 );
6 endPoint.notifyListeners();
7 },
8)
When the user taps, the app:
FIELDINGPOSITION
1double calculateAngle(Offset center, Offset target) {
2 double angleRadians = atan2(target.dy - center.dy, target.dx - center.dx);
3 return angleRadians * (180 / pi); // in degrees
4}
This function gives the angle from the pitch center to the user's tap.
1FIELDINGPOSITION getFieldingPosition(double angle) {
2 double adjustedAngle = (angle + 250) % 360;
3 ...
4}
We divide the circle into 8 slices (each 45°) and map each to a cricket fielding position like:
LONG_OFF
THIRD_MAN
DEEP_POINT
1canvas.drawCircle(center, bigRadius, bigCirclePaint);
2canvas.drawCircle(center, bigRadius * .93, smallCirclePaintLine);
3canvas.drawCircle(center, smallRadius, smallCirclePaint);
- Draws a big green circle (field)
- A slightly smaller stroked circle (boundary)
- A small center circle (pitch)
- A rectangular pitch using canvas.drawRect
1for (int i = 0; i < 8; i++) {
2 double angle = (pi / 4) * i;
3 Offset end = Offset(centerX + bigRadius * .9 * cos(angle), ...);
4 canvas.drawLine(center, end, linePaint);
5}
Splits the field into 8 sections using lines. Each angle maps to a fielding label using getBattingAngleFromRadians()
1TextPainter textPainter = TextPainter(
2 text: TextSpan(
3 text: "FIELDING_POSITION_NAME",
4 style: AppTextstyles.LowText(...),
5 ),
6);
Adds readable labels like LONG_ON
, DEEP_POINT
on each arc
1AppButton(
2 onPressed: () {
3 if (_selectedFieldingPosition != null) {
4 Get.back<FIELDINGPOSITION>(result: _selectedFieldingPosition);
5 } else {
6 AppCommon.toast(...);
7 }
8 },
9 title: "save",
10)
Tapping this button returns the chosen FIELDINGPOSITION
to the previous screen. If none is selected, a toast is shown.
With just GestureDetector
, CustomPainter
, and animation, we built a beautiful, interactive way to select fielding positions on a cricket field. This can be extended to allow multiple player markers, edit positions, or integrate with scoring systems.