| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 81fb760 commit 186bba6
42 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,30 @@ | |||
| 1 | + # Alerts | ||
| 2 | + | ||
| 3 | + The [`Alert`][pythonnative.Alert] class provides imperative access to | ||
| 4 | + the host platform's alert dialogs and action sheets. Alerts are *not* | ||
| 5 | + part of the element tree — they're fire-and-forget calls that present | ||
| 6 | + a native dialog and dispatch button callbacks. | ||
| 7 | + | ||
| 8 | + ::: pythonnative.alerts | ||
| 9 | + options: | ||
| 10 | + show_root_heading: false | ||
| 11 | + show_root_toc_entry: false | ||
| 12 | + members_order: source | ||
| 13 | + filters: ["!^_"] | ||
| 14 | + | ||
| 15 | + ## Patterns | ||
| 16 | + | ||
| 17 | + - **Confirm before destructive actions**: pair a `"destructive"` | ||
| 18 | + button with a `"cancel"` button via | ||
| 19 | + [`Alert.confirm`][pythonnative.alerts.Alert.confirm]. | ||
| 20 | + - **Action sheets**: pass `style="action_sheet"` to render an iOS-style | ||
| 21 | + bottom sheet; on Android this falls back to a regular dialog. | ||
| 22 | + - **Pickers**: the built-in [`Picker`][pythonnative.Picker] component | ||
| 23 | + is implemented on top of action sheets — use it for select/dropdown | ||
| 24 | + widgets. | ||
| 25 | + | ||
| 26 | + ## Testing | ||
| 27 | + | ||
| 28 | + When running off-device (e.g., in unit tests), `Alert.show` records | ||
| 29 | + each call to `Alert._test_log` instead of presenting a dialog. Reset | ||
| 30 | + the log with `Alert._test_log.clear()` between cases. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,24 @@ | |||
| 1 | + # Animated | ||
| 2 | + | ||
| 3 | + PythonNative's `Animated` API mirrors React Native's. Build performant | ||
| 4 | + animations declaratively by binding [`AnimatedValue`][pythonnative.AnimatedValue] | ||
| 5 | + instances to the `style` of an `Animated.View`, `Animated.Text`, or | ||
| 6 | + `Animated.Image`. The reconciler holds a `ref` to the underlying | ||
| 7 | + native view; the animation driver pushes value changes directly to | ||
| 8 | + the native handler's `set_animated_property` hook so per-frame updates | ||
| 9 | + bypass full reconciliation. | ||
| 10 | + | ||
| 11 | + ::: pythonnative.animated | ||
| 12 | + options: | ||
| 13 | + show_root_heading: false | ||
| 14 | + show_root_toc_entry: false | ||
| 15 | + members_order: source | ||
| 16 | + filters: ["!^_"] | ||
| 17 | + show_if_no_docstring: true | ||
| 18 | + | ||
| 19 | + ## See also | ||
| 20 | + | ||
| 21 | + - The [Animations guide](../guides/animations.md) walks through | ||
| 22 | + fade-ins, springs, sequences, and gesture-driven animations. | ||
| 23 | + - [`use_ref`][pythonnative.use_ref] explains the `ref` semantics that | ||
| 24 | + back `Animated.View`. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,6 +12,21 @@ slot across renders. | |||
| 12 | 12 | members_order: source | |
| 13 | 13 | filters: ["!^_"] | |
| 14 | 14 | ||
| 15 | + ## Platform-metric hooks | ||
| 16 | + | ||
| 17 | + These hooks subscribe to values published by | ||
| 18 | + `pythonnative.platform_metrics` and re-render the component when they | ||
| 19 | + change. The page host is the only code that updates the underlying | ||
| 20 | + values; user code consumes them. | ||
| 21 | + | ||
| 22 | + - [`use_window_dimensions`][pythonnative.use_window_dimensions] — viewport size. | ||
| 23 | + - [`use_safe_area_insets`][pythonnative.use_safe_area_insets] — top/bottom/left/right insets. | ||
| 24 | + - [`use_keyboard_height`][pythonnative.use_keyboard_height] — software keyboard height. | ||
| 25 | + | ||
| 26 | + For most apps the dedicated | ||
| 27 | + [`KeyboardAvoidingView`][pythonnative.KeyboardAvoidingView] component | ||
| 28 | + is preferable to consuming `use_keyboard_height` directly. | ||
| 29 | + | ||
| 15 | 30 | ## Next steps | |
| 16 | 31 | ||
| 17 | 32 | - Compose hooks into a screen: [Components](components.md). | |
@@ -21,3 +36,5 @@ slot across renders. | |||
| 21 | 36 | - Share state across the tree with | |
| 22 | 37 | [`create_context`][pythonnative.create_context] and | |
| 23 | 38 | [`Provider`][pythonnative.Provider]. | |
| 39 | + - Animate without re-rendering using [`use_ref`][pythonnative.use_ref] | ||
| 40 | + + `Animated`; see the [Animations guide](../guides/animations.md). | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,27 @@ | |||
| 1 | + # Platform | ||
| 2 | + | ||
| 3 | + The [`Platform`][pythonnative.Platform] class exposes runtime | ||
| 4 | + information about the host platform and a `select` helper for writing | ||
| 5 | + platform-aware code without scattering `if IS_IOS` / `if IS_ANDROID` | ||
| 6 | + branches throughout the codebase. | ||
| 7 | + | ||
| 8 | + ::: pythonnative.platform | ||
| 9 | + options: | ||
| 10 | + show_root_heading: false | ||
| 11 | + show_root_toc_entry: false | ||
| 12 | + members_order: source | ||
| 13 | + filters: ["!^_"] | ||
| 14 | + | ||
| 15 | + ## Quick reference | ||
| 16 | + | ||
| 17 | + | Attribute | Values | | ||
| 18 | + |---|---| | ||
| 19 | + | `Platform.OS` | `"ios"`, `"android"`, or `"test"` | | ||
| 20 | + | `Platform.Version` | Best-effort OS version string | | ||
| 21 | + | `Platform.is_ios` / `Platform.is_android` / `Platform.is_test` | Booleans | | ||
| 22 | + | `Platform.select(spec, default=None)` | Pick a value matching the current platform | | ||
| 23 | + | ||
| 24 | + ## See also | ||
| 25 | + | ||
| 26 | + - [Window dimensions, safe area, keyboard hooks](hooks.md) for | ||
| 27 | + reactive metric access. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,8 +17,11 @@ The reference is split per module so each page stays scannable: | |||
| 17 | 17 | ||
| 18 | 18 | | Area | Page | Key symbols | | |
| 19 | 19 | |---|---|---| | |
| 20 | - | Element factories | [Components](components.md) | [`Text`][pythonnative.Text], [`Button`][pythonnative.Button], [`Column`][pythonnative.Column], [`Row`][pythonnative.Row], [`ScrollView`][pythonnative.ScrollView], [`FlatList`][pythonnative.FlatList], [`ErrorBoundary`][pythonnative.ErrorBoundary] | | ||
| 21 | - | Hooks | [Hooks](hooks.md) | [`use_state`][pythonnative.use_state], [`use_reducer`][pythonnative.use_reducer], [`use_effect`][pythonnative.use_effect], [`use_memo`][pythonnative.use_memo], [`use_ref`][pythonnative.use_ref], [`use_context`][pythonnative.use_context] | | ||
| 20 | + | Element factories | [Components](components.md) | [`Text`][pythonnative.Text], [`Button`][pythonnative.Button], [`Column`][pythonnative.Column], [`Row`][pythonnative.Row], [`ScrollView`][pythonnative.ScrollView], [`FlatList`][pythonnative.FlatList], [`SectionList`][pythonnative.SectionList], [`Modal`][pythonnative.Modal], [`Pressable`][pythonnative.Pressable], [`StatusBar`][pythonnative.StatusBar], [`KeyboardAvoidingView`][pythonnative.KeyboardAvoidingView], [`RefreshControl`][pythonnative.RefreshControl], [`Picker`][pythonnative.Picker], [`ErrorBoundary`][pythonnative.ErrorBoundary] | | ||
| 21 | + | Hooks | [Hooks](hooks.md) | [`use_state`][pythonnative.use_state], [`use_reducer`][pythonnative.use_reducer], [`use_effect`][pythonnative.use_effect], [`use_memo`][pythonnative.use_memo], [`use_ref`][pythonnative.use_ref], [`use_context`][pythonnative.use_context], [`use_window_dimensions`][pythonnative.use_window_dimensions], [`use_safe_area_insets`][pythonnative.use_safe_area_insets], [`use_keyboard_height`][pythonnative.use_keyboard_height] | | ||
| 22 | + | Animations | [Animated](animated.md) | `Animated`, [`AnimatedValue`][pythonnative.AnimatedValue] | | ||
| 23 | + | System dialogs | [Alerts](alerts.md) | [`Alert`][pythonnative.Alert] | | ||
| 24 | + | Platform | [Platform](platform.md) | [`Platform`][pythonnative.Platform] | | ||
| 22 | 25 | | Navigation | [Navigation](navigation.md) | [`NavigationContainer`][pythonnative.NavigationContainer], [`create_stack_navigator`][pythonnative.create_stack_navigator], [`create_tab_navigator`][pythonnative.create_tab_navigator], [`create_drawer_navigator`][pythonnative.create_drawer_navigator], [`use_navigation`][pythonnative.use_navigation] | | |
| 23 | 26 | | Styling | [Style](style.md) | [`StyleSheet`][pythonnative.StyleSheet], [`ThemeContext`][pythonnative.style.ThemeContext] | | |
| 24 | 27 | | Element descriptor | [Element](element.md) | [`Element`][pythonnative.Element] | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -77,8 +77,39 @@ pn.Column( | |||
| 77 | 77 | ||
| 78 | 78 | **Lists:** | |
| 79 | 79 | ||
| 80 | - - [`FlatList(data, render_item, key_extractor, separator_height)`][pythonnative.FlatList]: | ||
| 81 | - scrollable data list. | ||
| 80 | + - [`FlatList(data, render_item, key_extractor, item_height, ...)`][pythonnative.FlatList]: | ||
| 81 | + scrollable data list. Pass `item_height=` to enable native | ||
| 82 | + virtualization (`UITableView` / `RecyclerView`); rows are mounted | ||
| 83 | + lazily as they scroll into view. | ||
| 84 | + - [`SectionList(sections, render_item, render_section_header, item_height, ...)`][pythonnative.SectionList]: | ||
| 85 | + virtualized list with section headers. | ||
| 86 | + | ||
| 87 | + **Platform UI:** | ||
| 88 | + | ||
| 89 | + - [`StatusBar(style, background_color, hidden)`][pythonnative.StatusBar]: | ||
| 90 | + configure the device's status bar (light/dark icons, color, hidden). | ||
| 91 | + - [`KeyboardAvoidingView(*children, behavior)`][pythonnative.KeyboardAvoidingView]: | ||
| 92 | + shift content up when the software keyboard appears. | ||
| 93 | + - [`RefreshControl(refreshing, on_refresh)`][pythonnative.RefreshControl]: | ||
| 94 | + pull-to-refresh spec for `ScrollView` and `FlatList` (passed via | ||
| 95 | + the `refresh_control=` prop). | ||
| 96 | + - [`Picker(value, items, on_change, placeholder)`][pythonnative.Picker]: | ||
| 97 | + select / dropdown widget backed by an action sheet. | ||
| 98 | + | ||
| 99 | + **Imperative APIs:** | ||
| 100 | + | ||
| 101 | + - [`Alert.show(title, message, buttons, style)`][pythonnative.Alert]: | ||
| 102 | + present a native alert dialog or action sheet. | ||
| 103 | + - [`Alert.confirm(title, on_confirm, on_cancel)`][pythonnative.alerts.Alert.confirm]: | ||
| 104 | + two-button confirm/cancel. | ||
| 105 | + | ||
| 106 | + **Animations:** | ||
| 107 | + | ||
| 108 | + - `Animated.View` / `Animated.Text` / `Animated.Image`: components | ||
| 109 | + whose `style` accepts [`AnimatedValue`][pythonnative.AnimatedValue] | ||
| 110 | + instances. Drive animations with `Animated.timing`, | ||
| 111 | + `Animated.spring`, or `Animated.decay`. See the | ||
| 112 | + [Animations guide](../guides/animations.md). | ||
| 82 | 113 | ||
| 83 | 114 | ### Flex layout model | |
| 84 | 115 | ||
@@ -214,7 +245,9 @@ hook state. | |||
| 214 | 245 | - [`use_callback(fn, deps)`][pythonnative.use_callback]: stable | |
| 215 | 246 | function references. | |
| 216 | 247 | - [`use_ref(initial)`][pythonnative.use_ref]: mutable ref that | |
| 217 | - persists across renders. | ||
| 248 | + persists across renders. When passed via the `ref=` prop, the | ||
| 249 | + reconciler populates `ref["current"]` with the underlying native | ||
| 250 | + view. | ||
| 218 | 251 | - [`use_context(context)`][pythonnative.use_context]: read from a | |
| 219 | 252 | context provider. | |
| 220 | 253 | - [`use_navigation()`][pythonnative.use_navigation]: navigation | |
@@ -223,6 +256,12 @@ hook state. | |||
| 223 | 256 | current route params. | |
| 224 | 257 | - [`use_focus_effect(effect, deps)`][pythonnative.use_focus_effect]: | |
| 225 | 258 | like `use_effect` but only runs when the screen is focused. | |
| 259 | + - [`use_window_dimensions()`][pythonnative.use_window_dimensions]: | ||
| 260 | + reactive viewport size. | ||
| 261 | + - [`use_safe_area_insets()`][pythonnative.use_safe_area_insets]: | ||
| 262 | + reactive safe-area insets. | ||
| 263 | + - [`use_keyboard_height()`][pythonnative.use_keyboard_height]: | ||
| 264 | + reactive software-keyboard height. | ||
| 226 | 265 | ||
| 227 | 266 | ### Custom hooks | |
| 228 | 267 | ||
@@ -257,15 +296,22 @@ def MyComponent(): | |||
| 257 | 296 | ||
| 258 | 297 | ## Platform detection | |
| 259 | 298 | ||
| 260 | - Use `utils.IS_ANDROID` / `utils.IS_IOS` when you need | ||
| 261 | - platform-specific logic: | ||
| 299 | + The recommended way to write platform-aware code is via | ||
| 300 | + [`Platform`][pythonnative.Platform]: | ||
| 262 | 301 | ||
| 263 | 302 | ```python | |
| 264 | - from pythonnative.utils import IS_ANDROID | ||
| 303 | + import pythonnative as pn | ||
| 304 | + | ||
| 305 | + title = pn.Platform.select({"ios": "iOS App", "android": "Android App"}) | ||
| 265 | 306 | ||
| 266 | - title = "Android App" if IS_ANDROID else "iOS App" | ||
| 307 | + if pn.Platform.is_ios: | ||
| 308 | + margin = 16 | ||
| 267 | 309 | ``` | |
| 268 | 310 | ||
| 311 | + `pn.Platform.OS` is `"ios"`, `"android"`, or `"test"` (the latter | ||
| 312 | + when running off-device, e.g., in unit tests). The lower-level | ||
| 313 | + `utils.IS_ANDROID` / `utils.IS_IOS` constants are still available. | ||
| 314 | + | ||
| 269 | 315 | ## Next steps | |
| 270 | 316 | ||
| 271 | 317 | - Learn the renderer underneath: [Architecture](architecture.md). | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,111 @@ | |||
| 1 | + # Animations | ||
| 2 | + | ||
| 3 | + PythonNative ships an `Animated` API modelled on React Native's. It's | ||
| 4 | + designed for the common case where a small set | ||
| 5 | + of style properties (opacity, transform, color) need to interpolate | ||
| 6 | + smoothly over time without re-rendering the component tree on every | ||
| 7 | + frame. | ||
| 8 | + | ||
| 9 | + ## Mental model | ||
| 10 | + | ||
| 11 | + 1. Create an [`AnimatedValue`][pythonnative.AnimatedValue] using | ||
| 12 | + [`use_memo`][pythonnative.use_memo] (so it survives re-renders). | ||
| 13 | + 2. Bind the value into the `style` of an `Animated.View`, | ||
| 14 | + `Animated.Text`, or `Animated.Image`. | ||
| 15 | + 3. Drive the value with `Animated.timing`, `Animated.spring`, or | ||
| 16 | + `Animated.decay`. Each driver returns a handle with `.start()` / | ||
| 17 | + `.stop()`. | ||
| 18 | + | ||
| 19 | + The animated component captures a `ref` to the underlying native view | ||
| 20 | + (via the same [`use_ref`][pythonnative.use_ref] mechanism users have | ||
| 21 | + access to). The animation driver then invokes the platform's native | ||
| 22 | + animation API (`UIView.animate` on iOS, `ViewPropertyAnimator` on | ||
| 23 | + Android) directly on that view, so per-frame updates skip the | ||
| 24 | + reconciler. | ||
| 25 | + | ||
| 26 | + ## Fade in on mount | ||
| 27 | + | ||
| 28 | + ```python | ||
| 29 | + import pythonnative as pn | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + @pn.component | ||
| 33 | + def FadeInBox(): | ||
| 34 | + opacity = pn.use_memo(lambda: pn.Animated.Value(0.0), []) | ||
| 35 | + | ||
| 36 | + def _fade_in(): | ||
| 37 | + pn.Animated.timing(opacity, to=1.0, duration=400).start() | ||
| 38 | + | ||
| 39 | + pn.use_effect(_fade_in, []) | ||
| 40 | + | ||
| 41 | + return pn.Animated.View( | ||
| 42 | + pn.Text("Hello!"), | ||
| 43 | + style={ | ||
| 44 | + "opacity": opacity, | ||
| 45 | + "background_color": "#0EA5E9", | ||
| 46 | + "padding": 16, | ||
| 47 | + "border_radius": 12, | ||
| 48 | + }, | ||
| 49 | + ) | ||
| 50 | + ``` | ||
| 51 | + | ||
| 52 | + `opacity` starts at `0.0` and the timing animation interpolates it to | ||
| 53 | + `1.0` over 400 ms. | ||
| 54 | + | ||
| 55 | + ## Spring animation on press | ||
| 56 | + | ||
| 57 | + ```python | ||
| 58 | + @pn.component | ||
| 59 | + def Bouncy(): | ||
| 60 | + scale = pn.use_memo(lambda: pn.Animated.Value(1.0), []) | ||
| 61 | + | ||
| 62 | + def _press(): | ||
| 63 | + pn.Animated.spring(scale, to=1.2, stiffness=200, damping=8).start() | ||
| 64 | + | ||
| 65 | + return pn.Pressable( | ||
| 66 | + pn.Animated.View( | ||
| 67 | + pn.Text("Tap me"), | ||
| 68 | + style={"scale": scale, "padding": 12, "background_color": "#10B981"}, | ||
| 69 | + ), | ||
| 70 | + on_press=_press, | ||
| 71 | + ) | ||
| 72 | + ``` | ||
| 73 | + | ||
| 74 | + Available transform shortcuts inside `style`: `scale`, `scale_x`, | ||
| 75 | + `scale_y`, `translate_x`, `translate_y`, `rotate`. Each accepts an | ||
| 76 | + `AnimatedValue` and the runtime maps them to the underlying native | ||
| 77 | + animation property. | ||
| 78 | + | ||
| 79 | + ## Sequencing and parallel composition | ||
| 80 | + | ||
| 81 | + ```python | ||
| 82 | + opacity = pn.Animated.Value(0.0) | ||
| 83 | + translate_y = pn.Animated.Value(20.0) | ||
| 84 | + | ||
| 85 | + pn.Animated.parallel([ | ||
| 86 | + pn.Animated.timing(opacity, to=1.0, duration=300), | ||
| 87 | + pn.Animated.spring(translate_y, to=0.0), | ||
| 88 | + ]).start() | ||
| 89 | + ``` | ||
| 90 | + | ||
| 91 | + Use `Animated.sequence` for one-after-another execution and | ||
| 92 | + `Animated.delay(ms)` to insert pauses inside a sequence. | ||
| 93 | + | ||
| 94 | + ## Easing | ||
| 95 | + | ||
| 96 | + `Animated.timing` accepts an `easing` argument: `"linear"`, | ||
| 97 | + `"ease_in"`, `"ease_out"`, `"ease_in_out"`, or `"bounce"`. | ||
| 98 | + | ||
| 99 | + ## Stopping an animation | ||
| 100 | + | ||
| 101 | + `start()` returns the handle you started with, and the handle exposes | ||
| 102 | + `.stop()`. A common pattern is to keep the handle in a `use_ref` so | ||
| 103 | + you can cancel a long-running animation when the user interrupts. | ||
| 104 | + | ||
| 105 | + ## When NOT to use `Animated` | ||
| 106 | + | ||
| 107 | + - For simple state transitions where re-rendering the tree is fine, | ||
| 108 | + plain [`use_state`][pythonnative.use_state] is simpler. | ||
| 109 | + - For physics simulations or per-frame layout (drag-and-drop, charts), | ||
| 110 | + consider running your own loop with | ||
| 111 | + [`use_effect`][pythonnative.use_effect] and a setter. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments