Flutter_map: Performance issue when using setState()

Created on 4 Mar 2020  路  25Comments  路  Source: fleaflet/flutter_map

Hi, I am using your plugin on version flutter_map: ^0.8.2 and Flutter 1.12.13 hotfix 7 and I observe some performance issue.

I want to display user's current location on the map using location plugin but when I listen to the stream and new value appears, only thing to reflect changes on the map is use setState, but there is the issue - when I rebuild whole map, it starts to be slow and I observe some performance issue (when I click on marker and open new page using Navigator etc.)

Interesting is that I notice performance drop even if I use only one time location Future - so map is rebuilded only once.

Thanks for your time!

All 25 comments

Have you tried it in release mode btw, and still have performance issues ?

Have you tried it in release mode btw, and still have performance issues ?

Yes, I have build it many times.

Strange. I use setState all the time to rebuild the map, like in this package and never noticed a performance problem. Maybe you should publish some code to give more detailled info about your issue

459 Show here

I'm working on dynamic rebuild strategy to avoid performance issues

I'm interested in this question #482 and would appreciate any more detailed info about layers rebuilding. Please publish you code sooner than later

Strange. I use setState all the time to rebuild the map, like in this package and never noticed a performance problem. Maybe you should publish some code to give more detailled info about your issue

I tried this package and it has one problem for me - it update layer only when I move with map or click on some element (marker). Also, I am missing posibility to create PolyLineLayer, there are only markers, polygons and tiles.

My code looks like this:

  void _getCurrentLocation() {
    statefulMapController = StatefulMapController(mapController: mapController);
    subscription = location.onLocationChanged().listen((l) {
      statefulMapController.addMarker(
        marker: Marker(
          point: LatLng(l.latitude, l.longitude),
          builder: (context) => Icon(
            Icons.place,
            color: darkBlue,
            size: 20,
          ),
        ),
        name: "Location",
      );
    });
  }

@synw here

https://github.com/ruizalexandre/flutter_map/commit/739fda3faa89ad6524cb0a864a8b4982f068922c
I'm working on it, I need to migrate all existing layers
And add unit tests

You can run the demo app
Home : :heavy_check_mark:
Add Pins : :heavy_check_mark:
Moving markers : :heavy_check_mark:

@synw here

ruizalexandre@739fda3
I'm working on it, I need to migrate all existing layers
And add unit tests

You can run the demo app
Home : 鉁旓笍
Add Pins : 鉁旓笍
Moving markers : 鉁旓笍

Please let me know, when it's ready!

@TenPetr : in the map_controller package you need to subscribe to the changefeed and call setState to trigger the rebuilds, otherwise it will act as you described. This is precisely what I want to address by moving this to a plugin to manage automatic rebuilds on assets state changes. The problem I am facing is that I did not find a clean way to manually trigger a rebuild of a layer in a plugin

map_controller definitely supports polylines: just use statefulMapController.lines as the value for the polyline layer. Feel free to post issues in the map_controller repository if you encounter problems with it

@TenPetr : in the map_controller package you need to subscribe to the changefeed and call setState to trigger the rebuilds, otherwise it will act as you described. This is precisely what I want to address by moving this to a plugin to manage automatic rebuilds on assets state changes. The problem I am facing is that I did not find a clean way to manually trigger a rebuild of a layer in a plugin

map_controller definitely supports polylines: just use statefulMapController.lines as the value for the polyline layer. Feel free to post issues in the map_controller repository if you encounter problems with it

Could you please help me with this? I don't fully understand what's going on..
Here is mu function, that is called in the initState()

  void _getCurrentLocation() {
    statefulMapController = StatefulMapController(mapController: mapController);
    subscription = location.onLocationChanged().listen((l) {
      statefulMapController.changeFeed.listen(
        (s) => setState(() {
          statefulMapController.addMarker(
            marker: Marker(
              point: LatLng(l.latitude, l.longitude),
              builder: (context) => Icon(
                Icons.place,
                color: darkBlue,
                size: 20,
              ),
            ),
            name: "Location",
          );
        }),
      );
    });
  }

and in the FlutterMap widget, I have MarkerLayerOption and this is how it looks like

MarkerLayerOptions(
      markers: statefulMapController.markers + parkingLotsMarkers,
),

Thank you for your time!

@TenPetr : in the map_controller package you need to subscribe to the changefeed and call setState to trigger the rebuilds, otherwise it will act as you described. This is precisely what I want to address by moving this to a plugin to manage automatic rebuilds on assets state changes. The problem I am facing is that I did not find a clean way to manually trigger a rebuild of a layer in a plugin

map_controller definitely supports polylines: just use statefulMapController.lines as the value for the polyline layer. Feel free to post issues in the map_controller repository if you encounter problems with it

Ok, I tried this and it works, kinda.. When I use setState, all widget tree its rebuilded, so my original problem is not solved. Do you have any suggestions please?

  void _getCurrentLocation() {
    statefulMapController = StatefulMapController(mapController: mapController);
    subscription = location.onLocationChanged().listen((l) {
      setState(() {
        statefulMapController.addLine(
          color: Color.fromRGBO(33, 150, 243, .3),
          width: 25,
          name: "Current Location",
          points: [LatLng(l.latitude, l.longitude)],
        );
      });
    });
  }

And also, when I add multiple polylines, only one (the most in the bottom) is rendered.

Thanks!

@TenPetr
Please test your usecase on my branch https://github.com/ruizalexandre/flutter_map/tree/feature/layer-widgets

You can run the demo app
Home : :heavy_check_mark:
Add Pins : :heavy_check_mark:
Moving markers : :heavy_check_mark:
Polylines/Polygons : :heavy_check_mark:
MapController : :heavy_check_mark:
Animation MapController : :heavy_check_mark:
OnTap : :heavy_check_mark:
Circle : :heavy_check_mark:
Overlay Image : :heavy_check_mark:

Plugins : :heavy_check_mark: :warning:
Migrate to StatelessWidget/StatefulWidget if using
Show examples on plugins page

:bulb: _To rebuild a plugin or a custom layer (same thing in fact)_

var mapState = MapStateInheritedWidget.of(context);
mapState.rebuild();

@TenPetr
Please test your usecase on my branch https://github.com/ruizalexandre/flutter_map/tree/feature/layer-widgets

You can run the demo app
Home : 鉁旓笍
Add Pins : 鉁旓笍
Moving markers : 鉁旓笍
Polylines/Polygons : 鉁旓笍
MapController : 鉁旓笍
Animation MapController : 鉁旓笍
OnTap : 鉁旓笍
Circle : 鉁旓笍
Overlay Image : 鉁旓笍

Plugins : 鉁旓笍 鈿狅笍
Migrate to StatelessWidget/StatefulWidget if using
Show examples on plugins page

馃挕 _To rebuild a plugin or a custom layer (same thing in fact)_

var mapState = MapStateInheritedWidget.of(context);
mapState.rebuild();

Well basically, it doesn't help me, because only one thing that I need is to update polyline layer and I can't do that without using setState method (which rebuilds whole widget tree) and when it run everytime when I have value in the stream and I click on the marker and new window pops up, it starts to be slow and laggy.

@TenPetr
Please test your usecase on my branch https://github.com/ruizalexandre/flutter_map/tree/feature/layer-widgets
You can run the demo app
Home : heavy_check_mark
Add Pins : heavy_check_mark
Moving markers : heavy_check_mark
Polylines/Polygons : heavy_check_mark
MapController : heavy_check_mark
Animation MapController : heavy_check_mark
OnTap : heavy_check_mark
Circle : heavy_check_mark
Overlay Image : heavy_check_mark
Plugins : heavy_check_mark warning
Migrate to StatelessWidget/StatefulWidget if using
Show examples on plugins page
bulb _To rebuild a plugin or a custom layer (same thing in fact)_

var mapState = MapStateInheritedWidget.of(context);
mapState.rebuild();

Well basically, it doesn't help me, because only one thing that I need is to update polyline layer and I can't do that without using setState method (which rebuilds whole widget tree) and when it run everytime when I have value in the stream and I click on the marker and new window pops up, it starts to be slow and laggy.

On my approach the polyline layer can be updated without reloading all flutter map
I change all strategy layer for that (can be used easily with Provider state also)

And create plugins is more easy because it's basically Flutter Widget only !

@TenPetr
Please test your usecase on my branch https://github.com/ruizalexandre/flutter_map/tree/feature/layer-widgets
You can run the demo app
Home : heavy_check_mark
Add Pins : heavy_check_mark
Moving markers : heavy_check_mark
Polylines/Polygons : heavy_check_mark
MapController : heavy_check_mark
Animation MapController : heavy_check_mark
OnTap : heavy_check_mark
Circle : heavy_check_mark
Overlay Image : heavy_check_mark
Plugins : heavy_check_mark warning
Migrate to StatelessWidget/StatefulWidget if using
Show examples on plugins page
bulb _To rebuild a plugin or a custom layer (same thing in fact)_

var mapState = MapStateInheritedWidget.of(context);
mapState.rebuild();

Well basically, it doesn't help me, because only one thing that I need is to update polyline layer and I can't do that without using setState method (which rebuilds whole widget tree) and when it run everytime when I have value in the stream and I click on the marker and new window pops up, it starts to be slow and laggy.

On my approach the polyline layer can be updated without reloading all flutter map
I change all strategy layer for that (can be used easily with Provider state also)

And create plugins is more easy because it's basically Flutter Widget only !

Could you please show me (on some code example) how to do that? I am lost ..
(I just need to update polyline layer based on users location - which is provided by location package) this is what I struggle with all the time.. do you think you can show me how to do that and what I have to install to make it work (without reloading whole map)?
Thank you very much!

@TenPetr
https://github.com/ruizalexandre/flutter_map/blob/feature/layer-widgets/example/lib/pages/user_position.dart

I made this example for you
Simulation of user in Paris (refresh only user marker and draw a polyline after each new point)
If you drag on map > rebuild automatically all layers :+1:

Hope helping you

@TenPetr
https://github.com/ruizalexandre/flutter_map/blob/feature/layer-widgets/example/lib/pages/user_position.dart

I made this example for you
Simulation of user in Paris (refresh only user marker and draw a polyline after each new point)
If you drag on map > rebuild automatically all layers 馃憤

Hope helping you

Many thanks for your example, but I have one problem with it. Where did you get PolylineLayerWidget and MarkerLayerWidget, because I am getting error. How should I import them if they are not merged in the package?

Thanks a lot!

  @override
  Widget build(BuildContext context) {
    return MarkerLayerWidget(
      markers: [_marker],
    );
  }

...

  @override
  Widget build(BuildContext context) {
    return PolylineLayerWidget(
      polylines: [
        Polyline(
          points: _points,
          strokeWidth: 4.0,
          color: Colors.blueAccent,
        ),
      ],
    );
  }

And also, when want to use this new widget in the map, I am getting error.

The element type 'UserPositionLayer' can't be assigned to the list type 'LayerOptions'.

FlutterMap(
          options: MapOptions(
            center: LatLng(49.069761, 17.462189),
            zoom: 15,
            minZoom: 14,
            maxZoom: 18,
            plugins: [
              UserLocationPlugin(),
            ],
          ),
          layers: [
            TileLayerOptions(
              urlTemplate: mapUrl,
              subdomains: ['a', 'b', 'c'],
            ),
            MarkerLayerOptions(
              markers: parkingLotsMarkers,
            ),
            UserPositionLayer(stream: userPosition.stream),
          ],
        ),

@TenPetr
https://github.com/ruizalexandre/flutter_map/blob/feature/layer-widgets/example/lib/pages/user_position.dart
I made this example for you
Simulation of user in Paris (refresh only user marker and draw a polyline after each new point)
If you drag on map > rebuild automatically all layers +1
Hope helping you

Many thanks for your example, but I have one problem with it. Where did you get PolylineLayerWidget and MarkerLayerWidget, because I am getting error. How should I import them if they are not merged in the package?

Thanks a lot!

  @override
  Widget build(BuildContext context) {
    return MarkerLayerWidget(
      markers: [_marker],
    );
  }

...

  @override
  Widget build(BuildContext context) {
    return PolylineLayerWidget(
      polylines: [
        Polyline(
          points: _points,
          strokeWidth: 4.0,
          color: Colors.blueAccent,
        ),
      ],
    );
  }

And also, when want to use this new widget in the map, I am getting error.

The element type 'UserPositionLayer' can't be assigned to the list type 'LayerOptions'.

FlutterMap(
          options: MapOptions(
            center: LatLng(49.069761, 17.462189),
            zoom: 15,
            minZoom: 14,
            maxZoom: 18,
            plugins: [
              UserLocationPlugin(),
            ],
          ),
          layers: [
            TileLayerOptions(
              urlTemplate: mapUrl,
              subdomains: ['a', 'b', 'c'],
            ),
            MarkerLayerOptions(
              markers: parkingLotsMarkers,
            ),
            UserPositionLayer(stream: userPosition.stream),
          ],
        ),

It's simple, it's under development on my fork.
I'm on it and I need little time to finish it.

You can clone my fork at this time and checkout my branch __layer-widgets__

I change many things (I need to clean code also)

Just thinking about this one a bit more....the current flutter_map layer method seems to use streams to trigger updates ?

So could we do something like below ?

/// top of class
static var controller = StreamController<Null>();

/// in your build, include your widget in your return...
/// return Scaffold or whatever, with this inside it...

PolylineLayerOptions(
   rebuild: controller.stream,
      polylines: [
            Polyline(
                points: points,
                strokeWidth: 4.0,
                color: Colors.purple),
          ],
  ),

///somewhere else
controller.add(null); // trigger stream update...

/// close and dispose controller...

Now, if this did work...we've probably overwritten the normal triggering of a map move to update the polylinelayeroptions rebuild stream...this may not be what you want...so if you need both triggers (manual and map move independant), maybe merge the two streams and add that as the rebuild: param ?

There may be fundamental issues with it, so just putting it out there to be pondered...

edit: not sure you would want the controller in that as a static actually...but not able to test it from here atm, you may need it elsewhere so it doesn't get overwritten by flutter_maps own build, and you end up creating a new controller everytime..not sure!

@johnpryan @synw
https://github.com/johnpryan/flutter_map/pull/542
PR available :+1:

@johnpryan @synw

542

PR available 馃憤

So I just have to wait till they merge it and publish it on the pub.dev?

I tried it on your branch, but I want to implement it in my project, so I was confused how should I do that if it's not available in the package.

If you want to use another github source as a dependency, you can add it to your pubspec.yaml (you can always switch back later).

https://stackoverflow.com/questions/54022704/how-to-add-a-package-from-github-flutter

so for the flutter_map entry, point it to the git repo instead like it shows in the post.

If you want to use another github source as a dependency, you can add it to your pubspec.yaml (you can always switch back later).

https://stackoverflow.com/questions/54022704/how-to-add-a-package-from-github-flutter

so for the flutter_map entry, point it to the git repo instead like it shows in the post.

Thank you very much, it's working!

I had the same issue than @TenPetr
@ruizalexandre solution definitely made the job
The plugin is so much faster
Any idea if and when it will be released on the official channel ?

Was this page helpful?
0 / 5 - 0 ratings