pythonnative 0.32.0__py3-none-any.whl → 0.34.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. pythonnative/__init__.py +27 -15
  2. pythonnative/animated.py +7 -13
  3. pythonnative/cli/pn.py +1 -2
  4. pythonnative/component.py +255 -0
  5. pythonnative/components/__init__.py +106 -0
  6. pythonnative/components/_base.py +132 -0
  7. pythonnative/components/controls.py +515 -0
  8. pythonnative/components/layout.py +459 -0
  9. pythonnative/components/lists.py +809 -0
  10. pythonnative/components/media.py +204 -0
  11. pythonnative/components/overlays.py +107 -0
  12. pythonnative/components/pressable.py +252 -0
  13. pythonnative/components/structural.py +167 -0
  14. pythonnative/components/text.py +291 -0
  15. pythonnative/diagnostics.py +1 -1
  16. pythonnative/element.py +108 -29
  17. pythonnative/gestures.py +1 -1
  18. pythonnative/hooks.py +366 -671
  19. pythonnative/hosts/__init__.py +85 -0
  20. pythonnative/hosts/android.py +269 -0
  21. pythonnative/hosts/base.py +665 -0
  22. pythonnative/hosts/desktop.py +107 -0
  23. pythonnative/hosts/ios.py +404 -0
  24. pythonnative/hot_reload.py +16 -24
  25. pythonnative/layout.py +372 -66
  26. pythonnative/native_modules/__init__.py +23 -0
  27. pythonnative/native_modules/net_info.py +5 -1
  28. pythonnative/native_views/__init__.py +1 -1
  29. pythonnative/native_views/android.py +50 -6
  30. pythonnative/native_views/base.py +64 -1
  31. pythonnative/native_views/desktop.py +11 -3
  32. pythonnative/native_views/ios.py +72 -12
  33. pythonnative/navigation/__init__.py +102 -0
  34. pythonnative/navigation/container.py +154 -0
  35. pythonnative/navigation/handle.py +573 -0
  36. pythonnative/navigation/hooks.py +94 -0
  37. pythonnative/navigation/host.py +58 -0
  38. pythonnative/navigation/linking.py +200 -0
  39. pythonnative/navigation/navigators.py +637 -0
  40. pythonnative/navigation/screen.py +149 -0
  41. pythonnative/navigation/state.py +248 -0
  42. pythonnative/net.py +4 -0
  43. pythonnative/platform_metrics.py +1 -1
  44. pythonnative/preview.py +21 -19
  45. pythonnative/project/android.py +7 -1
  46. pythonnative/project/doctor.py +1 -1
  47. pythonnative/project/ios.py +1 -0
  48. pythonnative/project/runtime_assets.py +1 -1
  49. pythonnative/reconciler/__init__.py +29 -0
  50. pythonnative/reconciler/boundaries.py +365 -0
  51. pythonnative/reconciler/children.py +88 -0
  52. pythonnative/reconciler/core.py +1153 -0
  53. pythonnative/reconciler/layout_pass.py +359 -0
  54. pythonnative/reconciler/vnode.py +266 -0
  55. pythonnative/scheduler.py +159 -0
  56. pythonnative/sdk/_components.py +2 -4
  57. pythonnative/style.py +95 -13
  58. pythonnative/suspense.py +8 -12
  59. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +85 -1
  60. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +6 -7
  61. pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +1 -1
  62. pythonnative/templates/android_template/app/src/main/res/values/strings.xml +3 -1
  63. pythonnative/templates/ios_template/ios_template/AppDelegate.swift +33 -0
  64. pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +1 -1
  65. pythonnative/templates/ios_template/ios_template/ViewController.swift +12 -10
  66. pythonnative/testing/__init__.py +53 -0
  67. pythonnative/testing/backend.py +276 -0
  68. pythonnative/testing/harness.py +391 -0
  69. {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/METADATA +2 -21
  70. {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/RECORD +74 -43
  71. pythonnative/components.py +0 -2856
  72. pythonnative/navigation.py +0 -1031
  73. pythonnative/reconciler.py +0 -2298
  74. pythonnative/screen.py +0 -2068
  75. {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/WHEEL +0 -0
  76. {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/entry_points.txt +0 -0
  77. {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/licenses/LICENSE +0 -0
  78. {pythonnative-0.32.0.dist-info → pythonnative-0.34.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,459 @@
1
+ """Container factories: ``View``, ``Column``, ``Row``, ``Spacer``, ``ScrollView``, and the inset-aware wrappers.
2
+
3
+ ``SafeAreaView`` and ``KeyboardAvoidingView`` are thin factories over
4
+ hook-driven composites (``_SafeAreaContainer`` and
5
+ ``_KeyboardAvoidingContainer``) that subscribe to the platform metrics
6
+ and re-render when the insets or keyboard height change.
7
+ """
8
+
9
+ from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, Union
10
+
11
+ from ..component import component
12
+ from ..element import Element
13
+ from ..hooks import Ref, use_keyboard_height, use_safe_area_insets, use_state
14
+ from ..style import AccessibilityState, StyleProp, resolve_style
15
+ from ._base import _make_element
16
+
17
+
18
+ def View(
19
+ *children: Element,
20
+ style: StyleProp = None,
21
+ gestures: Optional[List[Any]] = None,
22
+ hit_slop: Optional[Union[float, Dict[str, float]]] = None,
23
+ on_layout: Optional[Callable[[Dict[str, float]], None]] = None,
24
+ accessibility_label: Optional[str] = None,
25
+ accessibility_hint: Optional[str] = None,
26
+ accessibility_role: Optional[str] = None,
27
+ accessible: Optional[bool] = None,
28
+ accessibility_state: Optional[AccessibilityState] = None,
29
+ accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
30
+ test_id: Optional[str] = None,
31
+ ref: Optional[Ref] = None,
32
+ key: Optional[str] = None,
33
+ ) -> Element:
34
+ """Universal flex container (like React Native's ``View``).
35
+
36
+ Defaults to ``flex_direction: "column"`` (override via ``style``).
37
+
38
+ Flex container properties (passed via ``style``):
39
+
40
+ - ``flex_direction``: ``"column"`` (default), ``"row"``,
41
+ ``"column_reverse"``, ``"row_reverse"``.
42
+ - ``flex_wrap``: ``"nowrap"`` (default), ``"wrap"``,
43
+ ``"wrap_reverse"``, with ``align_content`` controlling how
44
+ wrapped lines share leftover cross-axis space.
45
+ - ``justify_content``: main-axis distribution. Accepts
46
+ ``"flex_start"`` (default), ``"center"``, ``"flex_end"``,
47
+ ``"space_between"``, ``"space_around"``, ``"space_evenly"``.
48
+ - ``align_items``: cross-axis alignment. Accepts ``"stretch"``
49
+ (default), ``"flex_start"``, ``"center"``, ``"flex_end"``.
50
+ - ``direction``: ``"ltr"`` (default) or ``"rtl"``. Flips rows and
51
+ resolves ``margin_start`` / ``padding_end`` / absolute ``start``
52
+ / ``end`` insets.
53
+ - ``overflow``: ``"visible"`` (default) or ``"hidden"``.
54
+ - ``spacing`` (alias ``gap``; per-axis ``row_gap`` /
55
+ ``column_gap``), ``padding``, ``background_color``,
56
+ ``border_radius``, ``border_width``, ``border_color``,
57
+ ``shadow_color``, ``shadow_offset``, ``shadow_opacity``,
58
+ ``shadow_radius``, ``elevation``, ``opacity``, ``transform``.
59
+
60
+ Args:
61
+ *children: Child elements rendered inside the container.
62
+ style: Style dict (or list of dicts).
63
+ gestures: Optional list of gesture descriptors from
64
+ `pythonnative.gestures` (e.g. ``[gestures.Pan(on_change=…)]``)
65
+ recognized natively on this view.
66
+ hit_slop: Extend the touch target beyond the view's bounds
67
+ without changing layout: a uniform number of points, or a
68
+ dict with any of ``top`` / ``left`` / ``bottom`` /
69
+ ``right``.
70
+ on_layout: Callback invoked with
71
+ ``{"x", "y", "width", "height"}`` after this view is laid
72
+ out, and again whenever its frame changes.
73
+ accessibility_label: Spoken description for screen readers.
74
+ accessibility_hint: Spoken extra detail (iOS only).
75
+ accessibility_role: Semantic role for assistive tech.
76
+ accessible: Override whether the element is exposed to AT.
77
+ accessibility_state: Current widget state for assistive tech,
78
+ e.g. ``{"disabled": True, "selected": False}``. Recognized
79
+ keys: ``disabled``, ``selected``, ``checked``, ``busy``,
80
+ ``expanded``.
81
+ accessibility_live_region: How AT announces dynamic changes to
82
+ this view: ``"none"``, ``"polite"``, or ``"assertive"``
83
+ (Android only).
84
+ test_id: Stable identifier for UI tests; exposed as
85
+ ``resource-id`` on Android and ``accessibilityIdentifier``
86
+ on iOS.
87
+ ref: Optional [`Ref`][pythonnative.Ref] from ``use_ref()``.
88
+ key: Stable identity for keyed reconciliation.
89
+
90
+ Returns:
91
+ An [`Element`][pythonnative.Element] of type ``"View"``.
92
+ """
93
+ return _make_element(
94
+ "View",
95
+ *children,
96
+ style=style,
97
+ ref=ref,
98
+ key=key,
99
+ gestures=gestures,
100
+ hit_slop=hit_slop,
101
+ on_layout=on_layout,
102
+ accessibility_label=accessibility_label,
103
+ accessibility_hint=accessibility_hint,
104
+ accessibility_role=accessibility_role,
105
+ accessible=accessible,
106
+ accessibility_state=accessibility_state,
107
+ accessibility_live_region=accessibility_live_region,
108
+ test_id=test_id,
109
+ _defaults={"flex_direction": "column"},
110
+ )
111
+
112
+
113
+ def Column(
114
+ *children: Element,
115
+ style: StyleProp = None,
116
+ on_layout: Optional[Callable[[Dict[str, float]], None]] = None,
117
+ ref: Optional[Ref] = None,
118
+ key: Optional[str] = None,
119
+ ) -> Element:
120
+ """Arrange children vertically.
121
+
122
+ Convenience wrapper around [`View`][pythonnative.View] with
123
+ ``flex_direction`` locked to ``"column"``. Use ``View`` directly if
124
+ you need to switch between row and column at runtime.
125
+
126
+ Args:
127
+ *children: Child elements stacked top to bottom.
128
+ style: Style dict (or list of dicts).
129
+ on_layout: Callback invoked with
130
+ ``{"x", "y", "width", "height"}`` after layout and on
131
+ frame changes.
132
+ ref: Optional [`Ref`][pythonnative.Ref] for native-view access.
133
+ key: Stable identity for keyed reconciliation.
134
+
135
+ Returns:
136
+ An [`Element`][pythonnative.Element] of type ``"Column"``.
137
+ """
138
+ return _make_element(
139
+ "Column",
140
+ *children,
141
+ style=style,
142
+ on_layout=on_layout,
143
+ ref=ref,
144
+ key=key,
145
+ _forced={"flex_direction": "column"},
146
+ )
147
+
148
+
149
+ def Row(
150
+ *children: Element,
151
+ style: StyleProp = None,
152
+ on_layout: Optional[Callable[[Dict[str, float]], None]] = None,
153
+ ref: Optional[Ref] = None,
154
+ key: Optional[str] = None,
155
+ ) -> Element:
156
+ """Arrange children horizontally.
157
+
158
+ Convenience wrapper around [`View`][pythonnative.View] with
159
+ ``flex_direction`` locked to ``"row"``. Use ``View`` directly if you
160
+ need to switch between row and column at runtime.
161
+
162
+ Args:
163
+ *children: Child elements arranged left to right.
164
+ style: Style dict (or list of dicts).
165
+ on_layout: Callback invoked with
166
+ ``{"x", "y", "width", "height"}`` after layout and on
167
+ frame changes.
168
+ ref: Optional [`Ref`][pythonnative.Ref] for native-view access.
169
+ key: Stable identity for keyed reconciliation.
170
+
171
+ Returns:
172
+ An [`Element`][pythonnative.Element] of type ``"Row"``.
173
+ """
174
+ return _make_element(
175
+ "Row",
176
+ *children,
177
+ style=style,
178
+ on_layout=on_layout,
179
+ ref=ref,
180
+ key=key,
181
+ _forced={"flex_direction": "row"},
182
+ )
183
+
184
+
185
+ def Spacer(
186
+ *,
187
+ size: Optional[float] = None,
188
+ flex: Optional[float] = None,
189
+ key: Optional[str] = None,
190
+ ) -> Element:
191
+ """Insert empty space inside a flex container.
192
+
193
+ Pass ``size`` for a fixed gap, or ``flex`` to expand and absorb
194
+ remaining space.
195
+
196
+ Args:
197
+ size: Fixed gap in dp/pt along the parent's main axis. Mirrored
198
+ on both axes: whichever axis the parent's
199
+ ``flex_direction`` chooses as main becomes the actual gap.
200
+ flex: Flex-grow weight; useful for pushing siblings to the
201
+ opposite end of a [`Row`][pythonnative.Row] or
202
+ [`Column`][pythonnative.Column].
203
+ key: Stable identity for keyed reconciliation.
204
+
205
+ Returns:
206
+ An [`Element`][pythonnative.Element] of type ``"Spacer"``.
207
+ """
208
+ width = size if size is not None else None
209
+ height = size if size is not None else None
210
+ return _make_element(
211
+ "Spacer",
212
+ key=key,
213
+ size=size,
214
+ width=width,
215
+ height=height,
216
+ flex=flex,
217
+ )
218
+
219
+
220
+ def ScrollView(
221
+ *children: Element,
222
+ refresh_control: Optional[Dict[str, Any]] = None,
223
+ scroll_axis: Optional[Literal["vertical", "horizontal"]] = None,
224
+ on_scroll: Optional[Callable[[Dict[str, float]], None]] = None,
225
+ shows_scroll_indicator: bool = True,
226
+ paging_enabled: bool = False,
227
+ bounces: bool = True,
228
+ content_container_style: StyleProp = None,
229
+ keyboard_dismiss_mode: Optional[Literal["none", "on_drag", "interactive"]] = None,
230
+ style: StyleProp = None,
231
+ ref: Optional[Ref] = None,
232
+ key: Optional[str] = None,
233
+ ) -> Element:
234
+ """Wrap children in a scrollable container.
235
+
236
+ ``ScrollView`` typically takes a single child (a ``Column`` or
237
+ ``Row`` aggregating the scrollable content). It accepts ``*children``
238
+ for ergonomic call sites; the underlying native scroll view stacks
239
+ them on its content axis.
240
+
241
+ Args:
242
+ *children: Child elements to scroll.
243
+ refresh_control: Optional pull-to-refresh spec, typically
244
+ constructed via
245
+ [`RefreshControl`][pythonnative.RefreshControl]. The dict
246
+ must have ``refreshing`` (bool) and ``on_refresh``
247
+ (callable).
248
+ scroll_axis: ``"vertical"`` (default) or ``"horizontal"``.
249
+ on_scroll: Callback invoked with ``{"x": …, "y": …}`` content
250
+ offsets as the user scrolls.
251
+ shows_scroll_indicator: When ``False``, hides the scroll bar.
252
+ paging_enabled: When ``True``, the scroll view snaps to
253
+ multiples of its own size (carousel behavior).
254
+ bounces: When ``False``, disables the iOS rubber-band overscroll.
255
+ content_container_style: Style applied to the inner content
256
+ wrapper (padding, alignment, spacing of the scrollable
257
+ content), distinct from ``style`` (the scroll view frame).
258
+ keyboard_dismiss_mode: ``"none"`` (default), ``"on_drag"``, or
259
+ ``"interactive"``. Controls whether scrolling dismisses
260
+ the keyboard.
261
+ style: Style dict (or list of dicts).
262
+ ref: Optional [`Ref`][pythonnative.Ref] from ``use_ref()``.
263
+ key: Stable identity for keyed reconciliation.
264
+
265
+ Returns:
266
+ An [`Element`][pythonnative.Element] of type ``"ScrollView"``.
267
+ """
268
+ return _make_element(
269
+ "ScrollView",
270
+ *children,
271
+ style=style,
272
+ ref=ref,
273
+ key=key,
274
+ refresh_control=refresh_control,
275
+ scroll_axis=scroll_axis,
276
+ on_scroll=on_scroll,
277
+ shows_scroll_indicator=False if shows_scroll_indicator is False else None,
278
+ paging_enabled=paging_enabled or None,
279
+ bounces=False if bounces is False else None,
280
+ content_container_style=resolve_style(content_container_style) or None,
281
+ keyboard_dismiss_mode=keyboard_dismiss_mode,
282
+ )
283
+
284
+
285
+ # ======================================================================
286
+ # SafeAreaView
287
+ # ======================================================================
288
+
289
+ _SAFE_AREA_EDGES: Tuple[str, ...] = ("top", "left", "bottom", "right")
290
+
291
+
292
+ def _numeric_edge_padding(style: Dict[str, Any], edge: str) -> float:
293
+ """Return the numeric padding already declared for ``edge`` in ``style``.
294
+
295
+ Only numeric values participate; percentage strings and dict
296
+ shorthands are left alone (the inset simply overrides them for
297
+ that edge). Resolution order matches the layout engine:
298
+ ``padding_{edge}`` beats the axis shorthand, which beats
299
+ ``padding``.
300
+ """
301
+ axis_key = "padding_vertical" if edge in ("top", "bottom") else "padding_horizontal"
302
+ for key in (f"padding_{edge}", axis_key, "padding"):
303
+ value = style.get(key)
304
+ if isinstance(value, (int, float)) and not isinstance(value, bool):
305
+ return float(value)
306
+ return 0.0
307
+
308
+
309
+ @component
310
+ def _SafeAreaContainer(
311
+ *children: Element,
312
+ edges: Optional[Tuple[str, ...]] = None,
313
+ style: Optional[Dict[str, Any]] = None,
314
+ ) -> Element:
315
+ """Hook-driven body of `SafeAreaView`.
316
+
317
+ Reads the live insets via
318
+ [`use_safe_area_insets`][pythonnative.use_safe_area_insets] (so the
319
+ subtree re-renders when the platform publishes new values, e.g. on
320
+ rotation) and adds each selected edge's inset on top of any padding
321
+ the user declared for that edge.
322
+ """
323
+ insets = use_safe_area_insets()
324
+ resolved: Dict[str, Any] = dict(style or {})
325
+ selected = edges or _SAFE_AREA_EDGES
326
+ for edge in _SAFE_AREA_EDGES:
327
+ if edge not in selected:
328
+ continue
329
+ inset = float(insets.get(edge, 0.0) or 0.0)
330
+ if inset > 0:
331
+ resolved[f"padding_{edge}"] = _numeric_edge_padding(resolved, edge) + inset
332
+ return Element("SafeAreaView", resolved, list(children))
333
+
334
+
335
+ def SafeAreaView(
336
+ *children: Element,
337
+ edges: Optional[Tuple[Literal["top", "left", "bottom", "right"], ...]] = None,
338
+ style: StyleProp = None,
339
+ key: Optional[str] = None,
340
+ ) -> Element:
341
+ """Container that respects safe-area insets (notch, status bar, home indicator).
342
+
343
+ Applies the platform-reported insets as extra padding on the
344
+ selected edges and re-renders automatically when the insets change
345
+ (rotation, split view). User padding on an inset edge is added to
346
+ the inset, matching ``react-native-safe-area-context``.
347
+
348
+ Args:
349
+ *children: Child elements that should avoid system UI overlays.
350
+ edges: Which edges to pad; defaults to all four.
351
+ style: Style dict (or list of dicts).
352
+ key: Stable identity for keyed reconciliation.
353
+
354
+ Returns:
355
+ An [`Element`][pythonnative.Element] that renders a
356
+ ``"SafeAreaView"`` container.
357
+ """
358
+ return _SafeAreaContainer(
359
+ *children,
360
+ edges=tuple(edges) if edges else None,
361
+ style=resolve_style(style),
362
+ ).with_key(key)
363
+
364
+
365
+ # ======================================================================
366
+ # KeyboardAvoidingView
367
+ # ======================================================================
368
+
369
+
370
+ @component
371
+ def _KeyboardAvoidingContainer(
372
+ *children: Element,
373
+ behavior: Literal["padding", "position", "height"] = "padding",
374
+ keyboard_vertical_offset: float = 0.0,
375
+ style: Optional[Dict[str, Any]] = None,
376
+ ) -> Element:
377
+ """Hook-driven body of `KeyboardAvoidingView`.
378
+
379
+ Subscribes to the platform-reported keyboard height via
380
+ [`use_keyboard_height`][pythonnative.use_keyboard_height] and
381
+ applies the shift according to ``behavior``:
382
+
383
+ - ``"padding"``: adds the shift as bottom padding, resizing the
384
+ content area (the default, and the right choice for forms
385
+ inside a full-height container).
386
+ - ``"position"``: translates the whole container upward without
387
+ resizing it (useful for pinned footers/toolbars).
388
+ - ``"height"``: shrinks the container's own height by the shift.
389
+ The resting height is captured via ``on_layout`` while the
390
+ keyboard is hidden, so flex-sized containers work too.
391
+ """
392
+ keyboard = use_keyboard_height()
393
+ base_height, set_base_height = use_state(0.0)
394
+ mode = behavior or "padding"
395
+ offset = float(keyboard_vertical_offset or 0.0)
396
+ shift = max(0.0, keyboard - offset) if keyboard > 0 else 0.0
397
+ resolved: Dict[str, Any] = dict(style or {})
398
+ props: Dict[str, Any] = resolved
399
+ if mode == "height":
400
+
401
+ def _record_layout(frame: Dict[str, float]) -> None:
402
+ if keyboard <= 0:
403
+ measured = float(frame.get("height", 0.0))
404
+ if measured > 0 and abs(measured - base_height) > 0.5:
405
+ set_base_height(measured)
406
+
407
+ props = dict(resolved)
408
+ props["on_layout"] = _record_layout
409
+ if shift > 0 and base_height > 0:
410
+ props["height"] = max(0.0, base_height - shift)
411
+ elif shift > 0:
412
+ if mode == "position":
413
+ transform = list(resolved.get("transform") or [])
414
+ transform.append({"translate_y": -shift})
415
+ resolved["transform"] = transform
416
+ else:
417
+ resolved["padding_bottom"] = _numeric_edge_padding(resolved, "bottom") + shift
418
+ return Element("KeyboardAvoidingView", props, list(children))
419
+
420
+
421
+ def KeyboardAvoidingView(
422
+ *children: Element,
423
+ behavior: Literal["padding", "position", "height"] = "padding",
424
+ keyboard_vertical_offset: float = 0.0,
425
+ style: StyleProp = None,
426
+ key: Optional[str] = None,
427
+ ) -> Element:
428
+ """Wrap content that should shift up when the keyboard is shown.
429
+
430
+ Subscribes to the platform-reported keyboard height (via
431
+ [`use_keyboard_height`][pythonnative.use_keyboard_height]
432
+ internally) and shifts its content so the focused text input stays
433
+ visible. On iOS the height comes from
434
+ ``UIKeyboardWillShowNotification``; on Android from the window's
435
+ IME insets.
436
+
437
+ Args:
438
+ *children: Children rendered inside the avoiding container.
439
+ behavior: ``"padding"`` (adds bottom padding, resizing the
440
+ content), ``"position"`` (translates the container upward
441
+ without resizing), or ``"height"`` (shrinks the
442
+ container's height by the keyboard overlap, matching
443
+ React Native's ``"height"`` behavior).
444
+ keyboard_vertical_offset: Distance in layout units already
445
+ covered by other UI (e.g. a nav bar); subtracted from the
446
+ keyboard height before applying the shift.
447
+ style: Style dict (or list of dicts).
448
+ key: Stable identity for keyed reconciliation.
449
+
450
+ Returns:
451
+ An [`Element`][pythonnative.Element] that renders a
452
+ ``"KeyboardAvoidingView"`` container.
453
+ """
454
+ return _KeyboardAvoidingContainer(
455
+ *children,
456
+ behavior=behavior,
457
+ keyboard_vertical_offset=keyboard_vertical_offset,
458
+ style=resolve_style(style),
459
+ ).with_key(key)