1

My goal:

  • Create an interactive map in flutter
  • User should be able to pan around in the map
  • User should be able to click on different countries in the map
  • When user click's on a country, the country changes color
  • The app should run at reasonable framerate even on weaker phones

Gif of how the app looks

I have the first four points implemented, however, I am struggling with making it run smoothly. I am using custom clippers for each country.

The question

Can I speed Clippers up? If not, what are some alternatives to Clippers that still let me do the first four requirements? I would prefer to use flutter, but I am open to any technology that let's me develop for iOS and Android at the same time.

Current implementation

The important points are:

  • I am using InteractiveViewer to allow movement in the map.
  • Each country is represented by Clipper with a touch control and container inside.

I don't think the code is that important but it still might be usefull to someone, so I am posting it below.

First, there is the wrapper that puts everything into InteractiveViewer

class MapWrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: InteractiveViewer(
        constrained: false,
        child: MapContent(),
      ),
    );
  }
}

Then, there is MapContent:

class MapContent extends StatelessWidget {
  List<Widget> buildMap(List<MapItem> mapItems) {
    List<Widget> result = [];
    for (final item in mapItems) {
      result.add(
        ClipPath(
            clipper: item.clipper,
            child: GestureDetector(
              onTap: handleTap,
              child: Container(
                color: item.color,
                // this is not the best way to make sure container covers the whole clipper, I am pretty new to flutter and just want to get a prototype running first.
                width: item.clipper.getBounds().width +
                    item.clipper.getBounds().left,
                height: item.clipper.getBounds().height +
                    item.clipper.getBounds().top,
              ),
            )),
      );
    }
    return result;
  }


  @override
  Widget build(BuildContext context) {
    MapData map = MapData("assets/cz-05.svg");

    return Stack(
      children: buildMap(map .getItems()),
    );
  }
}

And the MapData with MapItem


class MapItem {
  MapItem(this.clipper, this.name, this.color);

  SvgClipper clipper;
  String name;
  Color color;
}

class MapData {
  MapData(String filePath)
      : _items = loadItems() // static method that just loads all <path> from svg (see below);

  List<MapItem> _items;

  List<MapItem> getItems() {
    return _items;
  }
}

and finally, I am using data from here, for example this czech map.

SternK
  • 11,649
  • 22
  • 32
  • 46
Matt
  • 21
  • 3
  • https://gist.github.com/pskink/afd4f20a40ae7756555877ec030daa46 – pskink Jun 15 '23 at 13:47
  • and you can tweak with `cacheFactor` in line #248 – pskink Jun 15 '23 at 13:58
  • and if you don't want to pass all the countries at once you could always to rebuild `Stack` / `Flow` / whatever and pass only the visible countries (but I don't think you will get any performance boost as `Flow` is designed to paint the subset of `children`) – pskink Jun 16 '23 at 19:24
  • what cacheFactor do you mean? Which line do you mean by #248? – Matt Jun 19 '23 at 12:55
  • that is amazing. Sorry for getting back so late, I missed the first link in the first comment because there used to be a different comment with a different link, but then I guess it was deleted. Feel free to post it as an answer as well, I will happily accept it. And thank you very much for the help, I am just starting with Flutter so my mind is blown with what is possible. – Matt Jun 19 '23 at 14:33
  • i just found one bug in `ExtendedViewporf` class, I will fix it soon as so feel free to post a self answer then (without extra stuff I used that you don't need, like custom `InteractiveInkFeature`) – pskink Jun 19 '23 at 15:15
  • @pskink just out of curiosity, do you know why the performance is lower when zooming in and out compared to just moving around? It is like this even when I modified the code to only draw everything just once at the start of the application (called function `paintChildren` in `WorldMapDelegate` just once by removing the ExtendedViewport logic.) – Matt Jun 20 '23 at 09:46

0 Answers0