Sniady
Sniady

Reputation: 1693

Flutter FutureBuilder used with Google Maps plugin and custom icons makes the screen flick to black at start

I'm trying to add a google map widget with custom icons on the screen. For that reason, I need to use the FutureBuilder to get the icons BitmapDescriptor.fromAssetImage before I try to render the map. This method is an async one so I have thrown it into the FutureBuilder so the entire widget waits until all my icons are allocated.

The app works, but the problem is that it has this quick but very noticeable screen flick, which makes the area black. I have a progress indicator but it does not matter, the black screen always shows up.

@override
  Widget build(BuildContext context) {
    return Consumer <RestaurantsData>(
        builder: (context, _restaurantsData, __) => FutureBuilder<bool>(
          future: _prepareWidget(),
          builder: (BuildContext buildContext, AsyncSnapshot<bool> snapshot) {
            if (snapshot.connectionState == ConnectionState.done
                && snapshot.data
                && snapshot.hasData) {
              return googleMap(_restaurantsData);
            }
            logger.d("Map widget still processing");
            return Center(
              // Display Progress Indicator
              child: CircularProgressIndicator(
                backgroundColor: Colors.white,
              ),
            );
          })
    );
  }

Widget googleMap(RestaurantsData _restaurantsData){
    return Container(
        child: GoogleMap(
            mapType: MapType.normal,
            myLocationEnabled: true,
            myLocationButtonEnabled: true,
            mapToolbarEnabled: false,
            zoomGesturesEnabled: true,
            rotateGesturesEnabled: true,
            initialCameraPosition: CameraPosition(
                target: _defaultLatLng,
                zoom: _defaultZoom
            ),
            onMapCreated: (GoogleMapController controller) {
              _controller = controller;
            },
            onTap: (latLng) {
              if (bottomSheetCtrl != null) {
                bottomSheetCtrl.close();
                bottomSheetCtrl = null;
              }
            },
            markers: createRestaurantMarkers(_restaurantsData),
            gestureRecognizers: _gestureRecognizers
        )
    );
  }

  Set<Marker> createRestaurantMarkers(RestaurantsData restaurantsData) {
    return restaurantsData.getRestaurants()
        .map((item) =>
        Marker(
            position: item.latLng,
            markerId: MarkerId("${item.id}|${item.name}"),
            icon: getIconFor(item),
            onTap: () {
              logger.i("Marker clicked: ${item.id}, ${item.name}");
              showRestaurantDetails(context, item);
              restaurantsData.selectOne(item);
            }
        ))
        .toSet();
  }

The prepare widget call is as follows:

Future<bool> _prepareWidget() async {
    _markerIcon = await BitmapDescriptor.fromAssetImage(
        ImageConfiguration(devicePixelRatio: 2.5),
        marker
    );
    _selectedMarkerIcon = await BitmapDescriptor.fromAssetImage(
        ImageConfiguration(devicePixelRatio: 2.5),
        markerSelected
    );
    _disabledMarkerIcon = await BitmapDescriptor.fromAssetImage(
        ImageConfiguration(devicePixelRatio: 2.5),
        disabledMarker
    );
    _disabledSelectedMarkerIcon = await BitmapDescriptor.fromAssetImage(
        ImageConfiguration(devicePixelRatio: 2.5),
        disabledMarkerSelected
    );

    return Future.value(_markerIcon != null
        && _selectedMarkerIcon != null
        && _disabledMarkerIcon != null
        && _disabledSelectedMarkerIcon != null
    );
  }

I have 4 icons that are always static and available from the start of the app.

Any ideas on how to fix this? I'm fairly new to flutter so this issue maybe because of something that I'm doing wrong.

Upvotes: 2

Views: 2090

Answers (1)

Sniady
Sniady

Reputation: 1693

I fixed the flickering by building the Future using FuturBuilder and passing it as a Provider in an ancestor widget. It's build before all main widgets and only once.

In a parent widget:

return MultiProvider(
        providers: [
          FutureProvider<MapMarkerProvider>(
              lazy: false,
              create: (_) async => MapMarkerProvider().loadMarkers() //<- Async Action loading icons.
          ),
        ],
        child: Scaffold(
            appBar: new EmptyAppBar(),
            body: Container(
                ....
            )
        )
    );
  }

Upvotes: 3

Related Questions