Fade welcome bees with the QR scanner transition (#8859)

Fixes the welcome bees remaining visible when the QR scanner opens after
#8853. Bees now share the foreground’s fade and scale progress, pause
while hidden, and return on dismissal. Reduce Motion skips scaling.

Validated scanner open/close in the iPhone simulator,
normal/reduced-motion widget journeys, analysis, and the full mobile
suite. Simulator camera hardware is unavailable; QR decoding was not
exercised. Independent review found no blockers.


https://github.com/user-attachments/assets/1ce046b2-63e3-4441-90e9-9d9ec048bdfa

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
This commit is contained in:
klopez4212
2026-10-31 16:09:27 +00:00
committed by GitHub
parent 2ca77747d1
commit 0a46f329c0
4 changed files with 54 additions and 7 deletions

View File

@@ -365,6 +365,7 @@ class PairingPage extends HookConsumerWidget {
value: onboardingSystemOverlayStyle,
child: _OnboardingBackground(
websiteWelcome: websiteWelcome,
scannerProgress: islandScannerProgress,
child: Scaffold(
resizeToAvoidBottomInset:
hostedFlow?.stage != HostedFlowStage.profile,

View File

@@ -3,16 +3,21 @@ part of '../pairing_page.dart';
class _OnboardingBackground extends StatelessWidget {
final Widget child;
final bool websiteWelcome;
final ValueListenable<double> scannerProgress;
const _OnboardingBackground({
required this.child,
this.websiteWelcome = false,
this.scannerProgress = const AlwaysStoppedAnimation(0),
});
@override
Widget build(BuildContext context) {
if (websiteWelcome) {
return _WebsiteWelcomeBackground(child: child);
return _WebsiteWelcomeBackground(
scannerProgress: scannerProgress,
child: child,
);
}
final isDark = context._onboardingIsDark;
return DecoratedBox(

View File

@@ -4,8 +4,12 @@ part of '../pairing_page.dart';
// The bee layer uses the website’s flight model and remains decorative.
class _WebsiteWelcomeBackground extends HookWidget {
final Widget child;
final ValueListenable<double> scannerProgress;
const _WebsiteWelcomeBackground({required this.child});
const _WebsiteWelcomeBackground({
required this.child,
required this.scannerProgress,
});
@override
Widget build(BuildContext context) {
@@ -29,11 +33,28 @@ class _WebsiteWelcomeBackground extends HookWidget {
Positioned.fill(
child: IgnorePointer(
child: ExcludeSemantics(
child: LayoutBuilder(
builder: (context, constraints) => RepaintBoundary(
child: _WebsiteBeeSwarm(
size: constraints.biggest,
pointer: pointer,
child: ValueListenableBuilder<double>(
valueListenable: scannerProgress,
builder: (context, progress, child) => Opacity(
key: const Key('scanner-bees-opacity'),
opacity: 1 - progress,
child: Transform.scale(
key: const Key('scanner-bees-scale'),
scale: MediaQuery.disableAnimationsOf(context)
? 1
: 1 - 0.04 * progress,
child: TickerMode(
enabled: progress < 1,
child: child!,
),
),
),
child: LayoutBuilder(
builder: (context, constraints) => RepaintBoundary(
child: _WebsiteBeeSwarm(
size: constraints.biggest,
pointer: pointer,
),
),
),
),

View File

@@ -317,6 +317,23 @@ void main() {
await tester.pump();
final opacity = find.byKey(const Key('scanner-welcome-opacity'));
final scale = find.byKey(const Key('scanner-welcome-scale'));
void expectBeesMatchWelcome() {
if (adding) return;
expect(
tester
.widget<Opacity>(find.byKey(const Key('scanner-bees-opacity')))
.opacity,
tester.widget<Opacity>(opacity).opacity,
);
expect(
tester
.widget<Transform>(find.byKey(const Key('scanner-bees-scale')))
.transform
.storage[0],
tester.widget<Transform>(scale).transform.storage[0],
);
}
// The welcome swarm animates continuously. Wait for the scanner's
// observable transition endpoint rather than global frame idleness.
Future<void> waitForOpacity(double target) async {
@@ -372,7 +389,9 @@ void main() {
closeTo(1 - 0.04 * progress, 0.001),
);
}
expectBeesMatchWelcome();
await waitForOpacity(0);
expectBeesMatchWelcome();
expect(tester.widget<Opacity>(opacity).opacity, 0);
expect(
tester.widget<Transform>(scale).transform.storage[0],
@@ -390,6 +409,7 @@ void main() {
await tester.tapAt(const Offset(196.5, 700));
await tester.pump();
await waitForOpacity(1);
expectBeesMatchWelcome();
expect(tester.widget<Opacity>(opacity).opacity, 1);
expect(tester.widget<Transform>(scale).transform.storage[0], 1);
expect(