Skip to content

[google_maps_flutter][iOS]: Tile overlays not rendering correct color for partially transparent solid PNG tiles #156238

Open
@martyfuhry

Description

@martyfuhry

Steps to reproduce

  1. Create a TileOverlay in Flutter iOS
  2. Supply single-colored PNG tile with a transparency as all tiles

For example, use this as the tile:
not_working_tile

Expected results

The tiles are rendered on the map using the correct color and transparency.

For example, I expect this to be the rendered map from the above tile:

image

Actual results

The tiles are all the wrong color. They display as grey. This is the rendered map from the above tile:

image

Code sample

See my example project here

Code sample
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: public_member_api_docs

import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

import 'page.dart';

class TileOverlayPage extends GoogleMapExampleAppPage {
  const TileOverlayPage({Key? key})
      : super(const Icon(Icons.map), 'Tile overlay', key: key);

  @override
  Widget build(BuildContext context) {
    return const TileOverlayBody();
  }
}

class TileOverlayBody extends StatefulWidget {
  const TileOverlayBody({super.key});

  @override
  State<TileOverlayBody> createState() => _TileOverlayBodyState();
}

class _TileOverlayBodyState extends State<TileOverlayBody> {
  final ValueNotifier<TileOverlay?> _tileOverlay =
      ValueNotifier<TileOverlay?>(null);

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<TileOverlay?>(
      valueListenable: _tileOverlay,
      builder: (BuildContext context, TileOverlay? overlay, Widget? child) {
        final Set<TileOverlay> overlays =
            overlay == null ? <TileOverlay>{} : <TileOverlay>{overlay};

        return Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Center(
              child: SizedBox(
                width: 350.0,
                height: 300.0,
                child: GoogleMap(
                  initialCameraPosition: const CameraPosition(
                    target: LatLng(59.935460, 30.325177),
                    zoom: 7.0,
                  ),
                  tileOverlays: overlays,
                ),
              ),
            ),
            TextButton.icon(
              icon: Image.asset('assets/working_transparent_tile.png', height: 50,),
              onPressed: () {
                _tileOverlay.value =
                    _getOverlay('assets/working_transparent_tile.png');
              },
              label: const Text(
                'Use shaped transparent',
              ),
            ),
            TextButton.icon(
              icon: Image.asset('assets/not_working_transparent_tile.png', height: 50,),
              onPressed: () {
                _tileOverlay.value =
                    _getOverlay('assets/not_working_transparent_tile.png');
              },
              label: const Text(
                'Use solid (BROKEN)',
              ),
            ),
            TextButton.icon(
              icon: Image.asset('assets/working_opaque_tile.png', height: 50,),
              onPressed: () {
                _tileOverlay.value = _getOverlay(
                  'assets/working_opaque_tile.png',
                  transparency: 0.75,
                );
              },
              label: const Text(
                'Use shaped opaque with 75% transparency',
              ),
            ),
            TextButton.icon(
              icon: Image.asset('assets/not_working_opaque_tile.png', height: 50,),
              onPressed: () {
                _tileOverlay.value = _getOverlay(
                  'assets/not_working_opaque_tile.png',
                  transparency: 0.75,
                );
              },
              label: const Text(
                'Use solid opaque with 75% transpancy',
              ),
            ),
          ],
        );
      },
    );
  }

  TileOverlay _getOverlay(String filePath, {double transparency = 0}) {
    return TileOverlay(
      tileOverlayId: TileOverlayId('$transparency$filePath'),
      tileProvider: _ImageTileProvider(filePath),
      transparency: transparency,
    );
  }
}

class _ImageTileProvider implements TileProvider {
  const _ImageTileProvider(this.imagePath);

  final String imagePath;

  @override
  Future<Tile> getTile(int x, int y, int? zoom) async {
    final ByteData tileImage = await rootBundle.load(imagePath);
    return Tile(512, 512, tileImage.buffer.asUint8List());
  }
}

Screenshots or Video

Screenshots / Video demonstration

Shaped transparent
image

Solid (BROKEN)
image

Shaped opaque with 75% transparency
image

Solid opaque with 75% transparency
image

You can see above that supplying a non-transparent image and then using transparency: 0.75 in the TileOverlay gives me the correct behavior. Simply using a transparent image gives me the broken behavior above.

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.24.0, on macOS 14.5 23F79 darwin-x64, locale en-US)
    • Flutter version 3.24.0 on channel stable at /Users/martyfuhry/fvm/versions/3.24.0
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 80c2e84975 (9 weeks ago), 2024-07-30 23:06:49 +0700
    • Engine revision b8800d88be
    • Dart version 3.5.0
    • DevTools version 2.37.2

[!] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at /Users/martyfuhry/Library/Android/sdk
    • Platform android-33, build-tools 30.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    ✗ Could not determine java version
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 15.0.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 15A507
    • CocoaPods version 1.15.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 3.6)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 49.0.1
    • Dart plugin version 192.8052
    • Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)

[✓] Connected device (4 available)
    • Solanus the iPhone (2) (mobile) • 00008030-00044D462189802E            • ios            • iOS 17.6.1 21G93
    • iPhone 15 (mobile)              • 5599814B-93C5-42B9-B2D4-BD26E74E856A • ios            • com.apple.CoreSimulator.SimRuntime.iOS-17-0 (simulator)
    • macOS (desktop)                 • macos                                • darwin-x64     • macOS 14.5 23F79 darwin-x64
    • Chrome (web)                    • chrome                               • web-javascript • Google Chrome 129.0.6668.90
    ! Error: Browsing on the local area network for Solanus the iPhone. Ensure the device is unlocked and attached with a cable or associated with the same
      local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)

[✓] Network resources
    • All expected network resources are available.

! Doctor found issues in 1 category.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listfound in release: 3.24Found to occur in 3.24found in release: 3.26Found to occur in 3.26has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: mapsGoogle Maps pluginpackageflutter/packages repository. See also p: labels.platform-iosiOS applications specificallyteam-iosOwned by iOS platform teamtriaged-iosTriaged by iOS platform team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions