From 73dbd473a8d7fec2c88a10b8e25bbaef6aeae6ce Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Sat, 6 Jun 2026 13:43:13 -0500 Subject: [PATCH 1/2] Bind Small/Large/Extended FloatingActionButton variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds [ComposeBridge] partials and declarative [ComposeDefaults] enums for the three Material 3 1.4-bindable FAB variants: - SmallFloatingActionButton (40dp) — same shape as FAB - LargeFloatingActionButton (96dp) — same shape as FAB - ExtendedFloatingActionButton — multi-slot icon+text variant with Expanded:bool, modeled on AlertDialog (Icon and Text are required ComposableNode? slots; runtime null-checked) The four overloads are hash-mangled (-X-z6DiA, -ElI5-7k) due to inline Color/Dp parameters, so they need [ComposeBridge] until the upstream java-interop binder fix lands. The M3 Expressive variants (MediumFloatingActionButton, FloatingActionButtonMenu, ToggleFloatingActionButton) are tracked in #103 — they're not in Xamarin.AndroidX.Compose.Material3 1.4.0.3. Closes #52. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/ComposeNet.Compose/ComposeBridges.cs | 49 +++++++++++++++++++ src/ComposeNet.Compose/ComposeDefaults.cs | 18 +++++++ .../ExtendedFloatingActionButton.cs | 23 +++++++++ .../LargeFloatingActionButton.cs | 16 ++++++ .../PublicAPI.Unshipped.txt | 10 ++++ .../SmallFloatingActionButton.cs | 16 ++++++ src/ComposeNet.Sample/MainActivity.cs | 10 ++++ 7 files changed, 142 insertions(+) create mode 100644 src/ComposeNet.Compose/ExtendedFloatingActionButton.cs create mode 100644 src/ComposeNet.Compose/LargeFloatingActionButton.cs create mode 100644 src/ComposeNet.Compose/SmallFloatingActionButton.cs diff --git a/src/ComposeNet.Compose/ComposeBridges.cs b/src/ComposeNet.Compose/ComposeBridges.cs index 72e4a8a7..bcfba35c 100644 --- a/src/ComposeNet.Compose/ComposeBridges.cs +++ b/src/ComposeNet.Compose/ComposeBridges.cs @@ -474,6 +474,55 @@ public static partial void OutlinedIconToggleButton(bool @checked, [Callback(typ public static partial void FloatingActionButton(IFunction0 onClick, IModifier? modifier, IFunction2 content, IComposer composer); + // androidx.compose.material3.FloatingActionButtonKt.SmallFloatingActionButton-X-z6DiA + [ComposeBridge( + Class = "androidx/compose/material3/FloatingActionButtonKt", + JvmName = "SmallFloatingActionButton-X-z6DiA", + Signature = "(Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;" + + "Landroidx/compose/ui/graphics/Shape;JJ" + + "Landroidx/compose/material3/FloatingActionButtonElevation;" + + "Landroidx/compose/foundation/interaction/MutableInteractionSource;" + + "Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V", + Defaults = typeof(SmallFloatingActionButtonDefault))] + [ComposeFacade] + public static partial void SmallFloatingActionButton(IFunction0 onClick, IModifier? modifier, + IFunction2 content, IComposer composer); + + // androidx.compose.material3.FloatingActionButtonKt.LargeFloatingActionButton-X-z6DiA + [ComposeBridge( + Class = "androidx/compose/material3/FloatingActionButtonKt", + JvmName = "LargeFloatingActionButton-X-z6DiA", + Signature = "(Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;" + + "Landroidx/compose/ui/graphics/Shape;JJ" + + "Landroidx/compose/material3/FloatingActionButtonElevation;" + + "Landroidx/compose/foundation/interaction/MutableInteractionSource;" + + "Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V", + Defaults = typeof(LargeFloatingActionButtonDefault))] + [ComposeFacade] + public static partial void LargeFloatingActionButton(IFunction0 onClick, IModifier? modifier, + IFunction2 content, IComposer composer); + + // androidx.compose.material3.FloatingActionButtonKt.ExtendedFloatingActionButton-ElI5-7k + // (icon + text + expanded multi-slot variant — the canonical animated extended FAB) + [ComposeBridge( + Class = "androidx/compose/material3/FloatingActionButtonKt", + JvmName = "ExtendedFloatingActionButton-ElI5-7k", + Signature = "(Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;" + + "Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;Z" + + "Landroidx/compose/ui/graphics/Shape;JJ" + + "Landroidx/compose/material3/FloatingActionButtonElevation;" + + "Landroidx/compose/foundation/interaction/MutableInteractionSource;" + + "Landroidx/compose/runtime/Composer;II)V", + Defaults = typeof(ExtendedFloatingActionButtonDefault))] + [ComposeFacade] + public static partial void ExtendedFloatingActionButton( + IFunction2 text, + IFunction2 icon, + IFunction0 onClick, + IModifier? modifier, + bool expanded, + IComposer composer); + // androidx.compose.material3.SurfaceKt.Surface-T9BRK9s (non-interactive) [ComposeBridge( Class = "androidx/compose/material3/SurfaceKt", diff --git a/src/ComposeNet.Compose/ComposeDefaults.cs b/src/ComposeNet.Compose/ComposeDefaults.cs index 1605d39a..74bc7243 100644 --- a/src/ComposeNet.Compose/ComposeDefaults.cs +++ b/src/ComposeNet.Compose/ComposeDefaults.cs @@ -141,6 +141,24 @@ "!onClick", "modifier", "shape", "containerColor", "contentColor", "elevation", "interactionSource", "!content")] +// androidx.compose.material3.FloatingActionButtonKt.SmallFloatingActionButton-X-z6DiA: +// same shape as FloatingActionButton. +[assembly: ComposeDefaults("SmallFloatingActionButtonDefault", + "!onClick", "modifier", "shape", "containerColor", "contentColor", + "elevation", "interactionSource", "!content")] + +// androidx.compose.material3.FloatingActionButtonKt.LargeFloatingActionButton-X-z6DiA: +// same shape as FloatingActionButton. +[assembly: ComposeDefaults("LargeFloatingActionButtonDefault", + "!onClick", "modifier", "shape", "containerColor", "contentColor", + "elevation", "interactionSource", "!content")] + +// androidx.compose.material3.FloatingActionButtonKt.ExtendedFloatingActionButton-ElI5-7k: +// 10 user params; text/icon/onClick/expanded always provided by the caller. +[assembly: ComposeDefaults("ExtendedFloatingActionButtonDefault", + "!text", "!icon", "!onClick", "modifier", "!expanded", "shape", + "containerColor", "contentColor", "elevation", "interactionSource")] + // androidx.compose.material3.SurfaceKt.Surface-T9BRK9s (non-interactive): // 8 user params, only bit 7 = content provided. [assembly: ComposeDefaults("SurfaceDefault", diff --git a/src/ComposeNet.Compose/ExtendedFloatingActionButton.cs b/src/ComposeNet.Compose/ExtendedFloatingActionButton.cs new file mode 100644 index 00000000..f636996a --- /dev/null +++ b/src/ComposeNet.Compose/ExtendedFloatingActionButton.cs @@ -0,0 +1,23 @@ +namespace ComposeNet; + +/// +/// Material 3 ExtendedFloatingActionButton — the animated +/// extended FAB with separate Icon and Text slots and an +/// Expanded flag that animates between the icon-only and +/// icon + text states. +/// +/// +/// new ExtendedFloatingActionButton(onClick: () => /* ... */) +/// { +/// Icon = new Text("+"), +/// Text = new Text("Add"), +/// Expanded = true, +/// } +/// +/// +/// Both and are required — the +/// underlying Kotlin parameters have no default. Setting either to +/// null throws at +/// render time. +/// +public sealed partial class ExtendedFloatingActionButton; diff --git a/src/ComposeNet.Compose/LargeFloatingActionButton.cs b/src/ComposeNet.Compose/LargeFloatingActionButton.cs new file mode 100644 index 00000000..f5c987d5 --- /dev/null +++ b/src/ComposeNet.Compose/LargeFloatingActionButton.cs @@ -0,0 +1,16 @@ +namespace ComposeNet; + +/// +/// Material 3 LargeFloatingActionButton. Identical shape to +/// — just the larger (96 dp) +/// container. Use the collection-initializer form for a single icon +/// child: +/// +/// +/// new LargeFloatingActionButton(onClick: () => /* ... */) +/// { +/// new Text("+"), +/// } +/// +/// +public sealed partial class LargeFloatingActionButton; diff --git a/src/ComposeNet.Compose/PublicAPI.Unshipped.txt b/src/ComposeNet.Compose/PublicAPI.Unshipped.txt index aead0202..a61c6207 100644 --- a/src/ComposeNet.Compose/PublicAPI.Unshipped.txt +++ b/src/ComposeNet.Compose/PublicAPI.Unshipped.txt @@ -193,6 +193,12 @@ ComposeNet.ExposedDropdownMenu ComposeNet.ExposedDropdownMenu.ExposedDropdownMenu(bool expanded, System.Action! onDismissRequest) -> void ComposeNet.ExposedDropdownMenuBox ComposeNet.ExposedDropdownMenuBox.ExposedDropdownMenuBox(bool expanded, System.Action! onExpandedChange) -> void +ComposeNet.ExtendedFloatingActionButton +ComposeNet.ExtendedFloatingActionButton.ExtendedFloatingActionButton(System.Action! onClick, bool expanded) -> void +ComposeNet.ExtendedFloatingActionButton.Icon.get -> ComposeNet.ComposableNode? +ComposeNet.ExtendedFloatingActionButton.Icon.set -> void +ComposeNet.ExtendedFloatingActionButton.Text.get -> ComposeNet.ComposableNode? +ComposeNet.ExtendedFloatingActionButton.Text.set -> void ComposeNet.FilledIconButton ComposeNet.FilledIconButton.FilledIconButton(System.Action! onClick) -> void ComposeNet.FilledIconToggleButton @@ -280,6 +286,8 @@ ComposeNet.LargeFlexibleTopAppBar.Subtitle.get -> ComposeNet.ComposableNode? ComposeNet.LargeFlexibleTopAppBar.Subtitle.set -> void ComposeNet.LargeFlexibleTopAppBar.Title.get -> ComposeNet.ComposableNode? ComposeNet.LargeFlexibleTopAppBar.Title.set -> void +ComposeNet.LargeFloatingActionButton +ComposeNet.LargeFloatingActionButton.LargeFloatingActionButton(System.Action! onClick) -> void ComposeNet.LargeTopAppBar ComposeNet.LargeTopAppBar.Actions.get -> ComposeNet.ComposableNode? ComposeNet.LargeTopAppBar.Actions.set -> void @@ -562,6 +570,8 @@ ComposeNet.SingleChoiceSegmentedButtonRow ComposeNet.SingleChoiceSegmentedButtonRow.SingleChoiceSegmentedButtonRow() -> void ComposeNet.Slider ComposeNet.Slider.Slider(float value, System.Action! onValueChange) -> void +ComposeNet.SmallFloatingActionButton +ComposeNet.SmallFloatingActionButton.SmallFloatingActionButton(System.Action! onClick) -> void ComposeNet.Snackbar ComposeNet.Snackbar.Action.get -> ComposeNet.ComposableNode? ComposeNet.Snackbar.Action.set -> void diff --git a/src/ComposeNet.Compose/SmallFloatingActionButton.cs b/src/ComposeNet.Compose/SmallFloatingActionButton.cs new file mode 100644 index 00000000..89068032 --- /dev/null +++ b/src/ComposeNet.Compose/SmallFloatingActionButton.cs @@ -0,0 +1,16 @@ +namespace ComposeNet; + +/// +/// Material 3 SmallFloatingActionButton. Identical shape to +/// — just the smaller (40 dp) +/// container. Use the collection-initializer form for a single icon +/// child: +/// +/// +/// new SmallFloatingActionButton(onClick: () => /* ... */) +/// { +/// new Text("+"), +/// } +/// +/// +public sealed partial class SmallFloatingActionButton; diff --git a/src/ComposeNet.Sample/MainActivity.cs b/src/ComposeNet.Sample/MainActivity.cs index 40123513..084acf69 100644 --- a/src/ComposeNet.Sample/MainActivity.cs +++ b/src/ComposeNet.Sample/MainActivity.cs @@ -307,6 +307,16 @@ protected override void OnCreate(Bundle? savedInstanceState) { new Text("✕"), }, + new Row + { + new SmallFloatingActionButton(onClick: () => count++) { new Text("+") }, + new LargeFloatingActionButton(onClick: () => count++) { new Text("+") }, + }, + new ExtendedFloatingActionButton(onClick: () => count++, expanded: true) + { + Icon = new Text("✓"), + Text = new Text("Increment"), + }, new Button(onClick: () => showSnack.Value = true) { new Text("Show snackbar"), From d9daabce4bfc40019b9fa00338f56155f6c853d5 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Sat, 6 Jun 2026 13:53:42 -0500 Subject: [PATCH 2/2] Address PR review: fix XML doc examples for FAB variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use compilable lambda bodies (() => count.Value++) instead of () => /* ... */, and surface xpanded as the ctor parameter it actually is on ExtendedFloatingActionButton (was previously written as an object-initializer property, which doesn't exist). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/ComposeNet.Compose/ExtendedFloatingActionButton.cs | 7 +++---- src/ComposeNet.Compose/LargeFloatingActionButton.cs | 2 +- src/ComposeNet.Compose/SmallFloatingActionButton.cs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/ComposeNet.Compose/ExtendedFloatingActionButton.cs b/src/ComposeNet.Compose/ExtendedFloatingActionButton.cs index f636996a..aaeeb522 100644 --- a/src/ComposeNet.Compose/ExtendedFloatingActionButton.cs +++ b/src/ComposeNet.Compose/ExtendedFloatingActionButton.cs @@ -7,11 +7,10 @@ namespace ComposeNet; /// icon + text states. /// /// -/// new ExtendedFloatingActionButton(onClick: () => /* ... */) +/// new ExtendedFloatingActionButton(onClick: () => count.Value++, expanded: true) /// { -/// Icon = new Text("+"), -/// Text = new Text("Add"), -/// Expanded = true, +/// Icon = new Text("+"), +/// Text = new Text("Add"), /// } /// /// diff --git a/src/ComposeNet.Compose/LargeFloatingActionButton.cs b/src/ComposeNet.Compose/LargeFloatingActionButton.cs index f5c987d5..0d3b8228 100644 --- a/src/ComposeNet.Compose/LargeFloatingActionButton.cs +++ b/src/ComposeNet.Compose/LargeFloatingActionButton.cs @@ -7,7 +7,7 @@ namespace ComposeNet; /// child: /// /// -/// new LargeFloatingActionButton(onClick: () => /* ... */) +/// new LargeFloatingActionButton(onClick: () => count.Value++) /// { /// new Text("+"), /// } diff --git a/src/ComposeNet.Compose/SmallFloatingActionButton.cs b/src/ComposeNet.Compose/SmallFloatingActionButton.cs index 89068032..a57198e2 100644 --- a/src/ComposeNet.Compose/SmallFloatingActionButton.cs +++ b/src/ComposeNet.Compose/SmallFloatingActionButton.cs @@ -7,7 +7,7 @@ namespace ComposeNet; /// child: /// /// -/// new SmallFloatingActionButton(onClick: () => /* ... */) +/// new SmallFloatingActionButton(onClick: () => count.Value++) /// { /// new Text("+"), /// }