Skip to content

Commit

Permalink
Add touch handler for Pie Chart
Browse files Browse the repository at this point in the history
  • Loading branch information
nitin-janyani committed Dec 19, 2024
1 parent ce9e849 commit 8572602
Show file tree
Hide file tree
Showing 4 changed files with 320 additions and 0 deletions.
292 changes: 292 additions & 0 deletions example/lib/presentation/samples/pie/pie_char_sample4.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
import 'package:fl_chart_app/presentation/resources/app_resources.dart';
import 'package:fl_chart_app/presentation/widgets/indicator.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';

class PieChartSample4 extends StatefulWidget {
const PieChartSample4({super.key});

@override
State<StatefulWidget> createState() => PieChartSample4State();
}

class PieChartSample4State extends State {
Offset? hoveredOffset;
int innerTouchedIndex = -1;
int outerTouchedIndex = -1;

@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 1.3,
child: Column(
children: <Widget>[
const SizedBox(
height: 28,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Indicator(
color: AppColors.contentColorBlue,
text: 'One',
isSquare: false,
size:
innerTouchedIndex == 0 || outerTouchedIndex == 0 ? 18 : 16,
textColor: innerTouchedIndex == 0 || outerTouchedIndex == 0
? AppColors.mainTextColor1
: AppColors.mainTextColor3,
),
Indicator(
color: AppColors.contentColorYellow,
text: 'Two',
isSquare: false,
size:
innerTouchedIndex == 1 || outerTouchedIndex == 1 ? 18 : 16,
textColor: innerTouchedIndex == 1 || outerTouchedIndex == 1
? AppColors.mainTextColor1
: AppColors.mainTextColor3,
),
Indicator(
color: AppColors.contentColorPink,
text: 'Three',
isSquare: false,
size:
innerTouchedIndex == 2 || outerTouchedIndex == 2 ? 18 : 16,
textColor: innerTouchedIndex == 2 || outerTouchedIndex == 2
? AppColors.mainTextColor1
: AppColors.mainTextColor3,
),
Indicator(
color: AppColors.contentColorGreen,
text: 'Four',
isSquare: false,
size:
innerTouchedIndex == 3 || outerTouchedIndex == 3 ? 18 : 16,
textColor: innerTouchedIndex == 3 || outerTouchedIndex == 3
? AppColors.mainTextColor1
: AppColors.mainTextColor3,
),
],
),
const SizedBox(
height: 18,
),
Expanded(
child: AspectRatio(
aspectRatio: 1,
child: MouseRegion(
onHover: (event) {
setState(() {
hoveredOffset = event.localPosition;
});
},
child: Stack(
children: [
Positioned.fill(
child: PieChart(
PieChartData(
pieTouchData: PieTouchData(
externalTouchPosition: hoveredOffset,
externalTouchCallback: (pieTouchResponse) {
int? newTouchedIndex = pieTouchResponse
?.touchedSection?.touchedSectionIndex;
WidgetsBinding.instance
.addPostFrameCallback((timeStamp) {
if (mounted &&
newTouchedIndex != innerTouchedIndex) {
setState(() {
innerTouchedIndex = newTouchedIndex ?? -1;
});
}
});
},
),
startDegreeOffset: 180,
borderData: FlBorderData(
show: false,
),
sectionsSpace: 1,
centerSpaceRadius: 0,
sections: showingInnerSections(),
),
),
),
Positioned.fill(
child: PieChart(
PieChartData(
pieTouchData: PieTouchData(
externalTouchPosition: hoveredOffset,
externalTouchCallback: (pieTouchResponse) {
int? newTouchedIndex = pieTouchResponse
?.touchedSection?.touchedSectionIndex;
WidgetsBinding.instance
.addPostFrameCallback((timeStamp) {
if (mounted &&
newTouchedIndex != outerTouchedIndex) {
setState(() {
outerTouchedIndex = newTouchedIndex ?? -1;
});
}
});
},
),
startDegreeOffset: 180,
borderData: FlBorderData(
show: false,
),
sectionsSpace: 1,
centerSpaceRadius: 50,
sections: showingOuterSections(),
),
),
),
],
),
)),
),
],
),
);
}

List<PieChartSectionData> showingInnerSections() {
return List.generate(
4,
(i) {
final isTouched = i == innerTouchedIndex;
const color0 = AppColors.contentColorBlue;
const color1 = AppColors.contentColorYellow;
const color2 = AppColors.contentColorPink;
const color3 = AppColors.contentColorGreen;

switch (i) {
case 0:
return PieChartSectionData(
color: color0,
value: 25,
title: '',
radius: 45,
titlePositionPercentageOffset: 0.55,
borderSide: isTouched
? const BorderSide(
color: AppColors.contentColorWhite, width: 6)
: BorderSide(
color: AppColors.contentColorWhite.withOpacity(0)),
);
case 1:
return PieChartSectionData(
color: color1,
value: 30,
title: '',
radius: 45,
titlePositionPercentageOffset: 0.55,
borderSide: isTouched
? const BorderSide(
color: AppColors.contentColorWhite, width: 6)
: BorderSide(
color: AppColors.contentColorWhite.withOpacity(0)),
);
case 2:
return PieChartSectionData(
color: color2,
value: 25,
title: '',
radius: 45,
titlePositionPercentageOffset: 0.6,
borderSide: isTouched
? const BorderSide(
color: AppColors.contentColorWhite, width: 6)
: BorderSide(
color: AppColors.contentColorWhite.withOpacity(0)),
);
case 3:
return PieChartSectionData(
color: color3,
value: 40,
title: '',
radius: 45,
titlePositionPercentageOffset: 0.55,
borderSide: isTouched
? const BorderSide(
color: AppColors.contentColorWhite, width: 6)
: BorderSide(
color: AppColors.contentColorWhite.withOpacity(0)),
);
default:
throw Error();
}
},
);
}

List<PieChartSectionData> showingOuterSections() {
return List.generate(
4,
(i) {
final isTouched = i == outerTouchedIndex;
const color0 = AppColors.contentColorBlue;
const color1 = AppColors.contentColorYellow;
const color2 = AppColors.contentColorPink;
const color3 = AppColors.contentColorGreen;

switch (i) {
case 0:
return PieChartSectionData(
color: color0,
value: 25,
title: '',
radius: 30,
titlePositionPercentageOffset: 0.55,
borderSide: isTouched
? const BorderSide(
color: AppColors.contentColorWhite, width: 6)
: BorderSide(
color: AppColors.contentColorWhite.withOpacity(0)),
);
case 1:
return PieChartSectionData(
color: color1,
value: 25,
title: '',
radius: 15,
titlePositionPercentageOffset: 0.55,
borderSide: isTouched
? const BorderSide(
color: AppColors.contentColorWhite, width: 6)
: BorderSide(
color: AppColors.contentColorWhite.withOpacity(0)),
);
case 2:
return PieChartSectionData(
color: color2,
value: 25,
title: '',
radius: 10,
titlePositionPercentageOffset: 0.6,
borderSide: isTouched
? const BorderSide(
color: AppColors.contentColorWhite, width: 6)
: BorderSide(
color: AppColors.contentColorWhite.withOpacity(0)),
);
case 3:
return PieChartSectionData(
color: color3,
value: 25,
title: '',
radius: 20,
titlePositionPercentageOffset: 0.55,
borderSide: isTouched
? const BorderSide(
color: AppColors.contentColorWhite, width: 6)
: BorderSide(
color: AppColors.contentColorWhite.withOpacity(0)),
);
default:
throw Error();
}
},
);
}
}
17 changes: 17 additions & 0 deletions lib/src/chart/base/base_chart/base_chart_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ abstract class FlTouchData<R extends BaseTouchResponse> with EquatableMixin {
this.touchCallback,
this.mouseCursorResolver,
this.longPressDuration,
this.externalTouchCallback,
this.externalTouchPosition,
);

/// You can disable or enable the touch system using [enabled] flag,
Expand All @@ -107,13 +109,23 @@ abstract class FlTouchData<R extends BaseTouchResponse> with EquatableMixin {
/// default to 500 milliseconds refer to [kLongPressTimeout].
final Duration? longPressDuration;

/// If you will use an external touch handlers, you will need to pass the position.
final Offset? externalTouchPosition;

/// [externalTouchCallback] notifies you about the happened touch/pointer events.
/// It gives you a [BaseTouchResponse] which is the chart specific type and contains information
/// about the elements that has touched.
final BaseExternalTouchCallback<R>? externalTouchCallback;

/// Used for equality check, see [EquatableMixin].
@override
List<Object?> get props => [
enabled,
touchCallback,
mouseCursorResolver,
longPressDuration,
externalTouchCallback,
externalTouchPosition,
];
}

Expand Down Expand Up @@ -185,6 +197,11 @@ typedef MouseCursorResolver<R extends BaseTouchResponse> = MouseCursor Function(
R?,
);

/// Chart's touch callback.
typedef BaseExternalTouchCallback<R extends BaseTouchResponse> = void Function(
R?,
);

/// This class holds the touch response details of charts.
abstract class BaseTouchResponse {
const BaseTouchResponse();
Expand Down
4 changes: 4 additions & 0 deletions lib/src/chart/pie_chart/pie_chart_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -320,11 +320,15 @@ class PieTouchData extends FlTouchData<PieTouchResponse> with EquatableMixin {
BaseTouchCallback<PieTouchResponse>? touchCallback,
MouseCursorResolver<PieTouchResponse>? mouseCursorResolver,
Duration? longPressDuration,
Offset? externalTouchPosition,
BaseExternalTouchCallback<PieTouchResponse>? externalTouchCallback,
}) : super(
enabled ?? true,
touchCallback,
mouseCursorResolver,
longPressDuration,
externalTouchCallback,
externalTouchPosition,
);

/// Used for equality check, see [EquatableMixin].
Expand Down
7 changes: 7 additions & 0 deletions lib/src/chart/pie_chart/pie_chart_renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ class PieChartLeaf extends MultiChildRenderObjectWidget {
..targetData = targetData
..textScaler = MediaQuery.of(context).textScaler
..buildContext = context;
data.pieTouchData.externalTouchCallback?.call(
data.pieTouchData.externalTouchPosition != null
? renderObject.getResponseAtLocation(
data.pieTouchData.externalTouchPosition!,
)
: null,
);
}
}
// coverage:ignore-end
Expand Down

0 comments on commit 8572602

Please sign in to comment.