From 1607f14567d911a4b2e1bc35c52830aac6a57a74 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Fri, 12 Jun 2026 17:27:39 -0500 Subject: [PATCH] MAUI Phase 5 Slice 1: fix AndroidView fallback sizing for self-drawing views The original commit on this branch claimed every Phase 5 control worked unchanged through `ComposeWalker`'s `AndroidView { factory = view.ToPlatform(MauiContext) }` fallback. On-device verification on a Pixel 5 proved that wrong for self-drawing views: `MauiShapeView` and `PlatformGraphicsView` painted nothing - even forcing `platformView.SetBackgroundColor(Color.Magenta)` was invisible, which proves the View was getting a 0 x 0 layout slot. Root cause: the fallback never applied a Compose `Modifier.Size` (or `.Width`/`.Height`) derived from MAUI's `WidthRequest`/`HeightRequest`. Compose handed `AndroidView` an unbounded slot, the embedded View measured wrap-content, and self-drawing Views with no intrinsic size collapsed and their `Drawable` painted nothing. Compose-native handlers like `BoxViewHandler` already did this themselves via `IComposeHandler.BuildNode`; the fallback was missing the same mapping. Fix sits inside `ComposeWalker.Render`: read `WidthRequest`/`HeightRequest` off the `Microsoft.Maui.Controls.VisualElement` and translate to `Modifier.Size` / `Modifier.Width` / `Modifier.FillMaxWidth().Height(...)`, matching `BoxViewHandler`. No new public surface, no new handler. Sample pages reduced to the two with Compose-specific behaviour worth keeping as on-device reproducers for the bug: * `Pages/ShapesPage.xaml(.cs)` - all seven shape kinds (Rectangle / RoundRectangle / Ellipse / Line / Polygon / Polyline / Path) plus a fill-width Rectangle that exercises the `WidthRequest = -1, HeightRequest >= 0` branch. * `Pages/GraphicsViewPage.xaml(.cs)` - `IDrawable` paints a random zigzag plot; tap-to-reshuffle bumps the seed and `Invalidate()`s. The other three pages from the original slice (`WebViewPage`, `HybridWebViewPage`, `LayoutsPage`) are deleted - they exercised controls that already work via fallback (or already ship in working form across other sample pages, in the case of `Grid` / `AbsoluteLayout` / `FlexLayout` on `HomePage.xaml`) and didn't surface new Compose-specific behaviour worth the gallery slot. `docs/maui-backend.md` Phase 5 section rewritten to reflect the truth: per-control matrix updated (every shape + GraphicsView marked "works via fallback after the size-modifier fix"), `HybridWebView` moved to "deferred", verification matrix replaced with a concrete on-device verdict on a Pixel 5. Verified: * `dotnet test src/Microsoft.AndroidX.Compose.SourceGenerators.Tests` = 155 passed. * `dotnet build src/Microsoft.AndroidX.Compose` clean. * `dotnet build src/Microsoft.AndroidX.Compose.Maui` clean. * `dotnet build src/Microsoft.AndroidX.Compose.Maui.Sample` clean. * `dotnet build src/Microsoft.AndroidX.Compose.Maui.Sample -t:Install` clean. * `dotnet build src/Microsoft.AndroidX.Compose.Gallery` clean. * On-device on a Pixel 5: deployed, navigated to Shapes (all seven shape kinds + fill-width Rectangle render correctly) and to GraphicsView (canvas paints, tap-to-reshuffle redraws the plot and bumps the seed badge from 0 to 1). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/maui-backend.md | 152 ++++++++++++++++-- .../AppShell.xaml.cs | 7 + .../HomePage.xaml.cs | 20 +++ .../Pages/GraphicsViewPage.xaml | 45 ++++++ .../Pages/GraphicsViewPage.xaml.cs | 75 +++++++++ .../Pages/ShapesPage.xaml | 134 +++++++++++++++ .../Pages/ShapesPage.xaml.cs | 18 +++ .../ComposeWalker.cs | 37 ++++- 8 files changed, 471 insertions(+), 17 deletions(-) create mode 100644 src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/GraphicsViewPage.xaml create mode 100644 src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/GraphicsViewPage.xaml.cs create mode 100644 src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/ShapesPage.xaml create mode 100644 src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/ShapesPage.xaml.cs diff --git a/docs/maui-backend.md b/docs/maui-backend.md index 39c3fd97..c0bed059 100644 --- a/docs/maui-backend.md +++ b/docs/maui-backend.md @@ -1904,20 +1904,144 @@ graph + theming inherited from the page root. ### Phase 5 — graphics, gestures, shapes, BlazorWebView, infra -- `GraphicsViewHandler` → reuse MAUI's `PlatformGraphicsView` (Android - `View` subclass) inside an `AndroidView { ... }` composable, OR map - `ICanvas` to Compose's `Canvas` composable. -- `WebViewHandler` / `HybridWebViewHandler` → Android `WebView` inside - `AndroidView { ... }`. `BlazorWebViewHandler` already has an - Android-specific implementation in - `Microsoft.AspNetCore.Components.WebView.Maui` — reuse, just plug into - Compose's interop. -- Shapes (`Rectangle`/`Ellipse`/`Path`/...) → Compose `Canvas` drawing - via `DrawScope`. -- Gestures: MAUI's gesture recognizers already work on any Android - `View`. The challenge is `Tap`/`Pan`/`Pinch` competing with Compose's - own gesture detectors — `Modifier.PointerInput { }` may need to - forward gestures to MAUI's `GestureManager`. +#### Phase 5 Slice 1 — AndroidView fallback investigation ✅ shipped + +Earlier phases (and the Phase 2 Slice 2 single-`ComposeView`-per-page +refactor in particular) folded the only "real" Compose backend +behaviour we needed for Phase 5 into `ComposeWalker`'s default +branch. Anything whose handler is not registered as `IComposeHandler` +goes through + +```csharp +return new AndroidView(factory: _ => view.ToPlatform(mauiContext)) +{ + Modifier = sizeFromWidthHeightRequest, +}; +``` + +— MAUI's stock handler still produces an Android `View`, and Compose +hosts it as `androidx.compose.ui.viewinterop.AndroidView`. MAUI's +mapper (`ShapeViewHandler`, `GraphicsViewHandler`, `WebViewHandler`, +…) keeps owning the platform view's measure / arrange / draw / +lifecycle. + +##### Bug found and fixed in this slice + +The first cut of the investigation (the version that originally +landed on this branch) shipped without on-device verification and +claimed that **every** Phase 5 control worked unchanged via the +fallback. On-device verification in a follow-up pass proved that +wrong for self-drawing views: `MauiShapeView` and +`PlatformGraphicsView` painted nothing — even forcing +`platformView.SetBackgroundColor(Color.Magenta)` produced an +invisible View, proving zero size. + +Root cause: the fallback never applied a Compose `Modifier.Size` (or +`.Width` / `.Height`) derived from MAUI's `WidthRequest` / +`HeightRequest`. Without it Compose handed `AndroidView` an +unbounded slot, the embedded View measured at wrap-content, and +self-drawing Views with no intrinsic size collapsed to 0 × 0 and +their `Drawable` painted nothing. Compose-native handlers like +`BoxViewHandler` already do this themselves via +`IComposeHandler.BuildNode`; the fallback was missing the same +mapping. + +The fix sits entirely inside `ComposeWalker.Render` — read +`WidthRequest` / `HeightRequest` off the `VisualElement` and +translate to `Modifier.Size` / `Modifier.Width` / +`Modifier.FillMaxWidth().Height(...)` exactly the way +`BoxViewHandler` does. No new public surface, no new handler. + +##### Per-control matrix + +| Control | Handler today | Path | Verdict | +|---|---|---|---| +| `Microsoft.Maui.Controls.Shapes.Rectangle` / `RoundRectangle` / `Ellipse` / `Line` / `Polygon` / `Polyline` / `Path` | stock `ShapeViewHandler` → `MauiShapeView` (Android `View` painting via `android.graphics.Canvas`) | `AndroidView` fallback | **Works via fallback** after the size-modifier fix above. Verified on-device on a Pixel 5 (`Pages/ShapesPage.xaml`): all seven shape kinds render, including `LinearGradientBrush` fills and SVG `Path` data. The `Fill-width Rectangle` row at the bottom of the page additionally exercises the `WidthRequest = -1, HeightRequest >= 0` branch (`Modifier.FillMaxWidth().Height(...)`). | +| `Microsoft.Maui.Controls.GraphicsView` (+ `IDrawable`) | stock `GraphicsViewHandler` → `PlatformGraphicsView` (Android `View` over `Microsoft.Maui.Graphics`'s Android canvas) | `AndroidView` fallback | **Works via fallback** after the size-modifier fix. Verified on-device (`Pages/GraphicsViewPage.xaml`): `IDrawable.Draw` paints the canvas, and `Invalidate()` repaints — tap-to-reshuffle bumps the seed and the line plot redraws. | +| `Microsoft.Maui.Controls.WebView` | stock `WebViewHandler` → `MauiWebView : android.webkit.WebView` | `AndroidView` fallback | **Works via fallback.** Caller must give the cell explicit `HeightRequest` (a Compose `Column` doesn't constrain unbounded children). Sample page deleted from this slice — the WebView is rich enough to host its own scroll, and the AndroidView host has no Compose-specific interaction; left to consumer apps to verify against their own URLs. | +| `Microsoft.Maui.Controls.HybridWebView` | stock `HybridWebViewHandler` (MAUI 9+) | `AndroidView` fallback (expected) | **Deferred.** Spot-checked while building the slice; not part of the on-device verification matrix. The handler shape is identical to plain `WebView` so we expect parity, but we have not exercised `SendRawMessage` / `RawMessageReceived` end-to-end yet. | +| `BlazorWebView` (`Microsoft.AspNetCore.Components.WebView.Maui`) | Android-specific stock handler shipped by the Blazor WebView package | `AndroidView` fallback (consumer adds the package reference) | **Works via fallback by construction** — `BlazorWebViewHandler` derives from `ViewHandler`, produces an Android `WebView`, and registers itself through stock MAUI hosting. The Compose backend does not need to know about Blazor. Not exercised in the sample to avoid pulling the package as a hard dependency. | +| `Microsoft.Maui.Controls.Grid` / `AbsoluteLayout` / `FlexLayout` / `StackLayout` | stock `LayoutHandler` → `LayoutViewGroup` (already used by `HomePage.xaml` since Phase 1) | `AndroidView` fallback | **Works via fallback.** Children inside the layout resolve through MAUI's normal handler resolution — Compose-backed leaves degrade to per-leaf `ComposeView` islands (Phase 1 shape). Sample page deleted from this slice — these layouts already ship in working form across other sample pages (e.g. `HomePage.xaml`'s `Grid` chrome) and adding a dedicated demo doesn't surface new Compose-specific behaviour. The cross-sibling-animation / per-layout-theming improvement requires the Compose-native `Layout {}` adapter (#144 — see follow-ups). | + +##### What's intentionally not in Slice 1 + +- **Compose-native shape handlers via `Canvas` + `DrawScope`** — + parked behind issue #64 (drawing primitives binding). The + fallback paints correctly; lifting shapes into the page + composition only buys us cross-sibling animations between + shapes, which is a thin payoff against the size of #64 (medium- + high effort: `DrawScope` is ~30 hashed bridges over packed + inline-class params, plus a `Path` builder and `Brush` / + `Shape` factories). +- **Compose-native `GraphicsView` via `Canvas` + `IDrawable` → + `DrawScope` shim** — same trade-off as shapes, plus an extra + adapter to translate `Microsoft.Maui.Graphics.ICanvas` calls into + `DrawScope`. Out of scope until #64 ships. +- **Custom `Layout {}` adapter (#144)** — would let us register + `Grid`, `AbsoluteLayout`, `FlexLayout` as `IComposeHandler`s and + fold their measure-policy into the page composition (closing + the "per-leaf `ComposeView` island inside a fallback-hosted + layout" caveat above). Real bridge + generator work: bind + `androidx.compose.ui.layout.Layout`, `Measurable`, `Placeable`, + `MeasureScope`, `MeasureResult`, and the `PlacementScope` + DSL — out of scope for Slice 1, tracked by #144. +- **`HybridWebView` on-device verification** — handler shape is + identical to plain `WebView`; spot-check deferred to a + follow-up because it didn't surface new Compose-specific + behaviour worth the device time. +- **Hardening `Modifier.PointerInput` for MAUI `GestureManager` + hand-off** — gestures already work end-to-end through the + Phase 2 Slice 10 `GestureBridge` for Compose-backed leaves, and + through the embedded Android view's own touch system for + fallback leaves. No new work needed at the Phase 5 layer. + +**Delivered:** + +- `src/Microsoft.AndroidX.Compose.Maui/ComposeWalker.cs` — the + fallback path now reads `WidthRequest` / `HeightRequest` off the + MAUI `VisualElement` and applies a matching Compose `Modifier` + to the `AndroidView`. Internal change; no public-API delta. +- `src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/ShapesPage.xaml(.cs)` + + `GraphicsViewPage.xaml(.cs)` — on-device reproducers for the + bug above and proofs of the fix. +- `AppShell.xaml.cs` routes + `HomePage.xaml.cs` catalog entries + for those two pages. +- This investigation report. + +**Verified:** + +- `dotnet test src/Microsoft.AndroidX.Compose.SourceGenerators.Tests` + = 155 passed. +- `dotnet build src/Microsoft.AndroidX.Compose` clean. +- `dotnet build src/Microsoft.AndroidX.Compose.Maui` clean. +- `dotnet build src/Microsoft.AndroidX.Compose.Maui.Sample` clean + (zero warnings, zero errors). +- `dotnet build src/Microsoft.AndroidX.Compose.Gallery` clean. +- On-device on a Pixel 5: deployed, exercised `Pages/ShapesPage` + (all 7 shape kinds + fill-width Rectangle render) and + `Pages/GraphicsViewPage` (canvas paints, tap-to-reshuffle + invalidates and repaints). + +#### Phase 5 follow-ups (not in Slice 1) + +- `GraphicsViewHandler` Compose-native variant routing + `IDrawable.Draw` through a `Canvas { … }` composable wrapping + `DrawScope` (depends on #64). +- Compose-native shape handlers (Rectangle / Ellipse / Path / …) + via `DrawScope` (depends on #64). +- Custom `Layout {}` adapter (#144) → `GridHandler`, + `AbsoluteLayoutHandler`, `FlexLayoutHandler` that lift those + layouts into the page composition for cross-sibling animations + and per-layout theming. Most of the cost is the + `Measurable`/`Placeable`/`MeasureScope`/`PlacementScope` + bridge, not the per-layout adapter. +- `HybridWebView` on-device spot-check (parity with + `WebView` via the same fallback path expected, not yet + exercised). +- Gestures — MAUI's gesture recognizers already work on any + Android `View`. The Phase 2 Slice 10 `GestureBridge` covers the + Compose-folded leaves; fallback leaves use the embedded view's + own touch system. Nothing additional to do at Phase 5. ### Phase 6 — Essentials (parallel work, optional) diff --git a/src/Microsoft.AndroidX.Compose.Maui.Sample/AppShell.xaml.cs b/src/Microsoft.AndroidX.Compose.Maui.Sample/AppShell.xaml.cs index 9e4b1d87..14eeeefe 100644 --- a/src/Microsoft.AndroidX.Compose.Maui.Sample/AppShell.xaml.cs +++ b/src/Microsoft.AndroidX.Compose.Maui.Sample/AppShell.xaml.cs @@ -36,5 +36,12 @@ public AppShell() Routing.RegisterRoute("refresh", typeof(RefreshPage)); Routing.RegisterRoute("indicator", typeof(IndicatorPage)); Routing.RegisterRoute("semantics", typeof(SemanticsPage)); + + // Phase 5 — pages that exercise self-drawing AndroidView-hosted + // controls (Shapes, GraphicsView). Kept as on-device reproducers + // for the missing-`Modifier.Size` bug fixed in this slice. See + // docs/maui-backend.md (Phase 5). + Routing.RegisterRoute("shapes", typeof(ShapesPage)); + Routing.RegisterRoute("graphics-view", typeof(GraphicsViewPage)); } } diff --git a/src/Microsoft.AndroidX.Compose.Maui.Sample/HomePage.xaml.cs b/src/Microsoft.AndroidX.Compose.Maui.Sample/HomePage.xaml.cs index ff1a0196..740beda6 100644 --- a/src/Microsoft.AndroidX.Compose.Maui.Sample/HomePage.xaml.cs +++ b/src/Microsoft.AndroidX.Compose.Maui.Sample/HomePage.xaml.cs @@ -135,6 +135,26 @@ public HomePage() "SemanticProperties.Description / Hint / HeadingLevel + AutomationId routed to Compose `Modifier.Semantics { … }`.", Color.FromArgb("#3F51B5"), "semantics"), + + // ---- Phase 5 — self-drawing AndroidView fallback ---- + // + // These two entries exercise controls (`Shapes`, + // `GraphicsView`) whose stock Android view has no intrinsic + // size and so collapses to 0 × 0 inside Compose's `AndroidView` + // unless the fallback applies a `Modifier.Size` derived from + // `WidthRequest`/`HeightRequest`. Kept on the gallery as + // on-device reproducers for the bug fixed in this slice; see + // `docs/maui-backend.md` Phase 5 for the verdict matrix. + new DemoEntry( + "Shapes", + "Rectangle / Ellipse / Line / Polygon / Polyline / Path / RoundRectangle.", + Color.FromArgb("#7B1FA2"), + "shapes"), + new DemoEntry( + "GraphicsView", + "IDrawable canvas; tap to re-shuffle.", + Color.FromArgb("#00897B"), + "graphics-view"), }; } diff --git a/src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/GraphicsViewPage.xaml b/src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/GraphicsViewPage.xaml new file mode 100644 index 00000000..b607ce2e --- /dev/null +++ b/src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/GraphicsViewPage.xaml @@ -0,0 +1,45 @@ + + + + + + + + + + + diff --git a/src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/GraphicsViewPage.xaml.cs b/src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/GraphicsViewPage.xaml.cs new file mode 100644 index 00000000..01714fb3 --- /dev/null +++ b/src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/GraphicsViewPage.xaml.cs @@ -0,0 +1,75 @@ +namespace Microsoft.AndroidX.Compose.Maui.Sample.Pages; + +/// +/// GraphicsView demo — drives an through the +/// stock GraphicsViewHandler hosted by Compose +/// . Tapping the canvas +/// bumps a seed and re-shuffles the drawing, which exercises both +/// the fallback's repaint path and tap routing. +/// +public partial class GraphicsViewPage : ContentPage +{ + readonly SparkDrawable _drawable = new(); + + /// Build the page and seed the drawable. + public GraphicsViewPage() + { + InitializeComponent(); + GraphicsCanvas.Drawable = _drawable; + } + + void OnTap(object? sender, TouchEventArgs e) + { + _drawable.Seed++; + SeedLabel.Text = $"Seed: {_drawable.Seed} · Tap to re-shuffle"; + GraphicsCanvas.Invalidate(); + } + + /// + /// Synthetic drawable — random sparkline with a gradient-filled + /// circle on the right edge. Kept inside the page so the demo is + /// self-contained. + /// + sealed class SparkDrawable : IDrawable + { + public int Seed { get; set; } + + public void Draw(ICanvas canvas, RectF dirtyRect) + { + var random = new Random(Seed); + + // Background fill. + canvas.FillColor = Color.FromArgb("#FAFAFA"); + canvas.FillRectangle(dirtyRect); + + // Border. + canvas.StrokeColor = Color.FromArgb("#1A000000"); + canvas.StrokeSize = 1f; + canvas.DrawRectangle(dirtyRect); + + // Spark line — 12 random points across the width. + const int Points = 12; + float stepX = dirtyRect.Width / (Points - 1); + var path = new PathF(); + for (int i = 0; i < Points; i++) + { + float x = i * stepX; + float y = 20f + (float)random.NextDouble() * (dirtyRect.Height - 40f); + if (i == 0) path.MoveTo(x, y); + else path.LineTo(x, y); + } + canvas.StrokeColor = Color.FromArgb("#512BD4"); + canvas.StrokeSize = 3f; + canvas.DrawPath(path); + + // Right-edge circle. + float cx = dirtyRect.Right - 32f; + float cy = dirtyRect.Bottom - 32f; + canvas.FillColor = Color.FromArgb("#E91E63"); + canvas.FillCircle(cx, cy, 22f); + canvas.FontColor = Colors.White; + canvas.FontSize = 14f; + canvas.DrawString(Seed.ToString(), cx - 10f, cy - 8f, 20f, 16f, HorizontalAlignment.Center, VerticalAlignment.Center); + } + } +} diff --git a/src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/ShapesPage.xaml b/src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/ShapesPage.xaml new file mode 100644 index 00000000..d0ca52cc --- /dev/null +++ b/src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/ShapesPage.xaml @@ -0,0 +1,134 @@ + + + + + + + + + + + diff --git a/src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/ShapesPage.xaml.cs b/src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/ShapesPage.xaml.cs new file mode 100644 index 00000000..120fbe39 --- /dev/null +++ b/src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/ShapesPage.xaml.cs @@ -0,0 +1,18 @@ +namespace Microsoft.AndroidX.Compose.Maui.Sample.Pages; + +/// +/// Shapes demo — drops every concrete Microsoft.Maui.Controls.Shapes +/// subclass into a Compose-backed page to verify the +/// fallback path round-trips +/// MAUI's stock ShapeViewHandler + MauiShapeView. No +/// code-behind — the demo intentionally stays declarative so the visuals +/// stand or fall on the fallback alone. +/// +public partial class ShapesPage : ContentPage +{ + /// Build the page. + public ShapesPage() + { + InitializeComponent(); + } +} diff --git a/src/Microsoft.AndroidX.Compose.Maui/ComposeWalker.cs b/src/Microsoft.AndroidX.Compose.Maui/ComposeWalker.cs index 051b94c9..68f3f3d6 100644 --- a/src/Microsoft.AndroidX.Compose.Maui/ComposeWalker.cs +++ b/src/Microsoft.AndroidX.Compose.Maui/ComposeWalker.cs @@ -62,8 +62,39 @@ public static ComposableNode Render(IView view, IComposer composer, IMauiContext handler = view.Handler; } - return handler is IComposeHandler compose - ? compose.BuildNode(composer) - : new AndroidView(_ => view.ToPlatform(mauiContext)); + if (handler is IComposeHandler compose) + { + return compose.BuildNode(composer); + } + + // Stock-handler fallback: host the platform view inside Compose's + // `AndroidView { … }` interop. We have to apply a Compose + // `Modifier.Size` (or `Width`/`Height`) from the MAUI virtual + // view's `WidthRequest`/`HeightRequest` ourselves — without it + // Compose hands the AndroidView an unbounded slot and the hosted + // Android `View` measures wrap-content. For self-drawing MAUI + // views with no intrinsic size (`MauiShapeView`, + // `PlatformGraphicsView`) that collapses to 0×0 and the canvas + // paints nothing even though MAUI's mapper (`ShapeViewHandler`, + // `GraphicsViewHandler`) ran during `ToPlatform()` and set up a + // `Drawable`. Compose-aware handlers (`BoxViewHandler`, + // `LabelHandler`, …) apply this themselves via + // `IComposeHandler.BuildNode`; the fallback covers everyone else. + Modifier? modifier = null; + if (view is Microsoft.Maui.Controls.VisualElement ve) + { + modifier = (ve.WidthRequest, ve.HeightRequest) switch + { + ( >= 0d, >= 0d ) => Modifier.Size(new Dp((float)ve.WidthRequest), new Dp((float)ve.HeightRequest)), + ( >= 0d, _ ) => Modifier.Width(new Dp((float)ve.WidthRequest)), + ( _, >= 0d ) => Modifier.FillMaxWidth().Height(new Dp((float)ve.HeightRequest)), + _ => null, + }; + } + + return new AndroidView(factory: _ => view.ToPlatform(mauiContext)) + { + Modifier = modifier, + }; } }