Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add hit detection to Polylines #1728

Merged
merged 24 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8b2b763
Add an onTap callback to polylines.
ignatz Nov 9, 2023
84fcb53
Merge branch 'master' into polyline_tap
JaffaKetchup Dec 6, 2023
b55d499
Remove `PolylineLayer.interactive` argument, and calculate it automat…
JaffaKetchup Dec 6, 2023
f75abfc
Added `onTapTolerance`
JaffaKetchup Dec 6, 2023
bd7dd5e
Add handling for taps on multiple `Polyline`s
JaffaKetchup Dec 7, 2023
9420c85
Merge branch 'master' into polyline_tap
JaffaKetchup Dec 7, 2023
b7ee085
Added support for long presses and secondary taps
JaffaKetchup Dec 7, 2023
c714177
Merge branch 'master' into polyline_tap
josxha Dec 8, 2023
d49e372
Merge branch 'master' into polyline_tap
josxha Dec 10, 2023
66ebe10
Merge branch 'master' into polyline_tap
josxha Dec 10, 2023
72f3f18
Merge branch 'master' into polyline_tap
JaffaKetchup Dec 12, 2023
de17f41
Fix minor documentation mistake
JaffaKetchup Dec 12, 2023
88830ff
Simplified example changes
JaffaKetchup Dec 12, 2023
f820807
Use "occlude" instead of "obscure"
JaffaKetchup Dec 12, 2023
6a80c32
Pass entire `Polyline` object through in handler callbacks, instead o…
JaffaKetchup Dec 12, 2023
d1f525f
Merge branch 'master' into polyline_tap
JaffaKetchup Dec 12, 2023
1fd6e6d
Implement new base-only hit detection API
JaffaKetchup Dec 13, 2023
7f586c2
Improved example application
JaffaKetchup Dec 13, 2023
908f0dd
Minor improvements from review
JaffaKetchup Dec 13, 2023
7954ba2
Remove `hitKey` and generic typing
JaffaKetchup Dec 14, 2023
403d837
Replace `hitTolerance` with `minimumHitbox`
JaffaKetchup Dec 14, 2023
5eec211
Remove remaining reference to `Polyline.hitKey`
JaffaKetchup Dec 14, 2023
3ef7ee3
Revert `polylines` type to `List` from `Iterable`
JaffaKetchup Dec 15, 2023
fb9751d
Paint hover lines on top in polyline example
JaffaKetchup Dec 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
253 changes: 188 additions & 65 deletions example/lib/pages/polyline.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,96 @@ class PolylinePage extends StatefulWidget {
}

class _PolylinePageState extends State<PolylinePage> {
final PolylineHitNotifier hitNotifier = ValueNotifier(null);

final polylines = <Polyline, ({String title, String subtitle})>{
Polyline(
points: [
const LatLng(51.5, -0.09),
const LatLng(53.3498, -6.2603),
const LatLng(48.8566, 2.3522),
],
strokeWidth: 8,
color: const Color(0xFF60399E),
): (
title: 'Elizabeth Line',
subtitle: 'Nothing really special here...',
),
Polyline(
points: [
const LatLng(48.5, -3.09),
const LatLng(47.3498, -9.2603),
const LatLng(43.8566, -1.3522),
],
strokeWidth: 16000,
color: Colors.pink,
useStrokeWidthInMeter: true,
): (
title: 'Pink Line',
subtitle: 'Fixed radius in meters instead of pixels',
),
Polyline(
points: [
const LatLng(55.5, -0.09),
const LatLng(54.3498, -6.2603),
const LatLng(52.8566, 2.3522),
],
strokeWidth: 4,
gradientColors: [
const Color(0xffE40203),
const Color(0xffFEED00),
const Color(0xff007E2D),
],
): (
title: 'Traffic Light Line',
subtitle: 'Fancy gradient instead of a solid color',
),
Polyline(
points: [
const LatLng(50.5, -0.09),
const LatLng(51.3498, 6.2603),
const LatLng(53.8566, 2.3522),
],
strokeWidth: 20,
color: Colors.blue.withOpacity(0.6),
borderStrokeWidth: 20,
borderColor: Colors.red.withOpacity(0.4),
): (
title: 'BlueRed Line',
subtitle: 'Solid translucent color fill, with different color outline',
),
Polyline(
points: [
const LatLng(50.2, -0.08),
const LatLng(51.2498, -10.2603),
const LatLng(54.8566, -9.3522),
],
strokeWidth: 20,
color: Colors.black.withOpacity(0.2),
borderStrokeWidth: 20,
borderColor: Colors.white30,
): (
title: 'BlackWhite Line',
subtitle: 'Solid translucent color fill, with different color outline',
),
Polyline(
points: [
const LatLng(49.1, -0.06),
const LatLng(52.15, -1.4),
const LatLng(55.5, 0.8),
],
strokeWidth: 10,
color: Colors.yellow,
borderStrokeWidth: 10,
borderColor: Colors.blue.withOpacity(0.5),
): (
title: 'YellowBlue Line',
subtitle: 'Solid translucent color fill, with different color outline',
),
};

List<Polyline>? hoverLines;

@override
Widget build(BuildContext context) {
return Scaffold(
Expand All @@ -26,78 +116,111 @@ class _PolylinePageState extends State<PolylinePage> {
),
children: [
openStreetMapTileLayer,
PolylineLayer(
polylines: [
Polyline(
points: [
const LatLng(51.5, -0.09),
const LatLng(53.3498, -6.2603),
const LatLng(48.8566, 2.3522),
],
strokeWidth: 4,
color: Colors.purple,
),
Polyline(
points: [
const LatLng(55.5, -0.09),
const LatLng(54.3498, -6.2603),
const LatLng(52.8566, 2.3522),
],
strokeWidth: 4,
gradientColors: [
const Color(0xffE40203),
const Color(0xffFEED00),
const Color(0xff007E2D),
],
),
Polyline(
points: [
const LatLng(50.5, -0.09),
const LatLng(51.3498, 6.2603),
const LatLng(53.8566, 2.3522),
],
strokeWidth: 20,
color: Colors.blue.withOpacity(0.6),
borderStrokeWidth: 20,
borderColor: Colors.red.withOpacity(0.4),
MouseRegion(
hitTestBehavior: HitTestBehavior.deferToChild,
cursor: SystemMouseCursors.click,
onHover: (_) {
if (hitNotifier.value == null) return;

final lines = hitNotifier.value!.lines
.where((e) => polylines.containsKey(e))
.map(
(e) => Polyline(
points: e.points,
strokeWidth: e.strokeWidth + e.borderStrokeWidth,
color: Colors.transparent,
borderStrokeWidth: 15,
borderColor: Colors.green,
useStrokeWidthInMeter: e.useStrokeWidthInMeter,
),
)
.toList();
setState(() => hoverLines = lines);
},

/// Clear hovered lines when touched lines modal appears
onExit: (_) => setState(() => hoverLines = null),
child: GestureDetector(
onTap: () => _openTouchedLinesModal(
'Tapped',
hitNotifier.value!.lines,
hitNotifier.value!.point,
),
Polyline(
points: [
const LatLng(50.2, -0.08),
const LatLng(51.2498, -10.2603),
const LatLng(54.8566, -9.3522),
],
strokeWidth: 20,
color: Colors.black.withOpacity(0.2),
borderStrokeWidth: 20,
borderColor: Colors.white30,
onLongPress: () => _openTouchedLinesModal(
'Long pressed',
hitNotifier.value!.lines,
hitNotifier.value!.point,
),
Polyline(
points: [
const LatLng(49.1, -0.06),
const LatLng(52.15, -1.4),
const LatLng(55.5, 0.8),
],
strokeWidth: 10,
color: Colors.yellow,
borderStrokeWidth: 10,
borderColor: Colors.blue.withOpacity(0.5),
onSecondaryTap: () => _openTouchedLinesModal(
'Secondary tapped',
hitNotifier.value!.lines,
hitNotifier.value!.point,
),
Polyline(
points: [
const LatLng(48.1, -0.03),
const LatLng(50.5, -7.8),
const LatLng(56.5, 0.4),
],
strokeWidth: 10,
color: Colors.amber,
borderStrokeWidth: 10,
borderColor: Colors.blue.withOpacity(0.5),
child: PolylineLayer(
hitNotifier: hitNotifier,
polylines: polylines.keys.followedBy(hoverLines ?? []).toList(),
),
],
),
),
],
),
);
}

void _openTouchedLinesModal(
String eventType,
List<Polyline> tappedLines,
LatLng coords,
) {
tappedLines.removeWhere((e) => !polylines.containsKey(e));

showModalBottomSheet<void>(
context: context,
builder: (context) => Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Tapped Polyline(s)',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
Text(
'$eventType at point: (${coords.latitude.toStringAsFixed(6)}, ${coords.longitude.toStringAsFixed(6)})',
),
const SizedBox(height: 8),
Expanded(
child: ListView.builder(
itemBuilder: (context, index) {
final tappedLineData = polylines[tappedLines[index]]!;
return ListTile(
leading: index == 0
? const Icon(Icons.vertical_align_top)
: index == tappedLines.length - 1
? const Icon(Icons.vertical_align_bottom)
: const SizedBox.shrink(),
title: Text(tappedLineData.title),
subtitle: Text(tappedLineData.subtitle),
dense: true,
);
},
itemCount: tappedLines.length,
),
),
const SizedBox(height: 8),
Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
width: double.infinity,
child: OutlinedButton(
onPressed: () => Navigator.pop(context),
child: const Text('Close'),
),
),
),
],
),
),
);
}
}
Loading
Loading