From abefa4864f7400c9bfc8dfb94e383593210c1ac7 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 20:06:12 -0300 Subject: [PATCH 01/16] fix: Secondary Menu preferred placement mode is bottom right --- lib/src/controls/surfaces/commandbar.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/controls/surfaces/commandbar.dart b/lib/src/controls/surfaces/commandbar.dart index 60bffae86..9cae3b498 100644 --- a/lib/src/controls/surfaces/commandbar.dart +++ b/lib/src/controls/surfaces/commandbar.dart @@ -245,7 +245,7 @@ class _CommandBarState extends State { void showSecondaryMenu() { secondaryFlyoutController.showFlyout( autoModeConfiguration: FlyoutAutoConfiguration( - preferredMode: FlyoutPlacementMode.topRight.resolve( + preferredMode: FlyoutPlacementMode.bottomRight.resolve( Directionality.of(context), ), ), From 4352079880d2764a3a3c645fe9d7bdc74624ba73 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 20:27:41 -0300 Subject: [PATCH 02/16] feat: build target on showFlyout --- lib/src/controls/flyouts/flyout.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/src/controls/flyouts/flyout.dart b/lib/src/controls/flyouts/flyout.dart index 4387a5105..db7b6ea0c 100644 --- a/lib/src/controls/flyouts/flyout.dart +++ b/lib/src/controls/flyouts/flyout.dart @@ -536,6 +536,11 @@ class FlyoutController with ChangeNotifier { /// barrier. It's useful when the flyout is used as a context menu and the /// barrier should be dismissed when the user clicks outside of the flyout. /// If this is provided, [barrierDismissible] is ignored. + /// + /// [buildTarget] is a flag that determines whether the target should be built + /// or not. This helps when the target needs to be tappable, like the + /// [CommandBar] or [MenuBar] widgets. Any context dependencies of the target + /// must be available globally. Defaults to false. Future showFlyout({ required WidgetBuilder builder, bool barrierDismissible = true, @@ -554,6 +559,7 @@ class FlyoutController with ChangeNotifier { Offset? position, RouteSettings? settings, GestureRecognizer? barrierRecognizer, + bool buildTarget = false, }) async { _ensureAttached(); assert(_attachState!.mounted); @@ -657,6 +663,11 @@ class FlyoutController with ChangeNotifier { child: barrier, ), ), + if (buildTarget) + Positioned.fromRect( + rect: targetRect, + child: _attachState!.build(context), + ), Positioned.fill( child: SafeArea( child: CustomSingleChildLayout( From d9491f14cdf5b11f3b87e5019c8e4abbc9d1289f Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 20:28:10 -0300 Subject: [PATCH 03/16] feat: Make command bar primary items available on secondary flyout --- lib/src/controls/surfaces/commandbar.dart | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/src/controls/surfaces/commandbar.dart b/lib/src/controls/surfaces/commandbar.dart index 9cae3b498..4a8a56860 100644 --- a/lib/src/controls/surfaces/commandbar.dart +++ b/lib/src/controls/surfaces/commandbar.dart @@ -242,8 +242,14 @@ class _CommandBarState extends State { ...widget.secondaryItems, ]; - void showSecondaryMenu() { + void toggleSecondaryMenu() { + if (secondaryFlyoutController.isOpen) { + Navigator.of(context).pop(); + return; + } + secondaryFlyoutController.showFlyout( + buildTarget: true, autoModeConfiguration: FlyoutAutoConfiguration( preferredMode: FlyoutPlacementMode.bottomRight.resolve( Directionality.of(context), @@ -269,10 +275,10 @@ class _CommandBarState extends State { late CommandBarItem overflowItem; if (widget.overflowItemBuilder != null) { - overflowItem = widget.overflowItemBuilder!(showSecondaryMenu); + overflowItem = widget.overflowItemBuilder!(toggleSecondaryMenu); } else { overflowItem = CommandBarButton( - onPressed: showSecondaryMenu, + onPressed: toggleSecondaryMenu, icon: const Icon(FluentIcons.more), ); } @@ -282,10 +288,7 @@ class _CommandBarState extends State { allSecondaryItems.first is CommandBarSeparator) { allSecondaryItems.removeAt(0); } - overflowWidget = FlyoutTarget( - controller: secondaryFlyoutController, - child: overflowItem.build(context, primaryMode), - ); + overflowWidget = overflowItem.build(context, primaryMode); } var listBuilder = @@ -374,7 +377,7 @@ class _CommandBarState extends State { if (widget._isExpanded) { w = listBuilder.call(children: [Expanded(child: w)]); } - return w; + return FlyoutTarget(controller: secondaryFlyoutController, child: w); } @override From a67b30c4a735c44c23f9ef401d563487a24d84d4 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 20:38:52 -0300 Subject: [PATCH 04/16] fix: Show more/less tooltip on more/less button --- lib/l10n/generated/fluent_localizations.dart | 12 ++ .../generated/fluent_localizations_ar.dart | 6 + .../generated/fluent_localizations_be.dart | 6 + .../generated/fluent_localizations_bn.dart | 6 + .../generated/fluent_localizations_ca.dart | 6 + .../generated/fluent_localizations_cs.dart | 6 + .../generated/fluent_localizations_de.dart | 6 + .../generated/fluent_localizations_el.dart | 6 + .../generated/fluent_localizations_en.dart | 6 + .../generated/fluent_localizations_es.dart | 6 + .../generated/fluent_localizations_fa.dart | 6 + .../generated/fluent_localizations_fr.dart | 6 + .../generated/fluent_localizations_he.dart | 6 + .../generated/fluent_localizations_hi.dart | 6 + .../generated/fluent_localizations_hr.dart | 6 + .../generated/fluent_localizations_hu.dart | 6 + .../generated/fluent_localizations_id.dart | 6 + .../generated/fluent_localizations_it.dart | 6 + .../generated/fluent_localizations_ja.dart | 6 + .../generated/fluent_localizations_ko.dart | 6 + .../generated/fluent_localizations_ku.dart | 6 + .../generated/fluent_localizations_ms.dart | 6 + .../generated/fluent_localizations_my.dart | 6 + .../generated/fluent_localizations_nl.dart | 6 + .../generated/fluent_localizations_pl.dart | 6 + .../generated/fluent_localizations_pt.dart | 6 + .../generated/fluent_localizations_ro.dart | 6 + .../generated/fluent_localizations_ru.dart | 6 + .../generated/fluent_localizations_sk.dart | 6 + .../generated/fluent_localizations_sv.dart | 6 + .../generated/fluent_localizations_ta.dart | 6 + .../generated/fluent_localizations_th.dart | 6 + .../generated/fluent_localizations_tr.dart | 6 + .../generated/fluent_localizations_uk.dart | 6 + .../generated/fluent_localizations_ur.dart | 6 + .../generated/fluent_localizations_uz.dart | 6 + .../generated/fluent_localizations_vi.dart | 6 + .../generated/fluent_localizations_zh.dart | 6 + lib/l10n/intl_en.arb | 8 + lib/l10n/intl_pt.arb | 2 + lib/l10n/untranslated.json | 164 +++++++++++++++++- lib/src/controls/surfaces/commandbar.dart | 30 +++- 42 files changed, 430 insertions(+), 8 deletions(-) diff --git a/lib/l10n/generated/fluent_localizations.dart b/lib/l10n/generated/fluent_localizations.dart index 704ca318b..5970ff07c 100644 --- a/lib/l10n/generated/fluent_localizations.dart +++ b/lib/l10n/generated/fluent_localizations.dart @@ -355,6 +355,18 @@ abstract class FluentLocalizations { /// **'year'** String get year; + /// The text used by [CommandBar] to show more items. + /// + /// In en, this message translates to: + /// **'See more'** + String get seeMore; + + /// The text used by [CommandBar] to show less items. + /// + /// In en, this message translates to: + /// **'See less'** + String get seeLess; + /// Label for red color component in color picker /// /// In en, this message translates to: diff --git a/lib/l10n/generated/fluent_localizations_ar.dart b/lib/l10n/generated/fluent_localizations_ar.dart index edc2ad769..9959f4223 100644 --- a/lib/l10n/generated/fluent_localizations_ar.dart +++ b/lib/l10n/generated/fluent_localizations_ar.dart @@ -99,6 +99,12 @@ class FluentLocalizationsAr extends FluentLocalizations { @override String get year => 'سنة'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'أحمر'; diff --git a/lib/l10n/generated/fluent_localizations_be.dart b/lib/l10n/generated/fluent_localizations_be.dart index fb49805f2..7505b01b8 100644 --- a/lib/l10n/generated/fluent_localizations_be.dart +++ b/lib/l10n/generated/fluent_localizations_be.dart @@ -99,6 +99,12 @@ class FluentLocalizationsBe extends FluentLocalizations { @override String get year => 'год'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Чырвоны'; diff --git a/lib/l10n/generated/fluent_localizations_bn.dart b/lib/l10n/generated/fluent_localizations_bn.dart index 822b5278c..25fb71818 100644 --- a/lib/l10n/generated/fluent_localizations_bn.dart +++ b/lib/l10n/generated/fluent_localizations_bn.dart @@ -100,6 +100,12 @@ class FluentLocalizationsBn extends FluentLocalizations { @override String get year => 'বছর'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'লাল'; diff --git a/lib/l10n/generated/fluent_localizations_ca.dart b/lib/l10n/generated/fluent_localizations_ca.dart index 93f8a40ea..0dd820911 100644 --- a/lib/l10n/generated/fluent_localizations_ca.dart +++ b/lib/l10n/generated/fluent_localizations_ca.dart @@ -102,6 +102,12 @@ class FluentLocalizationsCa extends FluentLocalizations { @override String get year => 'any'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Vermell'; diff --git a/lib/l10n/generated/fluent_localizations_cs.dart b/lib/l10n/generated/fluent_localizations_cs.dart index 4ed53feb2..34afd1b92 100644 --- a/lib/l10n/generated/fluent_localizations_cs.dart +++ b/lib/l10n/generated/fluent_localizations_cs.dart @@ -100,6 +100,12 @@ class FluentLocalizationsCs extends FluentLocalizations { @override String get year => 'rok'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Červená'; diff --git a/lib/l10n/generated/fluent_localizations_de.dart b/lib/l10n/generated/fluent_localizations_de.dart index 516fb75e3..0e7d665d8 100644 --- a/lib/l10n/generated/fluent_localizations_de.dart +++ b/lib/l10n/generated/fluent_localizations_de.dart @@ -102,6 +102,12 @@ class FluentLocalizationsDe extends FluentLocalizations { @override String get year => 'Jahr'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Rot'; diff --git a/lib/l10n/generated/fluent_localizations_el.dart b/lib/l10n/generated/fluent_localizations_el.dart index 0bf9614c0..e4434991b 100644 --- a/lib/l10n/generated/fluent_localizations_el.dart +++ b/lib/l10n/generated/fluent_localizations_el.dart @@ -102,6 +102,12 @@ class FluentLocalizationsEl extends FluentLocalizations { @override String get year => 'έτος'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Κόκκινο'; diff --git a/lib/l10n/generated/fluent_localizations_en.dart b/lib/l10n/generated/fluent_localizations_en.dart index e4e42188e..9d938249a 100644 --- a/lib/l10n/generated/fluent_localizations_en.dart +++ b/lib/l10n/generated/fluent_localizations_en.dart @@ -101,6 +101,12 @@ class FluentLocalizationsEn extends FluentLocalizations { @override String get year => 'year'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Red'; diff --git a/lib/l10n/generated/fluent_localizations_es.dart b/lib/l10n/generated/fluent_localizations_es.dart index 8c4624476..052b55083 100644 --- a/lib/l10n/generated/fluent_localizations_es.dart +++ b/lib/l10n/generated/fluent_localizations_es.dart @@ -102,6 +102,12 @@ class FluentLocalizationsEs extends FluentLocalizations { @override String get year => 'año'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Rojo'; diff --git a/lib/l10n/generated/fluent_localizations_fa.dart b/lib/l10n/generated/fluent_localizations_fa.dart index 4a723b166..ef4973bfd 100644 --- a/lib/l10n/generated/fluent_localizations_fa.dart +++ b/lib/l10n/generated/fluent_localizations_fa.dart @@ -100,6 +100,12 @@ class FluentLocalizationsFa extends FluentLocalizations { @override String get year => 'سال'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'قرمز'; diff --git a/lib/l10n/generated/fluent_localizations_fr.dart b/lib/l10n/generated/fluent_localizations_fr.dart index 11421a613..b44be6b88 100644 --- a/lib/l10n/generated/fluent_localizations_fr.dart +++ b/lib/l10n/generated/fluent_localizations_fr.dart @@ -102,6 +102,12 @@ class FluentLocalizationsFr extends FluentLocalizations { @override String get year => 'année'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Rouge'; diff --git a/lib/l10n/generated/fluent_localizations_he.dart b/lib/l10n/generated/fluent_localizations_he.dart index dcea2d661..6d8fb214b 100644 --- a/lib/l10n/generated/fluent_localizations_he.dart +++ b/lib/l10n/generated/fluent_localizations_he.dart @@ -99,6 +99,12 @@ class FluentLocalizationsHe extends FluentLocalizations { @override String get year => 'שנה'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'אדום'; diff --git a/lib/l10n/generated/fluent_localizations_hi.dart b/lib/l10n/generated/fluent_localizations_hi.dart index 86ef78bbd..c36e57733 100644 --- a/lib/l10n/generated/fluent_localizations_hi.dart +++ b/lib/l10n/generated/fluent_localizations_hi.dart @@ -101,6 +101,12 @@ class FluentLocalizationsHi extends FluentLocalizations { @override String get year => 'साल'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'लाल'; diff --git a/lib/l10n/generated/fluent_localizations_hr.dart b/lib/l10n/generated/fluent_localizations_hr.dart index f76c2f1f5..d2742938c 100644 --- a/lib/l10n/generated/fluent_localizations_hr.dart +++ b/lib/l10n/generated/fluent_localizations_hr.dart @@ -101,6 +101,12 @@ class FluentLocalizationsHr extends FluentLocalizations { @override String get year => 'godina'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Crvena'; diff --git a/lib/l10n/generated/fluent_localizations_hu.dart b/lib/l10n/generated/fluent_localizations_hu.dart index df5a43e59..b534670f7 100644 --- a/lib/l10n/generated/fluent_localizations_hu.dart +++ b/lib/l10n/generated/fluent_localizations_hu.dart @@ -100,6 +100,12 @@ class FluentLocalizationsHu extends FluentLocalizations { @override String get year => 'év'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Vörös'; diff --git a/lib/l10n/generated/fluent_localizations_id.dart b/lib/l10n/generated/fluent_localizations_id.dart index bff6b46cf..7b679dcc9 100644 --- a/lib/l10n/generated/fluent_localizations_id.dart +++ b/lib/l10n/generated/fluent_localizations_id.dart @@ -101,6 +101,12 @@ class FluentLocalizationsId extends FluentLocalizations { @override String get year => 'tahun'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Merah'; diff --git a/lib/l10n/generated/fluent_localizations_it.dart b/lib/l10n/generated/fluent_localizations_it.dart index fe97fcac2..e85e92a8e 100644 --- a/lib/l10n/generated/fluent_localizations_it.dart +++ b/lib/l10n/generated/fluent_localizations_it.dart @@ -99,6 +99,12 @@ class FluentLocalizationsIt extends FluentLocalizations { @override String get year => 'anno'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Rosso'; diff --git a/lib/l10n/generated/fluent_localizations_ja.dart b/lib/l10n/generated/fluent_localizations_ja.dart index 5e4cbbaaa..3423e8cb8 100644 --- a/lib/l10n/generated/fluent_localizations_ja.dart +++ b/lib/l10n/generated/fluent_localizations_ja.dart @@ -99,6 +99,12 @@ class FluentLocalizationsJa extends FluentLocalizations { @override String get year => '年'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => '赤'; diff --git a/lib/l10n/generated/fluent_localizations_ko.dart b/lib/l10n/generated/fluent_localizations_ko.dart index f8295c93a..4757bbc41 100644 --- a/lib/l10n/generated/fluent_localizations_ko.dart +++ b/lib/l10n/generated/fluent_localizations_ko.dart @@ -99,6 +99,12 @@ class FluentLocalizationsKo extends FluentLocalizations { @override String get year => '년'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => '빨강'; diff --git a/lib/l10n/generated/fluent_localizations_ku.dart b/lib/l10n/generated/fluent_localizations_ku.dart index ab1e8666d..fe54f6246 100644 --- a/lib/l10n/generated/fluent_localizations_ku.dart +++ b/lib/l10n/generated/fluent_localizations_ku.dart @@ -101,6 +101,12 @@ class FluentLocalizationsKu extends FluentLocalizations { @override String get year => 'ساڵ'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'سوور'; diff --git a/lib/l10n/generated/fluent_localizations_ms.dart b/lib/l10n/generated/fluent_localizations_ms.dart index fea06bdcf..44dc67408 100644 --- a/lib/l10n/generated/fluent_localizations_ms.dart +++ b/lib/l10n/generated/fluent_localizations_ms.dart @@ -101,6 +101,12 @@ class FluentLocalizationsMs extends FluentLocalizations { @override String get year => 'year'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Merah'; diff --git a/lib/l10n/generated/fluent_localizations_my.dart b/lib/l10n/generated/fluent_localizations_my.dart index 35da3607f..adf1d51f0 100644 --- a/lib/l10n/generated/fluent_localizations_my.dart +++ b/lib/l10n/generated/fluent_localizations_my.dart @@ -99,6 +99,12 @@ class FluentLocalizationsMy extends FluentLocalizations { @override String get year => 'နှစ်'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'အနီ'; diff --git a/lib/l10n/generated/fluent_localizations_nl.dart b/lib/l10n/generated/fluent_localizations_nl.dart index 39d1cda67..8fda61686 100644 --- a/lib/l10n/generated/fluent_localizations_nl.dart +++ b/lib/l10n/generated/fluent_localizations_nl.dart @@ -101,6 +101,12 @@ class FluentLocalizationsNl extends FluentLocalizations { @override String get year => 'jaar'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Rood'; diff --git a/lib/l10n/generated/fluent_localizations_pl.dart b/lib/l10n/generated/fluent_localizations_pl.dart index 3fde2191b..153bc3a01 100644 --- a/lib/l10n/generated/fluent_localizations_pl.dart +++ b/lib/l10n/generated/fluent_localizations_pl.dart @@ -99,6 +99,12 @@ class FluentLocalizationsPl extends FluentLocalizations { @override String get year => 'year'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Czerwony'; diff --git a/lib/l10n/generated/fluent_localizations_pt.dart b/lib/l10n/generated/fluent_localizations_pt.dart index bf3cab755..7e03f0caf 100644 --- a/lib/l10n/generated/fluent_localizations_pt.dart +++ b/lib/l10n/generated/fluent_localizations_pt.dart @@ -102,6 +102,12 @@ class FluentLocalizationsPt extends FluentLocalizations { @override String get year => 'ano'; + @override + String get seeMore => 'Mostrar mais'; + + @override + String get seeLess => 'Mostrar menos'; + @override String get redLabel => 'Vermelho'; diff --git a/lib/l10n/generated/fluent_localizations_ro.dart b/lib/l10n/generated/fluent_localizations_ro.dart index 3d30ea481..b8e16c939 100644 --- a/lib/l10n/generated/fluent_localizations_ro.dart +++ b/lib/l10n/generated/fluent_localizations_ro.dart @@ -101,6 +101,12 @@ class FluentLocalizationsRo extends FluentLocalizations { @override String get year => 'an'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Roșu'; diff --git a/lib/l10n/generated/fluent_localizations_ru.dart b/lib/l10n/generated/fluent_localizations_ru.dart index e1fceb065..c9b6de881 100644 --- a/lib/l10n/generated/fluent_localizations_ru.dart +++ b/lib/l10n/generated/fluent_localizations_ru.dart @@ -99,6 +99,12 @@ class FluentLocalizationsRu extends FluentLocalizations { @override String get year => 'year'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Красный'; diff --git a/lib/l10n/generated/fluent_localizations_sk.dart b/lib/l10n/generated/fluent_localizations_sk.dart index a94ddf457..bd49fc08e 100644 --- a/lib/l10n/generated/fluent_localizations_sk.dart +++ b/lib/l10n/generated/fluent_localizations_sk.dart @@ -101,6 +101,12 @@ class FluentLocalizationsSk extends FluentLocalizations { @override String get year => 'rok'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Červená'; diff --git a/lib/l10n/generated/fluent_localizations_sv.dart b/lib/l10n/generated/fluent_localizations_sv.dart index b2e2f58ff..2a00c603b 100644 --- a/lib/l10n/generated/fluent_localizations_sv.dart +++ b/lib/l10n/generated/fluent_localizations_sv.dart @@ -99,6 +99,12 @@ class FluentLocalizationsSv extends FluentLocalizations { @override String get year => 'år'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Röd'; diff --git a/lib/l10n/generated/fluent_localizations_ta.dart b/lib/l10n/generated/fluent_localizations_ta.dart index a029ccf4d..d4162311c 100644 --- a/lib/l10n/generated/fluent_localizations_ta.dart +++ b/lib/l10n/generated/fluent_localizations_ta.dart @@ -103,6 +103,12 @@ class FluentLocalizationsTa extends FluentLocalizations { @override String get year => 'ஆண்டு'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'சிவப்பு'; diff --git a/lib/l10n/generated/fluent_localizations_th.dart b/lib/l10n/generated/fluent_localizations_th.dart index 99a526ce2..1ba557caf 100644 --- a/lib/l10n/generated/fluent_localizations_th.dart +++ b/lib/l10n/generated/fluent_localizations_th.dart @@ -99,6 +99,12 @@ class FluentLocalizationsTh extends FluentLocalizations { @override String get year => 'ปี'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'แดง'; diff --git a/lib/l10n/generated/fluent_localizations_tr.dart b/lib/l10n/generated/fluent_localizations_tr.dart index f01398755..4714a2daf 100644 --- a/lib/l10n/generated/fluent_localizations_tr.dart +++ b/lib/l10n/generated/fluent_localizations_tr.dart @@ -99,6 +99,12 @@ class FluentLocalizationsTr extends FluentLocalizations { @override String get year => 'yıl'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Kırmızı'; diff --git a/lib/l10n/generated/fluent_localizations_uk.dart b/lib/l10n/generated/fluent_localizations_uk.dart index c7796fc79..2518a9861 100644 --- a/lib/l10n/generated/fluent_localizations_uk.dart +++ b/lib/l10n/generated/fluent_localizations_uk.dart @@ -99,6 +99,12 @@ class FluentLocalizationsUk extends FluentLocalizations { @override String get year => 'рік'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Червоний'; diff --git a/lib/l10n/generated/fluent_localizations_ur.dart b/lib/l10n/generated/fluent_localizations_ur.dart index 9723e6c43..beb21bbcb 100644 --- a/lib/l10n/generated/fluent_localizations_ur.dart +++ b/lib/l10n/generated/fluent_localizations_ur.dart @@ -102,6 +102,12 @@ class FluentLocalizationsUr extends FluentLocalizations { @override String get year => 'سال'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'سرخ'; diff --git a/lib/l10n/generated/fluent_localizations_uz.dart b/lib/l10n/generated/fluent_localizations_uz.dart index 3760ad102..8092d7172 100644 --- a/lib/l10n/generated/fluent_localizations_uz.dart +++ b/lib/l10n/generated/fluent_localizations_uz.dart @@ -100,6 +100,12 @@ class FluentLocalizationsUz extends FluentLocalizations { @override String get year => 'yil'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Qizil'; diff --git a/lib/l10n/generated/fluent_localizations_vi.dart b/lib/l10n/generated/fluent_localizations_vi.dart index b747f21b7..a5baecf1b 100644 --- a/lib/l10n/generated/fluent_localizations_vi.dart +++ b/lib/l10n/generated/fluent_localizations_vi.dart @@ -100,6 +100,12 @@ class FluentLocalizationsVi extends FluentLocalizations { @override String get year => 'năm'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => 'Đỏ'; diff --git a/lib/l10n/generated/fluent_localizations_zh.dart b/lib/l10n/generated/fluent_localizations_zh.dart index f30dfd073..fb8f9cc08 100644 --- a/lib/l10n/generated/fluent_localizations_zh.dart +++ b/lib/l10n/generated/fluent_localizations_zh.dart @@ -99,6 +99,12 @@ class FluentLocalizationsZh extends FluentLocalizations { @override String get year => '年'; + @override + String get seeMore => 'See more'; + + @override + String get seeLess => 'See less'; + @override String get redLabel => '红色'; diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 826b9551b..735a1ca13 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -124,6 +124,14 @@ "@year": { "description": "The text used by [DatePicker] for the year field" }, + "seeMore": "See more", + "@seeMore": { + "description": "The text used by [CommandBar] to show more items." + }, + "seeLess": "See less", + "@seeLess": { + "description": "The text used by [CommandBar] to show less items." + }, "redLabel": "Red", "@redLabel": { "description": "Label for red color component in color picker" diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 0ffde4adf..9fc1f6254 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -31,6 +31,8 @@ "month": "mês", "day": "dia", "year": "ano", + "seeMore": "Mostrar mais", + "seeLess": "Mostrar menos", "redLabel": "Vermelho", "greenLabel": "Verde", "blueLabel": "Azul", diff --git a/lib/l10n/untranslated.json b/lib/l10n/untranslated.json index 7187dadd0..046bba50c 100644 --- a/lib/l10n/untranslated.json +++ b/lib/l10n/untranslated.json @@ -1,7 +1,14 @@ { + "ar": [ + "seeMore", + "seeLess" + ], + "be": [ "am", "pm", + "seeMore", + "seeLess", "colorBlack", "colorNavy", "colorDarkBlue", @@ -142,7 +149,89 @@ "colorWhite" ], + "bn": [ + "seeMore", + "seeLess" + ], + + "ca": [ + "seeMore", + "seeLess" + ], + + "cs": [ + "seeMore", + "seeLess" + ], + + "de": [ + "seeMore", + "seeLess" + ], + + "el": [ + "seeMore", + "seeLess" + ], + + "es": [ + "seeMore", + "seeLess" + ], + + "fa": [ + "seeMore", + "seeLess" + ], + + "fr": [ + "seeMore", + "seeLess" + ], + + "he": [ + "seeMore", + "seeLess" + ], + + "hi": [ + "seeMore", + "seeLess" + ], + + "hr": [ + "seeMore", + "seeLess" + ], + + "hu": [ + "seeMore", + "seeLess" + ], + + "id": [ + "seeMore", + "seeLess" + ], + + "it": [ + "seeMore", + "seeLess" + ], + + "ja": [ + "seeMore", + "seeLess" + ], + + "ko": [ + "seeMore", + "seeLess" + ], + "ku": [ + "seeMore", + "seeLess", "colorBlack", "colorNavy", "colorDarkBlue", @@ -290,12 +379,16 @@ "pm", "month", "day", - "year" + "year", + "seeMore", + "seeLess" ], "my": [ "am", "pm", + "seeMore", + "seeLess", "colorBlack", "colorNavy", "colorDarkBlue", @@ -436,6 +529,11 @@ "colorWhite" ], + "nl": [ + "seeMore", + "seeLess" + ], + "pl": [ "hour", "minute", @@ -443,7 +541,14 @@ "pm", "month", "day", - "year" + "year", + "seeMore", + "seeLess" + ], + + "ro": [ + "seeMore", + "seeLess" ], "ru": [ @@ -453,17 +558,53 @@ "pm", "month", "day", - "year" + "year", + "seeMore", + "seeLess" + ], + + "sk": [ + "seeMore", + "seeLess" + ], + + "sv": [ + "seeMore", + "seeLess" + ], + + "ta": [ + "seeMore", + "seeLess" + ], + + "th": [ + "seeMore", + "seeLess" + ], + + "tr": [ + "seeMore", + "seeLess" ], "uk": [ "am", - "pm" + "pm", + "seeMore", + "seeLess" + ], + + "ur": [ + "seeMore", + "seeLess" ], "uz": [ "am", "pm", + "seeMore", + "seeLess", "colorBlack", "colorNavy", "colorDarkBlue", @@ -602,5 +743,20 @@ "colorLightYellow", "colorIvory", "colorWhite" + ], + + "vi": [ + "seeMore", + "seeLess" + ], + + "zh": [ + "seeMore", + "seeLess" + ], + + "zh_Hant": [ + "seeMore", + "seeLess" ] } diff --git a/lib/src/controls/surfaces/commandbar.dart b/lib/src/controls/surfaces/commandbar.dart index 4a8a56860..9207fa59c 100644 --- a/lib/src/controls/surfaces/commandbar.dart +++ b/lib/src/controls/surfaces/commandbar.dart @@ -229,7 +229,9 @@ class _CommandBarState extends State { } Widget _buildForPrimaryMode( - BuildContext context, CommandBarItemDisplayMode primaryMode) { + BuildContext context, + CommandBarItemDisplayMode primaryMode, + ) { final builtItems = widget.primaryItems.map((item) => item.build(context, primaryMode)); Widget? overflowWidget; @@ -242,13 +244,14 @@ class _CommandBarState extends State { ...widget.secondaryItems, ]; - void toggleSecondaryMenu() { + Future toggleSecondaryMenu() async { if (secondaryFlyoutController.isOpen) { Navigator.of(context).pop(); + if (mounted) setState(() {}); return; } - secondaryFlyoutController.showFlyout( + final future = secondaryFlyoutController.showFlyout( buildTarget: true, autoModeConfiguration: FlyoutAutoConfiguration( preferredMode: FlyoutPlacementMode.bottomRight.resolve( @@ -271,6 +274,11 @@ class _CommandBarState extends State { ); }, ); + // Update the widget to update the tooltip of the default overflow item + if (mounted) setState(() {}); + + await future; + if (mounted) setState(() {}); } late CommandBarItem overflowItem; @@ -279,6 +287,9 @@ class _CommandBarState extends State { } else { overflowItem = CommandBarButton( onPressed: toggleSecondaryMenu, + tooltip: secondaryFlyoutController.isOpen + ? FluentLocalizations.of(context).seeLess + : FluentLocalizations.of(context).seeMore, icon: const Icon(FluentIcons.more), ); } @@ -524,6 +535,8 @@ class CommandBarButton extends CommandBarItem { /// {@macro flutter.widgets.Focus.autofocus} final bool autofocus; + final String? tooltip; + /// Creates a command bar button const CommandBarButton({ super.key, @@ -535,6 +548,7 @@ class CommandBarButton extends CommandBarItem { this.onLongPress, this.focusNode, this.autofocus = false, + this.tooltip, }); @override @@ -546,7 +560,7 @@ class CommandBarButton extends CommandBarItem { final showIcon = icon != null; final showLabel = label != null && (displayMode == CommandBarItemDisplayMode.inPrimary || !showIcon); - return IconButton( + final button = IconButton( key: key, onPressed: onPressed, onLongPress: onLongPress, @@ -572,6 +586,13 @@ class CommandBarButton extends CommandBarItem { if (showLabel) label!, ]), ); + if (tooltip != null) { + return Tooltip( + message: tooltip!, + child: button, + ); + } + return button; case CommandBarItemDisplayMode.inSecondary: return Padding( padding: const EdgeInsetsDirectional.only(end: 8.0, start: 8.0), @@ -582,6 +603,7 @@ class CommandBarButton extends CommandBarItem { autofocus: autofocus, icon: icon, text: label ?? const SizedBox.shrink(), + tooltip: tooltip, ), ); } From b0754eb19e1507079aabe7d3e85463a5ef120b59 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 20:44:21 -0300 Subject: [PATCH 05/16] feat: FlyoutController.close --- lib/src/controls/flyouts/flyout.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/src/controls/flyouts/flyout.dart b/lib/src/controls/flyouts/flyout.dart index db7b6ea0c..de15f8d0b 100644 --- a/lib/src/controls/flyouts/flyout.dart +++ b/lib/src/controls/flyouts/flyout.dart @@ -788,6 +788,12 @@ class FlyoutController with ChangeNotifier { return result; } + + void close() { + _ensureAttached(); + assert(_open); + Navigator.of(_attachState!.context).pop(); + } } class _DismissAction extends DismissAction { From c91e46ce15f03d2dfc68ef9175dbe9f41219924a Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 20:44:54 -0300 Subject: [PATCH 06/16] feat: Make CommandBar state public --- lib/src/controls/surfaces/commandbar.dart | 102 ++++++++++++---------- 1 file changed, 54 insertions(+), 48 deletions(-) diff --git a/lib/src/controls/surfaces/commandbar.dart b/lib/src/controls/surfaces/commandbar.dart index 9207fa59c..f5f002efc 100644 --- a/lib/src/controls/surfaces/commandbar.dart +++ b/lib/src/controls/surfaces/commandbar.dart @@ -182,12 +182,57 @@ class CommandBar extends StatefulWidget { : CrossAxisAlignment.center); @override - State createState() => _CommandBarState(); + State createState() => CommandBarState(); } -class _CommandBarState extends State { +class CommandBarState extends State { final secondaryFlyoutController = FlyoutController(); - List dynamicallyHiddenPrimaryItems = []; + List _dynamicallyHiddenPrimaryItems = []; + + List get allSecondaryItems { + return [ + ..._dynamicallyHiddenPrimaryItems + .map((index) => widget.primaryItems[index]), + ...widget.secondaryItems, + ]; + } + + Future toggleSecondaryMenu() async { + if (secondaryFlyoutController.isOpen) { + secondaryFlyoutController.close(); + if (mounted) setState(() {}); + return; + } + + final future = secondaryFlyoutController.showFlyout( + buildTarget: true, + autoModeConfiguration: FlyoutAutoConfiguration( + preferredMode: FlyoutPlacementMode.bottomRight.resolve( + Directionality.of(context), + ), + ), + builder: (context) { + return FlyoutContent( + constraints: const BoxConstraints(maxWidth: 200.0), + padding: const EdgeInsetsDirectional.only(top: 8.0), + child: ListView( + shrinkWrap: true, + children: allSecondaryItems.map((item) { + return item.build( + context, + CommandBarItemDisplayMode.inSecondary, + ); + }).toList(), + ), + ); + }, + ); + // Update the widget to update the tooltip of the default overflow item + if (mounted) setState(() {}); + + await future; + if (mounted) setState(() {}); + } @override void dispose() { @@ -238,49 +283,6 @@ class _CommandBarState extends State { if (widget.secondaryItems.isNotEmpty || widget.overflowBehavior == CommandBarOverflowBehavior.dynamicOverflow) { - var allSecondaryItems = [ - ...dynamicallyHiddenPrimaryItems - .map((index) => widget.primaryItems[index]), - ...widget.secondaryItems, - ]; - - Future toggleSecondaryMenu() async { - if (secondaryFlyoutController.isOpen) { - Navigator.of(context).pop(); - if (mounted) setState(() {}); - return; - } - - final future = secondaryFlyoutController.showFlyout( - buildTarget: true, - autoModeConfiguration: FlyoutAutoConfiguration( - preferredMode: FlyoutPlacementMode.bottomRight.resolve( - Directionality.of(context), - ), - ), - builder: (context) { - return FlyoutContent( - constraints: const BoxConstraints(maxWidth: 200.0), - padding: const EdgeInsetsDirectional.only(top: 8.0), - child: ListView( - shrinkWrap: true, - children: allSecondaryItems.map((item) { - return item.build( - context, - CommandBarItemDisplayMode.inSecondary, - ); - }).toList(), - ), - ); - }, - ); - // Update the widget to update the tooltip of the default overflow item - if (mounted) setState(() {}); - - await future; - if (mounted) setState(() {}); - } - late CommandBarItem overflowItem; if (widget.overflowItemBuilder != null) { overflowItem = widget.overflowItemBuilder!(toggleSecondaryMenu); @@ -364,7 +366,7 @@ class _CommandBarState extends State { } return true; }()); - dynamicallyHiddenPrimaryItems = hiddenItems; + _dynamicallyHiddenPrimaryItems = hiddenItems; }); }, children: builtItems.toList(), @@ -442,7 +444,7 @@ enum CommandBarItemDisplayMode { /// /// Normally you would want to render an item in this visual context as a /// [ListTile]. - inSecondary, + inSecondary; } /// An individual control displayed within a [CommandBar]. Sub-class this to @@ -526,7 +528,11 @@ class CommandBarButton extends CommandBarItem { /// The trailing widget to use if this item is shown in the secondary menu final Widget? trailing; + + /// The callback to call when the button is pressed. final VoidCallback? onPressed; + + /// The callback to call when the button is long pressed. final VoidCallback? onLongPress; /// {@macro flutter.widgets.Focus.focusNode} From 38a78623055c2f9cacf5ccdb486f26ca4ac50d7d Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 20:57:12 -0300 Subject: [PATCH 07/16] feat: CommandBarButton.closeAfterClick --- example/lib/screens/surface/commandbars.dart | 60 +++++++------------- lib/src/controls/flyouts/content.dart | 10 +++- lib/src/controls/flyouts/menu.dart | 10 ++++ lib/src/controls/inputs/dropdown_button.dart | 2 +- lib/src/controls/surfaces/commandbar.dart | 34 +++++++---- 5 files changed, 61 insertions(+), 55 deletions(-) diff --git a/example/lib/screens/surface/commandbars.dart b/example/lib/screens/surface/commandbars.dart index 0aaed41e2..d35d18893 100644 --- a/example/lib/screens/surface/commandbars.dart +++ b/example/lib/screens/surface/commandbars.dart @@ -12,27 +12,17 @@ class CommandBarsPage extends StatefulWidget { class _CommandBarsPageState extends State with PageMixin { final simpleCommandBarItems = [ - CommandBarBuilderItem( - builder: (context, mode, w) => Tooltip( - message: "Create something new!", - child: w, - ), - wrappedItem: CommandBarButton( - icon: const Icon(FluentIcons.add), - label: const Text('New'), - onPressed: () {}, - ), + CommandBarButton( + icon: const Icon(FluentIcons.add), + label: const Text('New'), + tooltip: 'Create something new!', + onPressed: () {}, ), - CommandBarBuilderItem( - builder: (context, mode, w) => Tooltip( - message: "Delete what is currently selected!", - child: w, - ), - wrappedItem: CommandBarButton( - icon: const Icon(FluentIcons.delete), - label: const Text('Delete'), - onPressed: () {}, - ), + CommandBarButton( + icon: const Icon(FluentIcons.delete), + label: const Text('Delete'), + tooltip: 'Delete what is currently selected!', + onPressed: () {}, ), CommandBarButton( icon: const Icon(FluentIcons.archive), @@ -140,27 +130,17 @@ class _CommandBarsPageState extends State with PageMixin { codeSnippet: ''' /// Define list of CommandBarItem final simpleCommandBarItems = [ - CommandBarBuilderItem( - builder: (context, mode, w) => Tooltip( - message: "Create something new!", - child: w, - ), - wrappedItem: CommandBarButton( - icon: const Icon(FluentIcons.add), - label: const Text('New'), - onPressed: () {}, - ), + CommandBarButton( + icon: const Icon(FluentIcons.add), + label: const Text('New'), + tooltip: 'Create something new!', + onPressed: () {}, ), - CommandBarBuilderItem( - builder: (context, mode, w) => Tooltip( - message: "Delete what is currently selected!", - child: w, - ), - wrappedItem: CommandBarButton( - icon: const Icon(FluentIcons.delete), - label: const Text('Delete'), - onPressed: () {}, - ), + CommandBarButton( + icon: const Icon(FluentIcons.delete), + label: const Text('Delete'), + tooltip: 'Delete what is currently selected!', + onPressed: () {}, ), CommandBarButton( icon: const Icon(FluentIcons.archive), diff --git a/lib/src/controls/flyouts/content.dart b/lib/src/controls/flyouts/content.dart index 7af9542ba..737dd0a38 100644 --- a/lib/src/controls/flyouts/content.dart +++ b/lib/src/controls/flyouts/content.dart @@ -64,7 +64,7 @@ class FlyoutContent extends StatelessWidget { RoundedRectangleBorder( borderRadius: BorderRadius.circular(6.0), side: BorderSide( - width: 0.25, + width: 0.5, color: theme.inactiveBackgroundColor, ), ); @@ -94,7 +94,7 @@ class FlyoutContent extends StatelessWidget { } } -/// A tile that is used inside of [FlyoutContent] +/// A tile that is used inside of [FlyoutContent]. /// /// See also: /// @@ -105,6 +105,7 @@ class FlyoutListTile extends StatelessWidget { const FlyoutListTile({ super.key, this.onPressed, + this.onLongPress, this.tooltip, this.icon, required this.text, @@ -117,8 +118,12 @@ class FlyoutListTile extends StatelessWidget { this.showSelectedIndicator = true, }); + /// Called when the tile is tapped or otherwise activated. final VoidCallback? onPressed; + /// Called when the tile is long-pressed. + final VoidCallback? onLongPress; + /// The tile tooltip text final String? tooltip; @@ -160,6 +165,7 @@ class FlyoutListTile extends StatelessWidget { return HoverButton( key: key, onPressed: onPressed, + onLongPress: onLongPress, focusNode: focusNode, autofocus: autofocus, semanticLabel: semanticLabel, diff --git a/lib/src/controls/flyouts/menu.dart b/lib/src/controls/flyouts/menu.dart index b027b7e52..c1bdc4786 100644 --- a/lib/src/controls/flyouts/menu.dart +++ b/lib/src/controls/flyouts/menu.dart @@ -236,6 +236,8 @@ class MenuFlyoutItem extends MenuFlyoutItemBase { required this.text, this.trailing, required this.onPressed, + this.onLongPress, + this.focusNode, this.selected = false, this.closeAfterClick = true, }); @@ -266,6 +268,12 @@ class MenuFlyoutItem extends MenuFlyoutItemBase { /// If `null`, the item will be marked as disabled. final VoidCallback? onPressed; + /// Called when the item is long pressed. + final VoidCallback? onLongPress; + + /// The focus node of the item. + final FocusNode? focusNode; + /// Whether this item is selected or not. final bool selected; @@ -301,6 +309,8 @@ class MenuFlyoutItem extends MenuFlyoutItemBase { if (closeAfterClick) Navigator.of(context).maybePop(); onPressed?.call(); }, + onLongPress: onLongPress, + focusNode: focusNode, ), ); } diff --git a/lib/src/controls/inputs/dropdown_button.dart b/lib/src/controls/inputs/dropdown_button.dart index 0c73d0177..da52307c1 100644 --- a/lib/src/controls/inputs/dropdown_button.dart +++ b/lib/src/controls/inputs/dropdown_button.dart @@ -111,7 +111,7 @@ class DropDownButton extends StatefulWidget { /// Whether the flyout will be closed after an item is tapped. /// - /// This is only effective on items that are [MenuFlyoutItem] + /// This is only effective on items that are [MenuFlyoutItem]. /// /// Defaults to `true` final bool closeAfterClick; diff --git a/lib/src/controls/surfaces/commandbar.dart b/lib/src/controls/surfaces/commandbar.dart index f5f002efc..dddc88520 100644 --- a/lib/src/controls/surfaces/commandbar.dart +++ b/lib/src/controls/surfaces/commandbar.dart @@ -541,8 +541,16 @@ class CommandBarButton extends CommandBarItem { /// {@macro flutter.widgets.Focus.autofocus} final bool autofocus; + /// The tooltip to show when the button is hovered over. final String? tooltip; + /// Whether the flyout will be closed after an item is tapped. + /// + /// This only affects items in secondary mode. + /// + /// Defaults to `true`. + final bool closeAfterClick; + /// Creates a command bar button const CommandBarButton({ super.key, @@ -555,6 +563,7 @@ class CommandBarButton extends CommandBarItem { this.focusNode, this.autofocus = false, this.tooltip, + this.closeAfterClick = true, }); @override @@ -600,18 +609,19 @@ class CommandBarButton extends CommandBarItem { } return button; case CommandBarItemDisplayMode.inSecondary: - return Padding( - padding: const EdgeInsetsDirectional.only(end: 8.0, start: 8.0), - child: FlyoutListTile( - key: key, - onPressed: onPressed, - focusNode: focusNode, - autofocus: autofocus, - icon: icon, - text: label ?? const SizedBox.shrink(), - tooltip: tooltip, - ), - ); + return MenuFlyoutItem( + key: key, + onPressed: onPressed, + onLongPress: onLongPress, + leading: icon, + text: label ?? const SizedBox.shrink(), + trailing: () { + if (trailing != null) return trailing!; + if (tooltip != null) return Text(tooltip!); + return null; + }(), + closeAfterClick: closeAfterClick, + ).build(context); } } } From aae95349a53416620a69049c032321671967853b Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 21:12:30 -0300 Subject: [PATCH 08/16] feat: Add CommandBar background color when --- lib/src/controls/flyouts/content.dart | 2 +- lib/src/controls/surfaces/commandbar.dart | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/src/controls/flyouts/content.dart b/lib/src/controls/flyouts/content.dart index 737dd0a38..b103c9684 100644 --- a/lib/src/controls/flyouts/content.dart +++ b/lib/src/controls/flyouts/content.dart @@ -64,7 +64,7 @@ class FlyoutContent extends StatelessWidget { RoundedRectangleBorder( borderRadius: BorderRadius.circular(6.0), side: BorderSide( - width: 0.5, + width: 1, color: theme.inactiveBackgroundColor, ), ); diff --git a/lib/src/controls/surfaces/commandbar.dart b/lib/src/controls/surfaces/commandbar.dart index dddc88520..b7538dd80 100644 --- a/lib/src/controls/surfaces/commandbar.dart +++ b/lib/src/controls/surfaces/commandbar.dart @@ -277,6 +277,7 @@ class CommandBarState extends State { BuildContext context, CommandBarItemDisplayMode primaryMode, ) { + final theme = FluentTheme.of(context); final builtItems = widget.primaryItems.map((item) => item.build(context, primaryMode)); Widget? overflowWidget; @@ -390,6 +391,24 @@ class CommandBarState extends State { if (widget._isExpanded) { w = listBuilder.call(children: [Expanded(child: w)]); } + w = Container( + decoration: ShapeDecoration( + color: secondaryFlyoutController.isOpen + ? theme.menuColor.withValues(alpha: kMenuColorOpacity) + : Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6.0), + side: BorderSide( + width: 1, + color: secondaryFlyoutController.isOpen + ? theme.inactiveBackgroundColor + : Colors.transparent, + ), + ), + ), + child: w, + ); + return FlyoutTarget(controller: secondaryFlyoutController, child: w); } @@ -594,7 +613,7 @@ class CommandBarButton extends CommandBarItem { icon: Row(mainAxisSize: MainAxisSize.min, children: [ if (showIcon) IconTheme.merge( - data: const IconThemeData(size: 16), + data: const IconThemeData(size: 16.0), child: icon!, ), if (showIcon && showLabel) const SizedBox(width: 10), From 05071c453292c1c66014eca94651f9e056e9d999 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 21:16:59 -0300 Subject: [PATCH 09/16] fix: Secondary Flyout position on vertical mode --- lib/src/controls/surfaces/commandbar.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/src/controls/surfaces/commandbar.dart b/lib/src/controls/surfaces/commandbar.dart index b7538dd80..a9f276488 100644 --- a/lib/src/controls/surfaces/commandbar.dart +++ b/lib/src/controls/surfaces/commandbar.dart @@ -207,9 +207,10 @@ class CommandBarState extends State { final future = secondaryFlyoutController.showFlyout( buildTarget: true, autoModeConfiguration: FlyoutAutoConfiguration( - preferredMode: FlyoutPlacementMode.bottomRight.resolve( - Directionality.of(context), - ), + preferredMode: (widget.direction == Axis.horizontal + ? FlyoutPlacementMode.bottomRight + : FlyoutPlacementMode.right) + .resolve(Directionality.of(context)), ), builder: (context) { return FlyoutContent( From 44fa452ef844032679d5ec1f1c71c18faaf37900 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 21:19:47 -0300 Subject: [PATCH 10/16] fix: Command Bar padding --- lib/src/controls/surfaces/commandbar.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/controls/surfaces/commandbar.dart b/lib/src/controls/surfaces/commandbar.dart index a9f276488..e9a394907 100644 --- a/lib/src/controls/surfaces/commandbar.dart +++ b/lib/src/controls/surfaces/commandbar.dart @@ -393,6 +393,7 @@ class CommandBarState extends State { w = listBuilder.call(children: [Expanded(child: w)]); } w = Container( + padding: const EdgeInsets.symmetric(horizontal: 4.0), decoration: ShapeDecoration( color: secondaryFlyoutController.isOpen ? theme.menuColor.withValues(alpha: kMenuColorOpacity) From a9b6f237b94bd92b3bdf6c41e188ba7f394347fd Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 21:40:23 -0300 Subject: [PATCH 11/16] feat: Improve CommandBar example --- example/lib/screens/surface/commandbars.dart | 857 ++----------------- 1 file changed, 91 insertions(+), 766 deletions(-) diff --git a/example/lib/screens/surface/commandbars.dart b/example/lib/screens/surface/commandbars.dart index d35d18893..8558239d6 100644 --- a/example/lib/screens/surface/commandbars.dart +++ b/example/lib/screens/surface/commandbars.dart @@ -107,399 +107,122 @@ class _CommandBarsPageState extends State with PageMixin { ), ]; + double? compactBreakpointWidth; + bool _vertical = false; + bool _compact = false; + var overflowBehavior = CommandBarOverflowBehavior.dynamicOverflow; + @override Widget build(BuildContext context) { return ScaffoldPage.scrollable( - header: PageHeader( - title: const Text('CommandBar'), - commandBar: CommandBar( - mainAxisAlignment: MainAxisAlignment.end, - primaryItems: [ - ...simpleCommandBarItems, - ], - ), - ), + header: const PageHeader(title: Text('CommandBar')), children: [ const Text( 'Command bars provide users with easy access to your app\'s most ' 'common tasks. Command bars can provide access to app-level or ' 'page-specific commands and can be used with any navigation pattern.', ), - subtitle(content: const Text('Simple command bar (no wrapping)')), - CardHighlight( - codeSnippet: ''' -/// Define list of CommandBarItem -final simpleCommandBarItems = [ - CommandBarButton( - icon: const Icon(FluentIcons.add), - label: const Text('New'), - tooltip: 'Create something new!', - onPressed: () {}, - ), - CommandBarButton( - icon: const Icon(FluentIcons.delete), - label: const Text('Delete'), - tooltip: 'Delete what is currently selected!', - onPressed: () {}, - ), - CommandBarButton( - icon: const Icon(FluentIcons.archive), - label: const Text('Archive'), - onPressed: () {}, - ), - CommandBarButton( - icon: const Icon(FluentIcons.move), - label: const Text('Move'), - onPressed: () {}, - ), - const CommandBarButton( - icon: Icon(FluentIcons.cancel), - label: Text('Disabled'), - onPressed: null, - ), -]; - -/// Generate CommandBar with different properties like overflowBehavior -CommandBar( - overflowBehavior: CommandBarOverflowBehavior.noWrap, - primaryItems: [ - ...simpleCommandBarItems, - ], -); -''', - child: CommandBar( - overflowBehavior: CommandBarOverflowBehavior.noWrap, - primaryItems: [ - ...simpleCommandBarItems, - ], - ), - ), - subtitle( - content: const Text('Simple command bar (no wrapping) (vertical)')), - CardHighlight( - codeSnippet: ''' -/// Lists of CommandBarItem named as simpleCommandBarItems and moreCommandBarItems -/// Used as following - -/// Generate CommandBar with different properties like overflowBehavior -CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.noWrap, - primaryItems: [ - ...simpleCommandBarItems, - ], -); -''', - child: CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.noWrap, - primaryItems: [ - ...simpleCommandBarItems, - ], - ), - ), - subtitle( - content: const Text( - 'Command bar with many items (wrapping, auto-compact < 600px)', - ), - ), - CardHighlight( - codeSnippet: ''' -/// Lists of CommandBarItem named as simpleCommandBarItems and moreCommandBarItems -/// Used as following - -CommandBar( - overflowBehavior: CommandBarOverflowBehavior.wrap, - compactBreakpointWidth: 600, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - ], -); -''', - child: CommandBar( - overflowBehavior: CommandBarOverflowBehavior.wrap, - compactBreakpointWidth: 600, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - ], - ), - ), - subtitle( - content: const Text( - 'Command bar with many items (wrapping, auto-compact < 600px) (vertical)', - ), - ), - CardHighlight( - codeSnippet: ''' -/// Lists of CommandBarItem named as simpleCommandBarItems and moreCommandBarItems -/// Used as following - -ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 230), - child: CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.wrap, - compactBreakpointWidth: 600, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(direction: Axis.horizontal), - ...moreCommandBarItems, - ], - ), -); -''', - child: ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 230), - child: CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.wrap, - compactBreakpointWidth: 600, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(direction: Axis.horizontal), - ...moreCommandBarItems, - ], - ), - ), - ), - subtitle( - content: const Text( - 'Carded compact command bar with many items (clipped)', - ), - ), - CardHighlight( - codeSnippet: ''' -/// Lists of CommandBarItem named as simpleCommandBarItems and moreCommandBarItems -/// Used as following - -ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 230), - child: CommandBarCard( - child: CommandBar( - overflowBehavior: CommandBarOverflowBehavior.clip, - isCompact: true, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - ], - ), - ), -); -''', - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 230), - child: CommandBarCard( - child: CommandBar( - overflowBehavior: CommandBarOverflowBehavior.clip, - isCompact: true, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - ], + const SizedBox(height: 8.0), + Card( + child: Wrap( + spacing: 12.0, + crossAxisAlignment: WrapCrossAlignment.end, + children: [ + SizedBox( + width: 200.0, + child: InfoLabel( + label: 'Compact breakpoint width', + child: NumberBox( + value: compactBreakpointWidth, + onChanged: (value) { + setState(() { + compactBreakpointWidth = value; + }); + }), + ), ), - ), - ), - ), - subtitle( - content: const Text( - 'Carded compact command bar with many items (clipped) (vertical)', - ), - ), - CardHighlight( - codeSnippet: ''' -/// Lists of CommandBarItem named as simpleCommandBarItems and moreCommandBarItems -/// Used as following - -ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 230), - child: CommandBarCard( - child: CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.clip, - isCompact: true, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - ], - ), - ), -); -''', - child: ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 230), - child: CommandBarCard( - child: CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.clip, - isCompact: true, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - ], + InfoLabel( + label: 'Overflow behavior', + child: ComboBox( + value: overflowBehavior, + items: CommandBarOverflowBehavior.values.map((behavior) { + return ComboBoxItem( + value: behavior, + child: Text( + behavior.name + .replaceAllMapped( + RegExp(r'([a-z])([A-Z])'), + (match) => '${match.group(1)} ${match.group(2)}', + ) + .uppercaseFirst(), + ), + ); + }).toList(), + onChanged: (value) { + setState(() { + overflowBehavior = value!; + }); + }, + ), ), - ), - ), - ), - subtitle( - content: const Text( - 'Carded compact command bar with many items (dynamic overflow)', - ), - ), - CardHighlight( - codeSnippet: ''' -/// Create different lists of CommandBarItem -/// named simpleCommandBarItems, moreCommandBarItems, evenMoreCommandBarItems -/// These lists can be used as primaryItems as shown -CommandBarCard( - child: CommandBar( - overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - const CommandBarSeparator(), - ...evenMoreCommandBarItems, - ], - ), -); -''', - child: CommandBarCard( - child: CommandBar( - overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - const CommandBarSeparator(), - ...evenMoreCommandBarItems, - ], - ), - ), - ), - subtitle( - content: const Text( - 'Carded compact command bar with many items (dynamic overflow) (vertical)', - ), - ), - CardHighlight( - codeSnippet: ''' -/// Create different lists of CommandBarItem -/// named simpleCommandBarItems, moreCommandBarItems, evenMoreCommandBarItems -/// These lists can be used as primaryItems as shown -ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 230), - child: CommandBarCard( - child: CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - const CommandBarSeparator(), - ...evenMoreCommandBarItems, - ], - ), - ), -); -''', - child: ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 230), - child: CommandBarCard( - child: CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - const CommandBarSeparator(), - ...evenMoreCommandBarItems, - ], + Checkbox( + checked: _vertical, + onChanged: (value) { + setState(() { + _vertical = value!; + }); + }, + content: const Text('Vertical'), ), - ), + Checkbox( + checked: _compact, + onChanged: (value) { + setState(() { + _compact = value!; + }); + }, + content: const Text('Compact'), + ), + ], ), ), subtitle( content: const Text( - 'End-aligned command bar with many items (dynamic overflow, auto-compact < 900px)', + 'Command bar with many items (dynamic overflow)', ), ), CardHighlight( codeSnippet: ''' -/// Create different lists of CommandBarItem -/// named simpleCommandBarItems, moreCommandBarItems, evenMoreCommandBarItems -/// These lists can be used as primaryItems as shown - -CommandBar( - mainAxisAlignment: MainAxisAlignment.end, - overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, - compactBreakpointWidth: 900, +CommandBar(${compactBreakpointWidth != null ? '\n compactBreakpointWidth: compactBreakpointWidth,' : ''}${_vertical ? '\n direction: Axis.vertical,' : ''}${_compact ? '\n isCompact: true,' : ''} + overflowBehavior: $overflowBehavior, primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, + CommandBarButton( + icon: const Icon(FluentIcons.add), + label: const Text('New'), + tooltip: 'Create something new!', + onPressed: () { + // Create something new! + }, + ), const CommandBarSeparator(), - ...evenMoreCommandBarItems, + CommandBarButton( + icon: const Icon(FluentIcons.delete), + label: const Text('Delete'), + tooltip: 'Delete what is currently selected!', + onPressed: () { + // Delete what is currently selected! + }, + ), ], -); -''', - child: CommandBar( - mainAxisAlignment: MainAxisAlignment.end, - overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, - compactBreakpointWidth: 900, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - const CommandBarSeparator(), - ...evenMoreCommandBarItems, - ], - ), - ), - subtitle( - content: const Text( - 'End-aligned command bar with many items (dynamic overflow, auto-compact < 900px) (vertical)', - ), - ), - CardHighlight( - codeSnippet: ''' -/// Create different lists of CommandBarItem -/// named simpleCommandBarItems, moreCommandBarItems, evenMoreCommandBarItems -/// These lists can be used as primaryItems as shown - -ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 230), - child: CommandBar( - direction: Axis.vertical, - mainAxisAlignment: MainAxisAlignment.end, - overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, - compactBreakpointWidth: 900, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - const CommandBarSeparator(), - ...evenMoreCommandBarItems, - ], - ), -); +), ''', - child: ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 230), + child: SizedBox( + height: _vertical ? 400.0 : null, child: CommandBar( - direction: Axis.vertical, - mainAxisAlignment: MainAxisAlignment.end, - overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, - compactBreakpointWidth: 900, + compactBreakpointWidth: compactBreakpointWidth, + direction: _vertical ? Axis.vertical : Axis.horizontal, + isCompact: _compact, + overflowBehavior: overflowBehavior, primaryItems: [ ...simpleCommandBarItems, const CommandBarSeparator(), @@ -510,404 +233,6 @@ ConstrainedBox( ), ), ), - subtitle( - content: const Text( - 'End-aligned command bar with permanent secondary items (dynamic overflow)', - ), - ), - CardHighlight( - codeSnippet: ''' -/// Lists of CommandBarItem named as simpleCommandBarItems and moreCommandBarItems -/// Used as following - -CommandBar( - mainAxisAlignment: MainAxisAlignment.end, - overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - ], - secondaryItems: evenMoreCommandBarItems, -); -''', - child: CommandBar( - mainAxisAlignment: MainAxisAlignment.end, - overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - ], - secondaryItems: evenMoreCommandBarItems, - ), - ), - subtitle( - content: const Text( - 'End-aligned command bar with permanent secondary items (dynamic overflow) (vertical)', - ), - ), - CardHighlight( - codeSnippet: ''' -/// Lists of CommandBarItem named as simpleCommandBarItems and moreCommandBarItems -/// Used as following - -ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 230), - child: CommandBar( - direction: Axis.vertical, - mainAxisAlignment: MainAxisAlignment.end, - overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - ], - secondaryItems: evenMoreCommandBarItems, - ), -); -''', - child: ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 230), - child: CommandBar( - direction: Axis.vertical, - mainAxisAlignment: MainAxisAlignment.end, - overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - ], - secondaryItems: evenMoreCommandBarItems, - ), - ), - ), - subtitle( - content: const Text( - 'Command bar with secondary items (wrapping)', - ), - ), - CardHighlight( - codeSnippet: ''' -/// Lists of CommandBarItem named as simpleCommandBarItems and moreCommandBarItems -/// Used as following - -CommandBar( - overflowBehavior: CommandBarOverflowBehavior.wrap, - primaryItems: simpleCommandBarItems, - secondaryItems: moreCommandBarItems, -); -''', - child: CommandBar( - overflowBehavior: CommandBarOverflowBehavior.wrap, - primaryItems: simpleCommandBarItems, - secondaryItems: moreCommandBarItems, - ), - ), - subtitle( - content: const Text( - 'Command bar with secondary items (wrapping) (vertical)', - ), - ), - CardHighlight( - codeSnippet: ''' -/// Lists of CommandBarItem named as simpleCommandBarItems and moreCommandBarItems -/// Used as following - -ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 230), - child: CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.wrap, - primaryItems: simpleCommandBarItems, - secondaryItems: moreCommandBarItems, - ), -); -''', - child: ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 230), - child: CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.wrap, - primaryItems: simpleCommandBarItems, - secondaryItems: moreCommandBarItems, - ), - ), - ), - subtitle( - content: const Text( - 'Carded complex command bar with many items (horizontal scrolling)', - ), - ), - CardHighlight( - codeSnippet: ''' -CommandBarCard( - child: Row(children: [ - Expanded( - child: CommandBar( - overflowBehavior: CommandBarOverflowBehavior.scrolling, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - ], - ), - ), - // End-aligned button(s) - CommandBar( - overflowBehavior: CommandBarOverflowBehavior.noWrap, - primaryItems: [ - CommandBarButton( - icon: const Icon(FluentIcons.refresh), - onPressed: () {}, - ), - ], - ), - ], - ), -); -''', - child: CommandBarCard( - child: Row( - children: [ - Expanded( - child: CommandBar( - overflowBehavior: CommandBarOverflowBehavior.scrolling, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - ], - ), - ), - // End-aligned button(s) - CommandBar( - overflowBehavior: CommandBarOverflowBehavior.noWrap, - primaryItems: [ - CommandBarButton( - icon: const Icon(FluentIcons.refresh), - onPressed: () {}, - ), - ], - ), - ], - ), - ), - ), - subtitle( - content: const Text( - 'Carded complex command bar with many items (vertical scrolling) (vertical)', - ), - ), - CardHighlight( - codeSnippet: ''' -ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 230, maxWidth: 48), - child: CommandBarCard( - child: Column( - children: [ - Expanded( - child: CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.scrolling, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(direction: Axis.horizontal), - ...moreCommandBarItems, - ], - ), - ), - // End-aligned button(s) - CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.noWrap, - primaryItems: [ - CommandBarButton( - icon: const Icon(FluentIcons.refresh), - onPressed: () {}, - ), - ], - ), - ], - ), - ), -); -''', - child: ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 230, maxWidth: 48), - child: CommandBarCard( - child: Column( - children: [ - Expanded( - child: CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.scrolling, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(direction: Axis.horizontal), - ...moreCommandBarItems, - ], - ), - ), - // End-aligned button(s) - CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.noWrap, - primaryItems: [ - CommandBarButton( - icon: const Icon(FluentIcons.refresh), - onPressed: () {}, - ), - ], - ), - ], - ), - ), - ), - ), - subtitle( - content: const Text( - 'Carded complex command bar with many items (dynamic overflow)', - ), - ), - CardHighlight( - codeSnippet: ''' -CommandBarCard( - child: Row( - children: [ - Expanded( - child: CommandBar( - overflowBehavior: CommandBarOverflowBehavior.dynamicOverflow, - overflowItemAlignment: MainAxisAlignment.end, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - ], - secondaryItems: evenMoreCommandBarItems, - ), - ), - // End-aligned button(s) - CommandBar( - overflowBehavior: CommandBarOverflowBehavior.noWrap, - primaryItems: [ - CommandBarButton( - icon: const Icon(FluentIcons.refresh), - onPressed: () {}, - ), - ], - ), - ], - ), -); -''', - child: CommandBarCard( - child: Row( - children: [ - Expanded( - child: CommandBar( - overflowBehavior: - CommandBarOverflowBehavior.dynamicOverflow, - overflowItemAlignment: MainAxisAlignment.end, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - ], - secondaryItems: evenMoreCommandBarItems, - ), - ), - // End-aligned button(s) - CommandBar( - overflowBehavior: CommandBarOverflowBehavior.noWrap, - primaryItems: [ - CommandBarButton( - icon: const Icon(FluentIcons.refresh), - onPressed: () {}, - ), - ], - ), - ], - ), - ), - ), - subtitle( - content: const Text( - 'Carded complex command bar with many items (dynamic overflow) (vertical)', - ), - ), - CardHighlight( - codeSnippet: ''' -ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 330), - child: CommandBarCard( - child: Column( - children: [ - Expanded( - child: CommandBar( - direction: Axis.vertical, - overflowBehavior: - CommandBarOverflowBehavior.dynamicOverflow, - overflowItemAlignment: MainAxisAlignment.end, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(direction: Axis.horizontal), - ...moreCommandBarItems, - ], - secondaryItems: evenMoreCommandBarItems, - ), - ), - // End-aligned button(s) - CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.noWrap, - primaryItems: [ - CommandBarButton( - icon: const Icon(FluentIcons.refresh), - onPressed: () {}, - ), - ], - ), - ], - ), - ), -); -''', - child: ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 330), - child: CommandBarCard( - child: Column( - children: [ - Expanded( - child: CommandBar( - direction: Axis.vertical, - overflowBehavior: - CommandBarOverflowBehavior.dynamicOverflow, - overflowItemAlignment: MainAxisAlignment.end, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(direction: Axis.horizontal), - ...moreCommandBarItems, - ], - secondaryItems: evenMoreCommandBarItems, - ), - ), - // End-aligned button(s) - CommandBar( - direction: Axis.vertical, - overflowBehavior: CommandBarOverflowBehavior.noWrap, - primaryItems: [ - CommandBarButton( - icon: const Icon(FluentIcons.refresh), - onPressed: () {}, - ), - ], - ), - ], - ), - ), - ), - ), ], ); } From a6ca4f8ab4c36e840d2dfc308c99e42a47b693d9 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 21:52:24 -0300 Subject: [PATCH 12/16] fix: CommandBarDivider height/width --- example/lib/screens/surface/commandbars.dart | 1 + lib/src/controls/surfaces/commandbar.dart | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/example/lib/screens/surface/commandbars.dart b/example/lib/screens/surface/commandbars.dart index 8558239d6..7aa408608 100644 --- a/example/lib/screens/surface/commandbars.dart +++ b/example/lib/screens/surface/commandbars.dart @@ -126,6 +126,7 @@ class _CommandBarsPageState extends State with PageMixin { Card( child: Wrap( spacing: 12.0, + runSpacing: 12.0, crossAxisAlignment: WrapCrossAlignment.end, children: [ SizedBox( diff --git a/lib/src/controls/surfaces/commandbar.dart b/lib/src/controls/surfaces/commandbar.dart index e9a394907..89384b1b1 100644 --- a/lib/src/controls/surfaces/commandbar.dart +++ b/lib/src/controls/surfaces/commandbar.dart @@ -393,7 +393,7 @@ class CommandBarState extends State { w = listBuilder.call(children: [Expanded(child: w)]); } w = Container( - padding: const EdgeInsets.symmetric(horizontal: 4.0), + padding: const EdgeInsets.all(4.0), decoration: ShapeDecoration( color: secondaryFlyoutController.isOpen ? theme.menuColor.withValues(alpha: kMenuColorOpacity) @@ -662,7 +662,6 @@ class CommandBarSeparator extends CommandBarItem { super.key, this.color, this.thickness, - this.direction = Axis.vertical, }); /// Override the color used by the [Divider]. @@ -671,25 +670,27 @@ class CommandBarSeparator extends CommandBarItem { /// Override the separator thickness. final double? thickness; - /// The direction of the separator. Defaults to [Axis.vertical]. - /// This attribute is opposite to [CommandBar. direction]. - final Axis direction; - @override Widget build(BuildContext context, CommandBarItemDisplayMode displayMode) { + final parent = context.findAncestorWidgetOfExactType(); + final parentDirection = parent?.direction ?? Axis.horizontal; + final direction = + parentDirection == Axis.horizontal ? Axis.vertical : Axis.horizontal; switch (displayMode) { case CommandBarItemDisplayMode.inPrimary: case CommandBarItemDisplayMode.inPrimaryCompact: return CommandBarItemInPrimary( child: ConstrainedBox( constraints: BoxConstraints( - minHeight: direction == Axis.vertical ? 28 : 0, - minWidth: direction == Axis.horizontal ? 28 : 0, + minWidth: direction == Axis.horizontal ? 24.0 : 0.0, + minHeight: direction == Axis.vertical ? 24.0 : 0.0, ), child: Divider( direction: direction, style: DividerThemeData( thickness: thickness, + horizontalMargin: EdgeInsets.zero, + verticalMargin: EdgeInsets.zero, decoration: color != null ? BoxDecoration(color: color) : null, ), ), From 522cc295298553d5edea22bd967c48d1a5a77705 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 21:52:41 -0300 Subject: [PATCH 13/16] fix: Commandbar flyout --- lib/src/controls/surfaces/commandbar.dart | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/src/controls/surfaces/commandbar.dart b/lib/src/controls/surfaces/commandbar.dart index 89384b1b1..1304d5fb7 100644 --- a/lib/src/controls/surfaces/commandbar.dart +++ b/lib/src/controls/surfaces/commandbar.dart @@ -697,13 +697,7 @@ class CommandBarSeparator extends CommandBarItem { ), ); case CommandBarItemDisplayMode.inSecondary: - return Divider( - style: DividerThemeData( - thickness: thickness, - decoration: color != null ? BoxDecoration(color: color) : null, - horizontalMargin: const EdgeInsetsDirectional.only(bottom: 5.0), - ), - ); + return const MenuFlyoutSeparator().build(context); } } } From ee44c230b5ab4eb56d9ddad3b2e4e7266cc26337 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 22:01:49 -0300 Subject: [PATCH 14/16] fix: Finding parent inside CommandBar --- example/lib/screens/surface/commandbars.dart | 40 +++++++++++--------- lib/src/controls/surfaces/commandbar.dart | 24 +++++++----- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/example/lib/screens/surface/commandbars.dart b/example/lib/screens/surface/commandbars.dart index 7aa408608..bf903038d 100644 --- a/example/lib/screens/surface/commandbars.dart +++ b/example/lib/screens/surface/commandbars.dart @@ -134,12 +134,13 @@ class _CommandBarsPageState extends State with PageMixin { child: InfoLabel( label: 'Compact breakpoint width', child: NumberBox( - value: compactBreakpointWidth, - onChanged: (value) { - setState(() { - compactBreakpointWidth = value; - }); - }), + value: compactBreakpointWidth, + onChanged: (value) { + setState(() { + compactBreakpointWidth = value; + }); + }, + ), ), ), InfoLabel( @@ -219,18 +220,21 @@ CommandBar(${compactBreakpointWidth != null ? '\n compactBreakpointWidth: compa ''', child: SizedBox( height: _vertical ? 400.0 : null, - child: CommandBar( - compactBreakpointWidth: compactBreakpointWidth, - direction: _vertical ? Axis.vertical : Axis.horizontal, - isCompact: _compact, - overflowBehavior: overflowBehavior, - primaryItems: [ - ...simpleCommandBarItems, - const CommandBarSeparator(), - ...moreCommandBarItems, - const CommandBarSeparator(), - ...evenMoreCommandBarItems, - ], + child: Align( + alignment: AlignmentDirectional.topStart, + child: CommandBar( + compactBreakpointWidth: compactBreakpointWidth, + direction: _vertical ? Axis.vertical : Axis.horizontal, + isCompact: _compact, + overflowBehavior: overflowBehavior, + primaryItems: [ + ...simpleCommandBarItems, + const CommandBarSeparator(), + ...moreCommandBarItems, + const CommandBarSeparator(), + ...evenMoreCommandBarItems, + ], + ), ), ), ), diff --git a/lib/src/controls/surfaces/commandbar.dart b/lib/src/controls/surfaces/commandbar.dart index 1304d5fb7..52a8ecaac 100644 --- a/lib/src/controls/surfaces/commandbar.dart +++ b/lib/src/controls/surfaces/commandbar.dart @@ -423,19 +423,25 @@ class CommandBarState extends State { final displayMode = (widget.isCompact ?? false) ? CommandBarItemDisplayMode.inPrimaryCompact : CommandBarItemDisplayMode.inPrimary; - return _buildForPrimaryMode(context, displayMode); + return Builder(builder: (context) { + return _buildForPrimaryMode(context, displayMode); + }); } else { return LayoutBuilder(builder: (context, constraints) { if (constraints.maxWidth > widget.compactBreakpointWidth!) { - return _buildForPrimaryMode( - context, - CommandBarItemDisplayMode.inPrimary, - ); + return Builder(builder: (context) { + return _buildForPrimaryMode( + context, + CommandBarItemDisplayMode.inPrimary, + ); + }); } else { - return _buildForPrimaryMode( - context, - CommandBarItemDisplayMode.inPrimaryCompact, - ); + return Builder(builder: (context) { + return _buildForPrimaryMode( + context, + CommandBarItemDisplayMode.inPrimaryCompact, + ); + }); } }); } From d37470f06662fb3815efd9f03f4597b644569f55 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 22:17:13 -0300 Subject: [PATCH 15/16] chore: Changelog --- CHANGELOG.md | 17 +++++++++++++++++ example/lib/screens/surface/commandbars.dart | 20 +++++++++++++++++--- lib/src/controls/flyouts/flyout.dart | 4 ++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11efe9336..e397ce79b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,23 @@ - feat: Add `TextBox.cursorOpacityAnimates` (defaults to `FluentThemeData.cursorOpacityAnimates`, which defaults to `false`); default setting improves CPU/GPU efficiency while TextBox has focus ([#1164](https://github.com/bdlukaa/fluent_ui/issues/1164)) - fix: `DatePicker` selectable range matches the one between `startDate` and `endDate`. [#1170](https://github.com/bdlukaa/fluent_ui/issues/1170) - fix: `ScaffoldPage` has a built-in color if no parent `NavigationView` is found. ([#1168](https://github.com/bdlukaa/fluent_ui/issues/1168)) +- feat: Added `CommandBarButton.closeAfterClick` ([#1149](https://github.com/bdlukaa/fluent_ui/issues/1149)) +- feat: Added `FlyoutController.showFlyout.buildTarget` ([#1173](https://github.com/bdlukaa/fluent_ui/issues/1173)) + Primary items of the command bar are now accessible through the secondary flyout. +- fix: `CommandBar` secondary menu preferred placement mode ([#1174](https://github.com/bdlukaa/fluent_ui/pull/1174)) +- feat: Added `FlyoutController.close` ([#1174](https://github.com/bdlukaa/fluent_ui/pull/1174)) +- feat: `CommandBarState` is now accessible, making it possible to open/close the secondary flyout programmatically ([#1174](https://github.com/bdlukaa/fluent_ui/pull/1174)) + ```dart + final commandBarKey = GlobalKey(); + + CommandBar( + key: commandBarKey, + ..., + ), + + commandBarKey.currentState?.toggleSecondaryMenu(); + commandBarKey.currentState?.secondaryFlyoutController.close(); + ``` ## 4.10.0 diff --git a/example/lib/screens/surface/commandbars.dart b/example/lib/screens/surface/commandbars.dart index bf903038d..65a81a5c7 100644 --- a/example/lib/screens/surface/commandbars.dart +++ b/example/lib/screens/surface/commandbars.dart @@ -11,6 +11,8 @@ class CommandBarsPage extends StatefulWidget { } class _CommandBarsPageState extends State with PageMixin { + final key = GlobalKey(); + final simpleCommandBarItems = [ CommandBarButton( icon: const Icon(FluentIcons.add), @@ -185,6 +187,12 @@ class _CommandBarsPageState extends State with PageMixin { }, content: const Text('Compact'), ), + Button( + onPressed: () { + key.currentState?.toggleSecondaryMenu(); + }, + child: const Text('Toggle secondary menu'), + ), ], ), ), @@ -194,8 +202,10 @@ class _CommandBarsPageState extends State with PageMixin { ), ), CardHighlight( - codeSnippet: ''' -CommandBar(${compactBreakpointWidth != null ? '\n compactBreakpointWidth: compactBreakpointWidth,' : ''}${_vertical ? '\n direction: Axis.vertical,' : ''}${_compact ? '\n isCompact: true,' : ''} + codeSnippet: '''final commandBarKey = GlobalKey(); + +CommandBar( + key: commandBarKey, ${compactBreakpointWidth != null ? '\n compactBreakpointWidth: compactBreakpointWidth,' : ''}${_vertical ? '\n direction: Axis.vertical,' : ''}${_compact ? '\n isCompact: true,' : ''} overflowBehavior: $overflowBehavior, primaryItems: [ CommandBarButton( @@ -216,13 +226,17 @@ CommandBar(${compactBreakpointWidth != null ? '\n compactBreakpointWidth: compa }, ), ], -), +); + +// To toggle the secondary menu +commandBarKey.currentState?.toggleSecondaryMenu(); ''', child: SizedBox( height: _vertical ? 400.0 : null, child: Align( alignment: AlignmentDirectional.topStart, child: CommandBar( + key: key, compactBreakpointWidth: compactBreakpointWidth, direction: _vertical ? Axis.vertical : Axis.horizontal, isCompact: _compact, diff --git a/lib/src/controls/flyouts/flyout.dart b/lib/src/controls/flyouts/flyout.dart index de15f8d0b..f36189dcb 100644 --- a/lib/src/controls/flyouts/flyout.dart +++ b/lib/src/controls/flyouts/flyout.dart @@ -789,9 +789,13 @@ class FlyoutController with ChangeNotifier { return result; } + /// Closes the flyout. + /// + /// The flyout must be open, otherwise an error is thrown. void close() { _ensureAttached(); assert(_open); + if (!_open) return; // safe for release Navigator.of(_attachState!.context).pop(); } } From 4ce2d05955598a126f5f81613de285c2411568a0 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 29 Jan 2025 22:25:04 -0300 Subject: [PATCH 16/16] fix: Directionality of BreadcrumbBar flyout mode --- lib/src/controls/navigation/breadcrumb_bar.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/controls/navigation/breadcrumb_bar.dart b/lib/src/controls/navigation/breadcrumb_bar.dart index d9616dc50..00cc8d4e3 100644 --- a/lib/src/controls/navigation/breadcrumb_bar.dart +++ b/lib/src/controls/navigation/breadcrumb_bar.dart @@ -166,7 +166,9 @@ class BreadcrumbBarState extends State> { flyoutController.showFlyout( barrierColor: Colors.transparent, autoModeConfiguration: FlyoutAutoConfiguration( - preferredMode: FlyoutPlacementMode.bottomCenter, + preferredMode: FlyoutPlacementMode.bottomCenter.resolve( + Directionality.of(context), + ), ), builder: (context) { return MenuFlyout(