import 'package:flutter/material.dart'; import 'focus_theme.dart'; /// Exposes the focus state of an enclosing focus wrapper to a descendant /// [CardFocusBorder] that draws the focus border itself. /// /// The shared focus chrome ([buildFocusChrome]) inserts this instead of painting /// a border when `delegateFocusBorder` is set, so cards can /// put the border on the exact rect the design highlights (the poster image, /// not the card-plus-captions rect — issue #1278). Only the [CardFocusBorder] /// element registers a dependency, so a focus flip rebuilds just that border /// box: a stable `child:` card subtree (e.g. the TV rail's MediaCard) is not /// rebuilt. class CardFocusScope extends InheritedWidget { const CardFocusScope({super.key, required this.showFocus, required super.child}); /// Whether the enclosing wrapper currently shows focus visuals /// (focused while in keyboard/d-pad input mode). final bool showFocus; /// Null when no delegating wrapper is above (touch mode skips the focus /// wrappers entirely) — [CardFocusBorder] then renders its child bare. static bool? maybeOf(BuildContext context) => context.dependOnInheritedWidgetOfExactType()?.showFocus; @override bool updateShouldNotify(CardFocusScope oldWidget) => showFocus != oldWidget.showFocus; } /// Draws the focus border around its child based on the enclosing /// [CardFocusScope], letting the card decide which rect gets highlighted. /// /// Defaults to an outside stroke so the border hugs the child exactly like the /// full-bleed card treatment (the child's own corner radius nests inside it). /// Decoration only — never affects layout. Renders the child unchanged when no /// scope is present. class CardFocusBorder extends StatelessWidget { const CardFocusBorder({ super.key, this.borderRadius = FocusTheme.defaultBorderRadius, this.borderRadii, this.strokeAlign = BorderSide.strokeAlignOutside, required this.child, }); final double borderRadius; /// Per-corner radii; overrides [borderRadius] when set (M3E grouped cards). final BorderRadius? borderRadii; final double strokeAlign; final Widget child; @override Widget build(BuildContext context) { final showFocus = CardFocusScope.maybeOf(context); if (showFocus == null) return child; return AnimatedContainer( duration: FocusTheme.getAnimationDuration(context), curve: Curves.easeOutCubic, foregroundDecoration: FocusTheme.focusDecoration( context, isFocused: showFocus, borderRadius: borderRadius, radii: borderRadii, borderStrokeAlign: strokeAlign, ), child: child, ); } }