1

I have 2 widgets. A parent StatefulWidget which changes states multiple times during first full load, and a child StatelessWidget which is expensive to build.

The reason the the child widget is expensive to build is because it is using google_maps_flutter library which is using web view to display google maps on screen.

If possible, I want build() function in this child widget to be executed only once.

However, whenever the parent widget is getting built multiple times due to state changes, it seems like the child widget is also getting built multiple times. Due to this, I see a bit of stuttering/lagging when the screen loads for the first time.

What is the best way to prevent child widget from getting rebuilt multiple times?

Below is a sample code.

Parent Widget

class ParentWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
   Completer<GoogleMapController> _controller = Completer();
   LocationPosition _myPosition;

   @override
   void initState() {
     super.initState();
     initialize();
   }

   void initialize(){
     ....// other initialization logic which may call `setState()` multiple times
   }

   Set<Marker> getMarkers(){
     ....
   }

   @override
   Widget build(BuildContext context) {
     return Scaffold(body: GoogleMapWidget(_controller, _myPosition, getMarkers()));
   }
}

Child widget

class GoogleMapWidget extends StatelessWidget {
  static const double ZOOM = 15;
  final Completer<GoogleMapController> _controller;
  final Set<Marker> markers;
  final LocationPosition _myPosition;
  GoogleMapWidget(this._controller, this._myPosition, this.markers);

  @override
  Widget build(BuildContext context) {
    print("Rebuilt"); // <-- This gets printed multiple times, which is not something I want.
    return GoogleMap(
        mapType: MapType.normal,
        initialCameraPosition: CameraPosition(
          target: LatLng(_myPosition.lat, _myPosition.lng),
          zoom: ZOOM,
        ),
        onMapCreated: (GoogleMapController controller) {
          _controller.complete(controller);
        },
        markers: markers);
  }
}
user482594
  • 16,878
  • 21
  • 72
  • 108
  • I would use one of the state management systems rather than trying to manage setState manually. Check out the Provider package. It has options for only building when necessary. – Suragch Jan 28 '20 at 00:33

1 Answers1

0

Every time setState is called, it triggers a rebuild. And calling it inside initState is probalbly not a good idea. Calling it multiple times is a no-no.

Importance of Calling SetState inside initState

To avoid multiple rebuilds,

  • Intialise all your objects in local fields and only when all are ready, you can call setState once.

  • Don't do any other processing other than setting variables in setState.

  • Don't trigger http/api calls in the build method. Even if you are, add conditional logic to only do so if required.

  • Before calling setState, you might want to check if the object is actually changed.

Also in particular to google maps package, check out the MapController methods, as you might be able to use the provided methods instead of setting the properties of the widget itself. This way the google maps package can figure out if setState is required.

ravi
  • 949
  • 11
  • 22
  • The reason `setState` is called in `initState` is to retrieve [1. user's current location] and then [2. fetch nearby locations for markers]. These need to be executed once, thus I have put them into the `initState`. Isn't this the best place to put them in? – user482594 Jan 28 '20 at 00:39
  • I assume both these calls are async. So can kick it off in initState and when they complete you can call setState (outside initState method). Also why would you need to setState after getting location, can't you wait until you got the markers. – ravi Jan 28 '20 at 01:48
  • Getting user's location takes 500ms~2000ms, and getting location can take another 500ms~3000ms. We provide early UX feedbacks to users by displaying ["We are determining your location..."] first, and when user's location info is available, we display ["We are finding nearby place of interests near you..."] for better user experience. That is why `setState` is called multiple times. – user482594 Jan 28 '20 at 04:08