4

I've searched quite a bit and not found a solid answer, so if this is a dupe, I honestly tried.

I have an app I'm rewriting and moving away from an html-based hybrid platform (specifically Trigger.io); doing the rewrite in Flutter and Dart, on the quick.

Part of the app includes a pretty simple screen where the user can click on an image of a human body, and via an image map, get back an identifier and caption for the body part (right forearm, left knee, head, etc).

I simply can not find an analog to this behavior and capability in Flutter. Have I missed something simple because I was totally over thinking it?

Thanks much.

ChrisH
  • 975
  • 12
  • 21

1 Answers1

6

You could wrap your Image in a GestureDetector and specify onTapDown (or onTapUp) callbacks that check the tapped coordinates and act accordingly.

(To convert global coordinates to local coordinates, see: flutter : Get Local position of Gesture Detector)

Here's a rough attempt:

import 'package:quiver/iterables.dart' show enumerate;

class ImageMap extends StatelessWidget {
  const ImageMap({
    Key key,
    @required this.image,
    @required this.onTap,
    @required this.regions,
  }) : super(key: key);

  final Widget image;
  final List<Path> regions;

  /// Callback that will be invoked with the index of the tapped region.
  final void Function(int) onTap;

  void _onTap(BuildContext context, Offset globalPosition) {
    RenderObject renderBox = context.findRenderObject();
    if (renderBox is RenderBox) {
      final localPosition = renderBox.globalToLocal(globalPosition);
      for (final indexedRegion in enumerate(regions)) {
        if (indexedRegion.value.contains(localPosition)) {
          onTap(indexedRegion.index);
          return;
        }
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: (details) => _onTap(context, details.globalPosition),
      child: image,
    );
  }
}
jamesdlin
  • 81,374
  • 13
  • 159
  • 204
  • Yeah, I've been investigating that. Still requires some way to map those coordinates to some part of the picture, and that's the missing key that an image map natively handles nicely in HTML. I've thought about overlaying the image with another image in the same shape, then color each different part a unique color, then run its opacity down to 0, and read the taps on THAT image, check the color value, look up the part. That's kind of what I'm going to test next. Am I making it way too hard? I'm under the gun on this, I might be in analysis paralysis. – ChrisH May 31 '19 at 13:25
  • 1
    You can use [`Rect.contains`](https://api.flutter.dev/flutter/dart-ui/Rect/contains.html) if you can decompose your regions of interest to rectangles. If you have more complex shapes, you can build a `Path` with its various `Path.add...` methods and use [`Path.contains`](https://api.flutter.dev/flutter/dart-ui/Path/contains.html). – jamesdlin May 31 '19 at 15:18
  • 1
    @ChrisH I've updated my answer with a more concrete example. – jamesdlin May 31 '19 at 15:53
  • @jamesdlin what is enumerate? – BIS Tech Dec 30 '20 at 07:46
  • @BloodLoss Oops. I think I meant the [`enumerate`](https://pub.dev/documentation/quiver/latest/quiver.iterables/enumerate.html) function from [`package:quiver`](https://pub.dev/packages/quiver). In retrospect, `enumerate(regions)` could have been `regions.asMap().entries` with `indexedRegion.key` instead of `indexedRegion.index`. – jamesdlin Dec 30 '20 at 07:54