Skip to content

fix: frame skip due to native viewer rebuilds#26539

Closed
shenlong-tanwen wants to merge 2 commits intomainfrom
fix/bring-back-globalkeys
Closed

fix: frame skip due to native viewer rebuilds#26539
shenlong-tanwen wants to merge 2 commits intomainfrom
fix/bring-back-globalkeys

Conversation

@shenlong-tanwen
Copy link
Member

No description provided.

late final _heroOffset = widget.heroOffset ?? TabsRouterScope.of(context)?.controller.activeIndex ?? 0;
late final _pageController = PageController(initialPage: widget.initialIndex);
late final _preloader = AssetPreloader(timelineService: ref.read(timelineServiceProvider), mounted: () => mounted);
Map<String, GlobalKey> _videoPlayerKeys = {};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not the prettiest way to do it, but the state has to be above the asset viewer page and since it is tied to the lifecycle of the asset viewer page, added this here for now

late final _heroOffset = widget.heroOffset ?? TabsRouterScope.of(context)?.controller.activeIndex ?? 0;
late final _pageController = PageController(initialPage: widget.initialIndex);
late final _preloader = AssetPreloader(timelineService: ref.read(timelineServiceProvider), mounted: () => mounted);
final Map<String, GlobalKey> _videoPlayerKeys = {};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to keep track of video player keys for all asset pages? Can't each page have its own global key? That should be enough to prevent the video from being rebuilt when it matters?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should never be two video widgets for the same asset.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe what I said wasn't clear - this map will grow unbounded. So if a user swipes between lots of videos, we'll end up with lots of global keys for no real reason.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and my suggestion was that we could have a field on the asset page state like

GlobalKey _videoKey;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that seems like it can work.

_listenForScaleBoundaries(controller);
}

GlobalKey _getVideoPlayerKey(String id) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let’s add a comment about why this is necessary.

backgroundDecoration: backgroundDecoration,
child: NativeVideoViewer(
key: ValueKey(displayAsset),
key: _getVideoPlayerKey(displayAsset.heroTag),
Copy link
Collaborator

@uhthomas uhthomas Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also I wonder if this is just my mistake. Can we try with just:

Suggested change
key: _getVideoPlayerKey(displayAsset.heroTag),
key: Key(displayAsset.heroTag),

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spent a lot of time on this and landed on global keys to make the behavior correct. We can test how alternatives behave in terms of rebuilding etc., but I feel this is already a solved problem.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be keen to try plain keys with a stable id (hero tag in this case). It should work the same?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's also a lot less code and a lot less confusing

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not the same because the hero animation tries to snapshot the destination, which creates two separate widget states for the video. A global key was the only way I found to avoid that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds weird - let's try with the Key on hero tag and see if it fixes it. All the research I've done into global keys suggests that it doesn't really make sense here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I think I have a better understanding now. So although a ValueKey does help when siblings are moved around, it doesn't help if the parent is recreated. It may create the exact same tree again, but the widget with those keys are disregarded. A global key will persist it, even if the parent is rebuilt. I see why a global key is needed, though I do think we should just keep the global key on the state of the asset page rather than having a map. I imagine the map was needed when both the asset page and asset viewer were one big widget. Now that it's its own separate stateful widget, it's not needed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if the video is the right level for it though. Surely painting images can be kind of expensive too. Should the global key be on the photo view widget itself?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of the image provider / image cache system, it's not as bad for images since the actual image loading "should" persist and this is the expensive part (painting an already loaded bitmap is relatively cheap). But it can be problematic if it results in the image request being cancelled and then re-requested. I haven't really looked into it though tbh, could be fine to use global keys for either and not just for videos.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I am concerned that this won't work properly for asset stacks, with videos.

Is there any reason we can't just use GlobalObjectKey(displayAsset.heroTag)? That means we don't need to store the global key anywhere, and it will work properly with asset stacks.

@uhthomas
Copy link
Collaborator

also fixes #26448

}

return PhotoView.customChild(
key: ValueKey(displayAsset),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, we need to fix the key here. Maybe we should remove the key from the native video player and just have it here.

@shenlong-tanwen
Copy link
Member Author

Superseded by #26553

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants