diff --git a/src/Avalonia.Controls/InputPaneAwareBehavior.cs b/src/Avalonia.Controls/InputPaneAwareBehavior.cs
new file mode 100644
index 0000000000..2d02371338
--- /dev/null
+++ b/src/Avalonia.Controls/InputPaneAwareBehavior.cs
@@ -0,0 +1,9 @@
+namespace Avalonia.Controls
+{
+ public enum InputPaneAwareBehavior
+ {
+ None = 0,
+ Pan,
+ Resize
+ }
+}
diff --git a/src/Avalonia.Controls/InputPaneAwareView.cs b/src/Avalonia.Controls/InputPaneAwareView.cs
new file mode 100644
index 0000000000..e9209a3bed
--- /dev/null
+++ b/src/Avalonia.Controls/InputPaneAwareView.cs
@@ -0,0 +1,147 @@
+using Avalonia.Animation;
+using Avalonia.Animation.Easings;
+using Avalonia.Controls.Platform;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// A control that automatically adjusts its child position or height based on the input pane's height to ensure the content is visible.
+ ///
+ public class InputPaneAwareView : Decorator
+ {
+ private InputPaneAwareBehavior _behavior;
+ private IInputPane? _inputPane;
+ private bool _firstLayoutDone;
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty BehaviorProperty = AvaloniaProperty.RegisterDirect(
+ nameof(Behavior),
+ o => o.Behavior,
+ (o, x) => o.Behavior = x);
+
+ private static readonly StyledProperty CurrentInputPanePaddingProperty = AvaloniaProperty.Register(
+ nameof(CurrentInputPanePadding), coerce: (o, x) =>
+ {
+ return x;
+ });
+
+ ///
+ /// Gets or sets how the view reacts to the input pane state.
+ ///
+ public InputPaneAwareBehavior Behavior
+ {
+ get => _behavior;
+ set => SetAndRaise(BehaviorProperty, ref _behavior, value);
+ }
+
+ private double CurrentInputPanePadding
+ {
+ get => GetValue(CurrentInputPanePaddingProperty);
+ set => SetValue(CurrentInputPanePaddingProperty, value);
+ }
+
+ static InputPaneAwareView()
+ {
+ AffectsMeasure(BehaviorProperty, CurrentInputPanePaddingProperty);
+ AffectsArrange(CurrentInputPanePaddingProperty);
+ }
+
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ if (_inputPane != null && _firstLayoutDone && VisualRoot is { } root && Behavior == InputPaneAwareBehavior.Resize)
+ {
+ SetCurrentValue(PaddingProperty, new Thickness(0, 0, 0, CurrentInputPanePadding));
+ }
+ return base.MeasureOverride(availableSize);
+ }
+
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ _firstLayoutDone = true;
+
+ if (Behavior == InputPaneAwareBehavior.Pan)
+ {
+ Child?.Arrange(new Rect(new Point(0, -CurrentInputPanePadding), finalSize));
+
+ return finalSize;
+ }
+ else
+ return base.ArrangeOverride(finalSize);
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+
+ _inputPane = TopLevel.GetTopLevel(this)?.InputPane;
+ _inputPane?.StateChanged += InputPaneAwareView_StateChanged;
+ EnsureInputPanePaddingApplied();
+ }
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+ _inputPane?.StateChanged -= InputPaneAwareView_StateChanged;
+ _inputPane = null;
+ SetCurrentValue(PaddingProperty, default);
+#pragma warning disable AVP1012 // An AvaloniaObject should use SetCurrentValue when assigning its own StyledProperty or AttachedProperty values
+ CurrentInputPanePadding = 0;
+#pragma warning restore AVP1012 // An AvaloniaObject should use SetCurrentValue when assigning its own StyledProperty or AttachedProperty values
+ }
+
+ private void EnsureInputPanePaddingApplied()
+ {
+ SetCurrentValue(PaddingProperty, default);
+
+ if (_inputPane != null && _firstLayoutDone && this.VisualRoot is { } root && Behavior != InputPaneAwareBehavior.None)
+ {
+ var occludedRect = _inputPane.OccludedRect;
+
+ var transformMatrix = this.TransformToVisual(root) ?? Matrix.Identity;
+
+ var translatedRect = Bounds.TransformToAABB(transformMatrix);
+
+ var intersect = occludedRect.Intersect(translatedRect);
+
+#pragma warning disable AVP1012 // An AvaloniaObject should use SetCurrentValue when assigning its own StyledProperty or AttachedProperty values
+ CurrentInputPanePadding = intersect.Height;
+ }
+ else
+ {
+ CurrentInputPanePadding = 0;
+ }
+#pragma warning restore AVP1012 // An AvaloniaObject should use SetCurrentValue when assigning its own StyledProperty or AttachedProperty values
+ }
+
+ private void InputPaneAwareView_StateChanged(object? sender, InputPaneStateEventArgs e)
+ {
+ var transition = new DoubleTransition()
+ {
+ Property = CurrentInputPanePaddingProperty,
+ Duration = e.AnimationDuration,
+ Easing = e.Easing as Easing ?? new LinearEasing()
+ };
+
+ Transitions =
+ [
+ transition
+ ];
+
+ EnsureInputPanePaddingApplied();
+
+ InvalidateMeasure();
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if(change.Property == BehaviorProperty)
+ {
+ EnsureInputPanePaddingApplied();
+ }
+ }
+ }
+}