Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 61 additions & 7 deletions lib/src/layer/tile_layer/tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,37 +49,91 @@ class Tile extends StatefulWidget {
}

class _TileState extends State<Tile> {
late final String tileKey;

@override
void initState() {
super.initState();
widget.tileImage.addListener(_onTileImageChange);
tileKey = '${widget.positionCoordinates}:${widget.tileImage.coordinates}';
}

@override
void dispose() {
widget.tileImage.removeListener(_onTileImageChange);
super.dispose();
}

void _onTileImageChange() {
if (mounted) setState(() {});
// Used to prevent unnecessary rebuilds
@override
void didUpdateWidget(Tile oldWidget) {
super.didUpdateWidget(oldWidget);
// We only care about position changes for rebuilds, not image content changes
// as those are handled by the TileImageWidget
if (oldWidget.currentPixelOrigin != widget.currentPixelOrigin ||
oldWidget.scaledTileDimension != widget.scaledTileDimension) {
setState(() {});
}
}

@override
Widget build(BuildContext context) {
final tileImageWidget = RepaintBoundary(
child: TileImageWidget(
key: ValueKey(widget.tileImage.coordinates.toString()),
tileImage: widget.tileImage,
),
);

return Positioned(
left: widget.positionCoordinates.x * widget.scaledTileDimension -
widget.currentPixelOrigin.dx,
top: widget.positionCoordinates.y * widget.scaledTileDimension -
widget.currentPixelOrigin.dy,
width: widget.scaledTileDimension,
height: widget.scaledTileDimension,
child: widget.tileBuilder?.call(context, _tileImage, widget.tileImage) ??
_tileImage,
child: widget.tileBuilder
?.call(context, tileImageWidget, widget.tileImage) ??
tileImageWidget,
);
}
}

/// A widget that displays a tile image.
///
/// This widget is separated from the [Tile] class to prevent unnecessary rebuilds.
@immutable
class TileImageWidget extends StatefulWidget {
/// The tile image data.
final TileImage tileImage;

/// Creates a new instance of [TileImageWidget].
const TileImageWidget({
super.key,
required this.tileImage,
});

@override
State<TileImageWidget> createState() => _TileImageWidgetState();
}

class _TileImageWidgetState extends State<TileImageWidget> {
@override
void initState() {
super.initState();
widget.tileImage.addListener(_onTileImageChange);
}

Widget get _tileImage {
@override
void dispose() {
widget.tileImage.removeListener(_onTileImageChange);
super.dispose();
}

void _onTileImageChange() {
if (mounted) setState(() {});
}

@override
Widget build(BuildContext context) {
if (widget.tileImage.loadError && widget.tileImage.errorImage != null) {
return Image(
image: widget.tileImage.errorImage!,
Expand Down
34 changes: 20 additions & 14 deletions lib/src/layer/tile_layer/tile_layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -579,23 +579,29 @@ See:
// tiles that are *ready* and at the target zoom level.
// We're happy to do a bit of diligent work here, since tiles not rendered are
// cycles saved later on in the render pipeline.
final tiles = _tileImageManager
final tileRenderers = _tileImageManager
.getTilesToRender(visibleRange: visibleTileRange)
.map((tileRenderer) => Tile(
// Must be an ObjectKey, not a ValueKey using the coordinates, in
// case we remove and replace the TileImage with a different one.
key: ObjectKey(tileRenderer),
scaledTileDimension: _tileScaleCalculator.scaledTileDimension(
map.zoom,
tileRenderer.positionCoordinates.z,
),
currentPixelOrigin: map.pixelOrigin,
tileImage: tileRenderer.tileImage,
positionCoordinates: tileRenderer.positionCoordinates,
tileBuilder: widget.tileBuilder,
))
.toList();

// Create a map of TileRenderer to Tile widgets with a stable key to prevent rebuilding
final tiles = tileRenderers.map((tileRenderer) {
// Use the coordinates as a unique identifier for the tile
final tileKey = ValueKey(
'${tileRenderer.positionCoordinates}:${tileRenderer.tileImage.coordinates}');

return Tile(
key: tileKey,
scaledTileDimension: _tileScaleCalculator.scaledTileDimension(
map.zoom,
tileRenderer.positionCoordinates.z,
),
currentPixelOrigin: map.pixelOrigin,
tileImage: tileRenderer.tileImage,
positionCoordinates: tileRenderer.positionCoordinates,
tileBuilder: widget.tileBuilder,
);
}).toList();

// Sort in render order. In reverse:
// 1. Tiles at the current zoom.
// 2. Tiles at the current zoom +/- 1.
Expand Down