diff --git a/lib/src/app.dart b/lib/src/app.dart index 1761f3fa43..c6beddb828 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -215,6 +215,13 @@ class _AppState extends ConsumerState { blendLevel: 20, ); + const iosMenuTheme = MenuThemeData( + style: MenuStyle( + elevation: WidgetStatePropertyAll(0), + shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: kCardBorderRadius)), + ), + ); + return AnnotatedRegion( value: FlexColorScheme.themedSystemNavigationBar( context, @@ -234,6 +241,7 @@ class _AppState extends ConsumerState { subtitleTextStyle: isIOS ? lightCupertino.textTheme.textStyle : null, leadingAndTrailingTextStyle: isIOS ? lightCupertino.textTheme.textStyle : null, ), + menuTheme: isIOS ? iosMenuTheme : null, floatingActionButtonTheme: floatingActionButtonTheme, navigationBarTheme: NavigationBarTheme.of(context).copyWith( height: remainingHeight < kSmallRemainingHeightLeftBoardThreshold ? 60 : null, @@ -252,6 +260,7 @@ class _AppState extends ConsumerState { subtitleTextStyle: isIOS ? darkCupertino.textTheme.textStyle : null, leadingAndTrailingTextStyle: isIOS ? darkCupertino.textTheme.textStyle : null, ), + menuTheme: isIOS ? iosMenuTheme : null, floatingActionButtonTheme: floatingActionButtonTheme, navigationBarTheme: NavigationBarTheme.of(context).copyWith( height: remainingHeight < kSmallRemainingHeightLeftBoardThreshold ? 60 : null, diff --git a/lib/src/view/broadcast/broadcast_list_screen.dart b/lib/src/view/broadcast/broadcast_list_screen.dart index 7efe86c09b..9d582f8428 100644 --- a/lib/src/view/broadcast/broadcast_list_screen.dart +++ b/lib/src/view/broadcast/broadcast_list_screen.dart @@ -14,6 +14,7 @@ import 'package:lichess_mobile/src/model/broadcast/broadcast.dart'; import 'package:lichess_mobile/src/model/broadcast/broadcast_providers.dart'; import 'package:lichess_mobile/src/model/common/id.dart'; import 'package:lichess_mobile/src/navigation.dart' show watchTabInteraction; +import 'package:lichess_mobile/src/network/http.dart'; import 'package:lichess_mobile/src/styles/lichess_colors.dart'; import 'package:lichess_mobile/src/styles/styles.dart'; import 'package:lichess_mobile/src/utils/image.dart'; @@ -21,7 +22,10 @@ import 'package:lichess_mobile/src/utils/l10n.dart'; import 'package:lichess_mobile/src/utils/l10n_context.dart'; import 'package:lichess_mobile/src/utils/navigation.dart'; import 'package:lichess_mobile/src/utils/screen.dart'; +import 'package:lichess_mobile/src/utils/share.dart'; import 'package:lichess_mobile/src/view/broadcast/broadcast_round_screen.dart'; +import 'package:lichess_mobile/src/widgets/adaptive_action_sheet.dart'; +import 'package:lichess_mobile/src/widgets/buttons.dart'; import 'package:lichess_mobile/src/widgets/cupertino.dart'; import 'package:lichess_mobile/src/widgets/platform.dart'; import 'package:lichess_mobile/src/widgets/shimmer.dart'; @@ -136,7 +140,7 @@ class _BodyState extends ConsumerState<_Body> { crossAxisCount: itemsByRow, crossAxisSpacing: 12.0, mainAxisSpacing: 16.0, - childAspectRatio: 1.35, + childAspectRatio: 1.2, ); final lowTierGridDelegate = SliverGridDelegateWithFixedCrossAxisCount( @@ -145,10 +149,10 @@ class _BodyState extends ConsumerState<_Body> { mainAxisSpacing: 16.0, childAspectRatio: screenWidth >= 1200 - ? 1.4 + ? 1.2 : screenWidth >= 700 - ? 1.3 - : 1.0, + ? 1.0 + : 0.8, ); final sections = [ @@ -346,7 +350,8 @@ class _BroadcastCarouselState extends State { final widgetWidth = constraints.maxWidth; final elementWidth = widgetWidth * flexWeights[0] / flexWeights.reduce((a, b) => a + b); final pictureHeight = elementWidth / 2; - final elementHeight = pictureHeight + (pictureHeight * 0.5); + final elementHeightFactor = flexWeights.length == 2 ? 0.75 : 0.6; + final elementHeight = pictureHeight + (pictureHeight * elementHeightFactor); return Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: ConstrainedBox( @@ -360,15 +365,7 @@ class _BroadcastCarouselState extends State { flexWeights: flexWeights, itemSnapping: true, padding: kBroadcastCarouselItemPadding, - onTap: (index) { - final broadcast = widget.broadcasts.active[index]; - pushPlatformRoute( - context, - title: broadcast.title, - rootNavigator: true, - builder: (context) => BroadcastRoundScreen(broadcast: broadcast), - ); - }, + enableSplash: false, children: [ if (widget._isLoading) for (final _ in [1, 2, 3, 4, 5, 6, 7, 8, 9]) @@ -412,104 +409,209 @@ class _BroadcastCardContent extends StatelessWidget { } } - final backgroundColor = _cardColors?.primaryContainer ?? Styles.cardColor(context); final titleColor = _cardColors?.onPrimaryContainer; final subTitleColor = _cardColors?.onPrimaryContainer.withValues(alpha: 0.8) ?? textShade(context, 0.8); - final bgHsl = HSLColor.fromColor(backgroundColor); - final liveHsl = HSLColor.fromColor(LichessColors.red); - final liveColor = (bgHsl.lightness <= 0.6 ? liveHsl.withLightness(0.9) : liveHsl).toColor(); - - return Padding( - padding: kBroadcastCardItemContentPadding, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + + return Stack( + children: [ + Padding( + padding: kBroadcastCardItemContentPadding, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Row( - mainAxisAlignment: - broadcast.isLive ? MainAxisAlignment.spaceBetween : MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.baseline, - textBaseline: TextBaseline.alphabetic, + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (!broadcast.isFinished) ...[ - Flexible( - flex: broadcast.isLive ? 1 : 0, - child: Text( - broadcast.round.name, - style: TextStyle(color: subTitleColor, letterSpacing: -0.2), - overflow: TextOverflow.clip, - softWrap: false, - maxLines: 1, - ), - ), - const SizedBox(width: 5.0), - ], - if (broadcast.isLive) ...[ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.circle, - size: 16, - color: liveColor, - shadows: const [ - Shadow(color: Colors.black54, offset: Offset(0, 1), blurRadius: 2), - ], + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.alphabetic, + children: [ + if (!broadcast.isFinished) ...[ + Flexible( + flex: 0, + child: Text( + broadcast.round.name, + style: TextStyle(color: subTitleColor, letterSpacing: -0.2), + overflow: TextOverflow.clip, + softWrap: false, + maxLines: 1, + ), ), - const SizedBox(width: 4.0), - Text( - 'LIVE', + const SizedBox(width: 5.0), + ], + if (eventDate != null) + Flexible( + child: Text( + eventDate, + style: TextStyle(fontSize: 12, color: subTitleColor), + overflow: TextOverflow.clip, + softWrap: false, + maxLines: 1, + ), + ), + ], + ), + const SizedBox(height: 6.0), + Text.rich( + maxLines: 3, + TextSpan( + children: [ + TextSpan( + text: broadcast.title, style: TextStyle( - fontSize: 14, + color: titleColor, fontWeight: FontWeight.bold, - color: liveColor, - shadows: const [ - Shadow(color: Colors.black54, offset: Offset(0, 1), blurRadius: 2), - ], + fontSize: 16.0, + height: 1.0, ), - overflow: TextOverflow.ellipsis, ), + if (broadcast.tour.information.players != null) + TextSpan( + text: '\n${broadcast.tour.information.players}', + style: TextStyle( + color: subTitleColor, + fontSize: 12, + letterSpacing: -0.2, + ), + ), ], ), - ] else if (eventDate != null) - Flexible( - child: Text( - eventDate, - style: TextStyle(fontSize: 12, color: subTitleColor), - overflow: TextOverflow.clip, - softWrap: false, - maxLines: 1, - ), - ), + overflow: TextOverflow.ellipsis, + ), ], ), - Text( - broadcast.title, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: titleColor, - fontWeight: FontWeight.bold, - fontSize: 16.0, - height: 1.0, + ], + ), + ), + Positioned( + bottom: 0, + left: kBroadcastCardItemContentPadding.horizontal / 2, + right: 0, + // bottom: kBroadcastCardItemContentPadding.vertical / 2, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (broadcast.isLive) ...[ + Container( + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 1.0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15.0), + ), + child: const Text( + 'LIVE', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: LichessColors.red, + ), + ), ), + const Spacer(), + ], + PlatformContextMenuAnchor( + builder: + (context, controller, _) => AppBarIconButton( + onPressed: () { + if (controller.isOpen) { + controller.close(); + } else { + controller.open(); + } + }, + semanticsLabel: context.l10n.menu, + icon: Icon(Icons.more_horiz, color: titleColor?.withValues(alpha: 0.5)), + ), + actions: [ + AppBarMenuAction( + icon: Icons.info, + label: context.l10n.broadcastOverview, + onPressed: () { + pushPlatformRoute( + context, + title: broadcast.title, + rootNavigator: true, + builder: + (context) => BroadcastRoundScreen( + broadcast: broadcast, + initialTab: BroadcastRoundTab.overview, + ), + ); + }, + ), + AppBarMenuAction( + icon: Icons.people, + label: context.l10n.players, + onPressed: () { + pushPlatformRoute( + context, + title: broadcast.title, + rootNavigator: true, + builder: + (context) => BroadcastRoundScreen( + broadcast: broadcast, + initialTab: BroadcastRoundTab.players, + ), + ); + }, + ), + AppBarMenuAction( + icon: + Theme.of(context).platform == TargetPlatform.iOS + ? CupertinoIcons.share + : Icons.share, + label: context.l10n.studyShareAndExport, + onPressed: () { + showAdaptiveActionSheet( + context: context, + actions: [ + BottomSheetAction( + makeLabel: (context) => Text(broadcast.title), + onPressed: (context) async { + launchShareDialog( + context, + uri: lichessUri( + '/broadcast/${broadcast.tour.slug}/${broadcast.tour.id}', + ), + ); + }, + ), + BottomSheetAction( + makeLabel: (context) => Text(broadcast.round.name), + onPressed: (context) async { + launchShareDialog( + context, + uri: lichessUri( + '/broadcast/${broadcast.tour.slug}/${broadcast.round.slug}/${broadcast.round.id}', + ), + ); + }, + ), + BottomSheetAction( + makeLabel: (context) => Text('${broadcast.round.name} PGN'), + onPressed: (context) async { + launchShareDialog( + context, + uri: lichessUri( + '/broadcast/${broadcast.tour.slug}/${broadcast.round.slug}/${broadcast.round.id}.pgn', + ), + ); + }, + ), + ], + ); + }, + ), + ], ), ], ), - if (broadcast.tour.information.players != null) - Text( - broadcast.tour.information.players!, - style: TextStyle(fontSize: 12, color: subTitleColor, letterSpacing: -0.2), - overflow: TextOverflow.ellipsis, - maxLines: 1, - ), - ], - ), + ), + ], ); } } @@ -629,7 +731,7 @@ class _BroadcastCardState extends State { onTapCancel: _onTapCancel, onTapUp: (_) => _onTapCancel(), child: AnimatedOpacity( - opacity: _tapDown ? 1.0 : 0.85, + opacity: _tapDown ? 1.0 : 0.95, duration: const Duration(milliseconds: 100), child: AnimatedContainer( duration: const Duration(milliseconds: 500), @@ -719,6 +821,7 @@ class BroadcastCarouselItem extends StatefulWidget { class _BroadcastCarouselItemState extends State { _CardColors? _cardColors; + bool _tapDown = false; String? get imageUrl => widget.broadcast.tour.imageUrl; @@ -758,6 +861,14 @@ class _BroadcastCarouselItemState extends State { } } + void _onTapDown() { + setState(() => _tapDown = true); + } + + void _onTapCancel() { + setState(() => _tapDown = false); + } + @override Widget build(BuildContext context) { final backgroundColor = _cardColors?.primaryContainer ?? Styles.cardColor(context); @@ -767,36 +878,56 @@ class _BroadcastCarouselItemState extends State { final flexWeights = widget.flexWeights; final totalFlex = flexWeights.reduce((a, b) => a + b); - return AnimatedContainer( - duration: const Duration(milliseconds: 500), - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration(color: backgroundColor), - child: OverflowBox( - maxWidth: width * flexWeights[0] / totalFlex - paddingWidth, - minWidth: width * flexWeights[0] / totalFlex - paddingWidth, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image( - image: imageProvider, - frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { - if (wasSynchronouslyLoaded) { - return child; - } - return AnimatedOpacity( - duration: const Duration(milliseconds: 500), - opacity: frame == null ? 0 : 1, - child: child, - ); - }, - errorBuilder: - (context, error, stackTrace) => const Image(image: kDefaultBroadcastImage), - ), - Expanded( - child: _BroadcastCardContent(broadcast: widget.broadcast, cardColors: _cardColors), + return GestureDetector( + onTap: () { + pushPlatformRoute( + context, + title: widget.broadcast.title, + rootNavigator: true, + builder: (context) => BroadcastRoundScreen(broadcast: widget.broadcast), + ); + }, + onTapDown: (_) => _onTapDown(), + onTapCancel: _onTapCancel, + onTapUp: (_) => _onTapCancel(), + child: AnimatedOpacity( + opacity: _tapDown ? 1.0 : 0.95, + duration: const Duration(milliseconds: 100), + child: AnimatedContainer( + duration: const Duration(milliseconds: 500), + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration(color: backgroundColor), + child: OverflowBox( + maxWidth: width * flexWeights[0] / totalFlex - paddingWidth, + minWidth: width * flexWeights[0] / totalFlex - paddingWidth, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image( + image: imageProvider, + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { + if (wasSynchronouslyLoaded) { + return child; + } + return AnimatedOpacity( + duration: const Duration(milliseconds: 500), + opacity: frame == null ? 0 : 1, + child: child, + ); + }, + errorBuilder: + (context, error, stackTrace) => const Image(image: kDefaultBroadcastImage), + ), + Expanded( + child: _BroadcastCardContent( + broadcast: widget.broadcast, + cardColors: _cardColors, + ), + ), + ], ), - ], + ), ), ), ); diff --git a/lib/src/view/broadcast/broadcast_round_screen.dart b/lib/src/view/broadcast/broadcast_round_screen.dart index 2856754f60..9af46aa883 100644 --- a/lib/src/view/broadcast/broadcast_round_screen.dart +++ b/lib/src/view/broadcast/broadcast_round_screen.dart @@ -20,20 +20,21 @@ import 'package:lichess_mobile/src/widgets/cupertino.dart'; import 'package:lichess_mobile/src/widgets/list.dart'; import 'package:lichess_mobile/src/widgets/platform.dart'; +enum BroadcastRoundTab { overview, boards, players } + class BroadcastRoundScreen extends ConsumerStatefulWidget { final Broadcast broadcast; + final BroadcastRoundTab? initialTab; - const BroadcastRoundScreen({required this.broadcast}); + const BroadcastRoundScreen({required this.broadcast, this.initialTab}); @override _BroadcastRoundScreenState createState() => _BroadcastRoundScreenState(); } -enum _CupertinoView { overview, boards, players } - class _BroadcastRoundScreenState extends ConsumerState with SingleTickerProviderStateMixin { - _CupertinoView selectedTab = _CupertinoView.overview; + BroadcastRoundTab selectedTab = BroadcastRoundTab.overview; late final TabController _tabController; late BroadcastTournamentId _selectedTournamentId; BroadcastRoundId? _selectedRoundId; @@ -43,7 +44,12 @@ class _BroadcastRoundScreenState extends ConsumerState @override void initState() { super.initState(); - _tabController = TabController(initialIndex: 0, length: 3, vsync: this); + selectedTab = widget.initialTab ?? BroadcastRoundTab.overview; + _tabController = TabController( + initialIndex: widget.initialTab?.index ?? 0, + length: 3, + vsync: this, + ); _selectedTournamentId = widget.broadcast.tour.id; _selectedRoundId = widget.broadcast.roundToLinkId; } @@ -54,7 +60,7 @@ class _BroadcastRoundScreenState extends ConsumerState super.dispose(); } - void setCupertinoTab(_CupertinoView mode) { + void setCupertinoTab(BroadcastRoundTab mode) { setState(() { selectedTab = mode; }); @@ -79,14 +85,14 @@ class _BroadcastRoundScreenState extends ConsumerState AsyncValue asyncTournament, AsyncValue asyncRound, ) { - final tabSwitcher = CupertinoSlidingSegmentedControl<_CupertinoView>( + final tabSwitcher = CupertinoSlidingSegmentedControl( groupValue: selectedTab, children: { - _CupertinoView.overview: Text(context.l10n.broadcastOverview), - _CupertinoView.boards: Text(context.l10n.broadcastBoards), - _CupertinoView.players: Text(context.l10n.players), + BroadcastRoundTab.overview: Text(context.l10n.broadcastOverview), + BroadcastRoundTab.boards: Text(context.l10n.broadcastBoards), + BroadcastRoundTab.players: Text(context.l10n.players), }, - onValueChanged: (_CupertinoView? view) { + onValueChanged: (BroadcastRoundTab? view) { if (view != null) { setCupertinoTab(view); } @@ -107,14 +113,14 @@ class _BroadcastRoundScreenState extends ConsumerState Expanded( child: switch (asyncRound) { AsyncData(value: final _) => switch (selectedTab) { - _CupertinoView.overview => _TabView( + BroadcastRoundTab.overview => _TabView( cupertinoTabSwitcher: tabSwitcher, sliver: BroadcastOverviewTab( broadcast: widget.broadcast, tournamentId: _selectedTournamentId, ), ), - _CupertinoView.boards => _TabView( + BroadcastRoundTab.boards => _TabView( cupertinoTabSwitcher: tabSwitcher, sliver: switch (asyncTournament) { AsyncData(:final value) => BroadcastBoardsTab( @@ -125,7 +131,7 @@ class _BroadcastRoundScreenState extends ConsumerState _ => const SliverFillRemaining(child: SizedBox.shrink()), }, ), - _CupertinoView.players => _TabView( + BroadcastRoundTab.players => _TabView( cupertinoTabSwitcher: tabSwitcher, sliver: BroadcastPlayersTab(tournamentId: _selectedTournamentId), ), @@ -224,13 +230,13 @@ class _BroadcastRoundScreenState extends ConsumerState ref.listen( broadcastRoundControllerProvider(_selectedRoundId ?? tournament.defaultRoundId), (_, round) { - if (round.hasValue && !roundLoaded) { + if (widget.initialTab == null && round.hasValue && !roundLoaded) { roundLoaded = true; if (round.value!.games.isNotEmpty) { _tabController.index = 1; if (Theme.of(context).platform == TargetPlatform.iOS) { - setCupertinoTab(_CupertinoView.boards); + setCupertinoTab(BroadcastRoundTab.boards); } } } diff --git a/lib/src/view/play/create_custom_game_screen.dart b/lib/src/view/play/create_custom_game_screen.dart index 137e28576e..0ad2e9f7f2 100644 --- a/lib/src/view/play/create_custom_game_screen.dart +++ b/lib/src/view/play/create_custom_game_screen.dart @@ -603,6 +603,7 @@ class _CreateGameBodyState extends ConsumerState<_CreateGameBody> { harmonizeCupertinoTitleStyle: true, title: Text(context.l10n.rated), trailing: Switch.adaptive( + applyCupertinoTheme: true, value: preferences.customRated, onChanged: (bool value) { ref.read(gameSetupPreferencesProvider.notifier).setCustomRated(value); diff --git a/lib/src/view/study/study_screen.dart b/lib/src/view/study/study_screen.dart index bcf75188ba..86a0cc3750 100644 --- a/lib/src/view/study/study_screen.dart +++ b/lib/src/view/study/study_screen.dart @@ -202,9 +202,19 @@ class _StudyMenu extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final state = ref.watch(studyControllerProvider(id)).requireValue; - return PlatformAppBarMenuButton( - semanticsLabel: 'Study menu', - icon: const Icon(Icons.more_horiz), + return PlatformContextMenuAnchor( + builder: + (context, controller, _) => AppBarIconButton( + onPressed: () { + if (controller.isOpen) { + controller.close(); + } else { + controller.open(); + } + }, + semanticsLabel: 'Study menu', + icon: const Icon(Icons.more_horiz), + ), actions: [ AppBarMenuAction( icon: Icons.settings, diff --git a/lib/src/widgets/buttons.dart b/lib/src/widgets/buttons.dart index c129185f4b..83db21ab9e 100644 --- a/lib/src/widgets/buttons.dart +++ b/lib/src/widgets/buttons.dart @@ -4,7 +4,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:popover/popover.dart'; /// Platform agnostic button which is used for important actions. /// @@ -464,105 +463,15 @@ class PlatformIconButton extends StatelessWidget { } } -const _kMenuWidth = 250.0; -const Color _kBorderColor = CupertinoDynamicColor.withBrightness( - color: Color(0xFFA9A9AF), - darkColor: Color(0xFF57585A), -); +/// A platform agnostic context menu anchor. +class PlatformContextMenuAnchor extends StatelessWidget { + const PlatformContextMenuAnchor({required this.actions, required this.builder, super.key}); -/// A platform agnostic menu button for the app bar. -class PlatformAppBarMenuButton extends StatelessWidget { - const PlatformAppBarMenuButton({ - required this.icon, - required this.semanticsLabel, - required this.actions, - super.key, - }); - - final Widget icon; - final String semanticsLabel; final List actions; + final MenuAnchorChildBuilder builder; @override Widget build(BuildContext context) { - if (Theme.of(context).platform == TargetPlatform.iOS) { - final menuActions = - actions.map((action) { - return CupertinoContextMenuAction( - onPressed: () { - if (action.dismissOnPress) { - Navigator.of(context).pop(); - } - action.onPressed(); - }, - trailingIcon: action.icon, - child: Text(action.label), - ); - }).toList(); - return AppBarIconButton( - onPressed: () { - showPopover( - context: context, - bodyBuilder: (context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: SizedBox( - width: _kMenuWidth, - child: IntrinsicHeight( - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(13.0)), - child: ColoredBox( - color: CupertinoDynamicColor.resolve( - CupertinoContextMenu.kBackgroundColor, - context, - ), - child: ScrollConfiguration( - behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false), - child: CupertinoScrollbar( - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - menuActions.first, - for (final Widget action in menuActions.skip(1)) - DecoratedBox( - decoration: BoxDecoration( - border: Border( - top: BorderSide( - color: CupertinoDynamicColor.resolve( - _kBorderColor, - context, - ), - width: 0.4, - ), - ), - ), - position: DecorationPosition.foreground, - child: action, - ), - ], - ), - ), - ), - ), - ), - ), - ), - ), - ); - }, - arrowWidth: 0.0, - arrowHeight: 0.0, - direction: PopoverDirection.top, - width: _kMenuWidth, - backgroundColor: Colors.transparent, - ); - }, - semanticsLabel: semanticsLabel, - icon: icon, - ); - } - return MenuAnchor( menuChildren: actions.map((action) { @@ -574,19 +483,7 @@ class PlatformAppBarMenuButton extends StatelessWidget { child: Text(action.label), ); }).toList(), - builder: (BuildContext context, MenuController controller, Widget? child) { - return AppBarIconButton( - onPressed: () { - if (controller.isOpen) { - controller.close(); - } else { - controller.open(); - } - }, - semanticsLabel: semanticsLabel, - icon: icon, - ); - }, + builder: builder, ); } }