| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -31,6 +31,8 @@ PythonNative is a cross-platform toolkit for building native Android and iOS app | |||
| 31 | 31 | ## Features | |
| 32 | 32 | ||
| 33 | 33 | - **Declarative UI:** Describe *what* your UI should look like with element functions (`Text`, `Button`, `Column`, `Row`, etc.). PythonNative creates and updates native views automatically. | |
| 34 | + - **Rich component library:** 25+ built-in components backed by real native widgets — `TextInput`, `Image` / `ImageBackground`, `ScrollView`, `FlatList` / `SectionList`, `Modal`, `Pressable` / `TouchableOpacity`, `Switch` / `Checkbox`, `Slider`, `SegmentedControl`, `Picker`, `DatePicker`, `ProgressBar` / `ActivityIndicator`, `WebView`, and more. | ||
| 35 | + - **Device APIs:** Cross-platform modules for `Camera`, `Location`, `FileSystem`, `Notifications`, `Clipboard`, `Share`, `Linking`, `Permissions`, `AppState`, `NetInfo`, `SecureStore`, `Battery`, `Haptics` / `Vibration`, and `Biometrics` — plus reactive `use_app_state` and `use_net_info` hooks. | ||
| 34 | 36 | - **Hooks and function components:** Manage state with `use_state`, side effects with `use_effect`, and navigation with `use_navigation`, all through one consistent pattern. | |
| 35 | 37 | - **Typed `style` prop:** Pass all visual and layout properties through a single `style` dict, fully described by the `pn.Style` `TypedDict` and the ergonomic `pn.style(...)` helper for IDE autocomplete and static checking. Compose reusable styles with `StyleSheet`. | |
| 36 | 38 | - **Cross-platform flexbox engine:** A pure-Python, Yoga-style layout engine computes frames once and applies them to native views, so `flex`, `padding`, `aspect_ratio`, and `position: "absolute"` produce the same geometry on Android and iOS. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -142,17 +142,50 @@ Container that respects safe area insets (notch, status bar). | |||
| 142 | 142 | ## ScrollView | |
| 143 | 143 | ||
| 144 | 144 | ```python | |
| 145 | - pn.ScrollView(child, style={"background_color": "#FFF"}) | ||
| 145 | + pn.ScrollView( | ||
| 146 | + child, | ||
| 147 | + scroll_axis="vertical", # or "horizontal" | ||
| 148 | + on_scroll=lambda x, y: ..., # content offset as the user scrolls | ||
| 149 | + shows_scroll_indicator=True, | ||
| 150 | + paging_enabled=False, # snap to viewport-sized pages (carousel) | ||
| 151 | + bounces=True, # iOS rubber-band overscroll | ||
| 152 | + keyboard_dismiss_mode=None, # "none" | "on_drag" | "interactive" | ||
| 153 | + content_container_style={"padding": 16}, | ||
| 154 | + refresh_control=pn.RefreshControl(refreshing=loading, on_refresh=reload), | ||
| 155 | + style={"background_color": "#FFF"}, | ||
| 156 | + ) | ||
| 146 | 157 | ``` | |
| 147 | 158 | ||
| 159 | + - `scroll_axis` — `"vertical"` (default) or `"horizontal"` | ||
| 160 | + - `on_scroll` — callback `(x, y) -> None` with the content offset | ||
| 161 | + - `shows_scroll_indicator` — show/hide the scroll bar | ||
| 162 | + - `paging_enabled` — snap scrolling to multiples of the viewport size | ||
| 163 | + - `bounces` — enable the iOS overscroll bounce | ||
| 164 | + - `content_container_style` — style for the inner content wrapper (padding, | ||
| 165 | + alignment, spacing), distinct from `style` (the scroll frame) | ||
| 166 | + - `keyboard_dismiss_mode` — `"none"`, `"on_drag"`, or `"interactive"` | ||
| 167 | + - `refresh_control` — pull-to-refresh spec (see [`RefreshControl`](#refreshcontrol)) | ||
| 168 | + | ||
| 148 | 169 | ## TextInput | |
| 149 | 170 | ||
| 150 | 171 | ```python | |
| 151 | 172 | pn.TextInput(value="", placeholder="Enter text", on_change=handler, secure=False, | |
| 173 | + editable=True, clear_button=True, on_focus=f, on_blur=g, | ||
| 174 | + selection_color="#007AFF", text_content_type="password", | ||
| 152 | 175 | style={"font_size": 16, "color": "#000", "background_color": "#FFF"}) | |
| 153 | 176 | ``` | |
| 154 | 177 | ||
| 155 | 178 | - `on_change` — callback `(str) -> None` receiving new text | |
| 179 | + - `on_submit` — callback `(str) -> None` on Return / Done | ||
| 180 | + - `editable` — when `False`, the field is read-only | ||
| 181 | + - `clear_button` — show an inline clear ("x") button while editing | ||
| 182 | + - `on_focus` / `on_blur` — callbacks `() -> None` on focus changes | ||
| 183 | + - `selection_color` — cursor / selection highlight color | ||
| 184 | + - `text_content_type` — autofill hint (`"username"`, `"password"`, | ||
| 185 | + `"one_time_code"`, …) | ||
| 186 | + - Other kwargs: `secure`, `multiline`, `keyboard_type`, `auto_capitalize`, | ||
| 187 | + `auto_correct`, `auto_focus`, `return_key_type`, `max_length`, | ||
| 188 | + `placeholder_color` | ||
| 156 | 189 | ||
| 157 | 190 | ## Image | |
| 158 | 191 | ||
@@ -182,23 +215,46 @@ pn.Slider(value=0.5, min_value=0.0, max_value=1.0, on_change=handler) | |||
| 182 | 215 | ## ProgressBar | |
| 183 | 216 | ||
| 184 | 217 | ```python | |
| 185 | - pn.ProgressBar(value=0.5, style={"background_color": "#EEE"}) | ||
| 218 | + pn.ProgressBar(value=0.5, color="#007AFF", track_color="#EEE", indeterminate=False) | ||
| 186 | 219 | ``` | |
| 187 | 220 | ||
| 188 | 221 | - `value` — 0.0 to 1.0 | |
| 222 | + - `color` — color of the filled portion | ||
| 223 | + - `track_color` — color of the unfilled track | ||
| 224 | + - `indeterminate` — animate continuously and ignore `value` | ||
| 189 | 225 | ||
| 190 | 226 | ## ActivityIndicator | |
| 191 | 227 | ||
| 192 | 228 | ```python | |
| 193 | - pn.ActivityIndicator(animating=True) | ||
| 229 | + pn.ActivityIndicator(animating=True, color="#007AFF", size="large") | ||
| 194 | 230 | ``` | |
| 195 | 231 | ||
| 232 | + - `animating` — hide the spinner when `False` | ||
| 233 | + - `color` — spinner color | ||
| 234 | + - `size` — `"small"` (default) or `"large"` | ||
| 235 | + | ||
| 196 | 236 | ## WebView | |
| 197 | 237 | ||
| 198 | 238 | ```python | |
| 199 | - pn.WebView(url="https://example.com") | ||
| 239 | + pn.WebView( | ||
| 240 | + url="https://example.com", | ||
| 241 | + html=None, # render inline HTML instead of a URL | ||
| 242 | + on_load=lambda url: ..., # page finished loading | ||
| 243 | + on_message=lambda msg: ..., # window.pythonnative.postMessage(...) | ||
| 244 | + on_navigation_state_change=lambda url: ..., | ||
| 245 | + inject_javascript="document.body.style.background='#fff'", | ||
| 246 | + scroll_enabled=True, | ||
| 247 | + ) | ||
| 200 | 248 | ``` | |
| 201 | 249 | ||
| 250 | + - `url` — page to load (ignored when `html` is given) | ||
| 251 | + - `html` — inline HTML markup | ||
| 252 | + - `on_load` — callback `(url) -> None` when a page finishes loading | ||
| 253 | + - `on_message` — callback `(str) -> None` for messages posted from page JS | ||
| 254 | + - `on_navigation_state_change` — callback `(url) -> None` on navigation | ||
| 255 | + - `inject_javascript` — JS evaluated after each page load | ||
| 256 | + - `scroll_enabled` — allow scrolling within the web content | ||
| 257 | + | ||
| 202 | 258 | ## Spacer | |
| 203 | 259 | ||
| 204 | 260 | ```python | |
@@ -219,12 +275,123 @@ Wraps any child element with tap/long-press handling. | |||
| 219 | 275 | ## Modal | |
| 220 | 276 | ||
| 221 | 277 | ```python | |
| 222 | - pn.Modal(*children, visible=show_modal, on_dismiss=handler, title="Confirm", | ||
| 278 | + pn.Modal(*children, visible=show_modal, on_dismiss=handler, on_show=shown, | ||
| 279 | + title="Confirm", animation_type="slide", transparent=False, | ||
| 280 | + presentation_style="page_sheet", dismiss_on_backdrop=True, | ||
| 223 | 281 | style={"background_color": "#FFF"}) | |
| 224 | 282 | ``` | |
| 225 | 283 | ||
| 226 | 284 | Overlay dialog shown when `visible=True`. | |
| 227 | 285 | ||
| 286 | + - `on_dismiss` — callback `() -> None` when dismissed via gesture | ||
| 287 | + - `on_show` — callback `() -> None` once the modal finishes presenting | ||
| 288 | + - `animation_type` — `"slide"` (default), `"fade"`, `"none"` | ||
| 289 | + - `transparent` — dim the underlying view instead of fully covering it | ||
| 290 | + - `presentation_style` — `"page_sheet"` (default), `"form_sheet"`, | ||
| 291 | + `"full_screen"`, `"overlay"` | ||
| 292 | + - `dismiss_on_backdrop` — tapping the dimmed backdrop dismisses the modal | ||
| 293 | + | ||
| 294 | + ## TouchableOpacity | ||
| 295 | + | ||
| 296 | + ```python | ||
| 297 | + pn.TouchableOpacity(child, on_press=handler, on_long_press=handler, | ||
| 298 | + active_opacity=0.2, disabled=False) | ||
| 299 | + ``` | ||
| 300 | + | ||
| 301 | + Tappable wrapper that dips to `active_opacity` while pressed (a thin alias | ||
| 302 | + over [`Pressable`](#pressable)). `disabled=True` ignores presses and dims the | ||
| 303 | + content. | ||
| 304 | + | ||
| 305 | + ## ImageBackground | ||
| 306 | + | ||
| 307 | + ```python | ||
| 308 | + pn.ImageBackground(child, source="bg.png", scale_type="cover", | ||
| 309 | + style={"width": 320, "height": 180, "padding": 16}) | ||
| 310 | + ``` | ||
| 311 | + | ||
| 312 | + Renders `child` content layered over a background image. Composed from an | ||
| 313 | + absolutely-filled `Image` plus a content `View`. | ||
| 314 | + | ||
| 315 | + - `source` — image resource name or URL | ||
| 316 | + - `scale_type` — `"cover"` (default), `"contain"`, `"stretch"`, `"center"` | ||
| 317 | + | ||
| 318 | + ## Checkbox | ||
| 319 | + | ||
| 320 | + ```python | ||
| 321 | + pn.Checkbox(value=accepted, on_change=set_accepted, label="Accept terms", | ||
| 322 | + disabled=False, color="#007AFF") | ||
| 323 | + ``` | ||
| 324 | + | ||
| 325 | + - `value` — checked state (`bool`) | ||
| 326 | + - `on_change` — callback `(bool) -> None` | ||
| 327 | + - `label` — optional inline label (also tappable) | ||
| 328 | + - `disabled` — grey out and ignore input | ||
| 329 | + - `color` — tint for the checked box | ||
| 330 | + | ||
| 331 | + | Platform | Native view | | ||
| 332 | + |----------|--------------------------| | ||
| 333 | + | Android | `android.widget.CheckBox` | | ||
| 334 | + | iOS | checkmark `UIButton` | | ||
| 335 | + | ||
| 336 | + ## SegmentedControl | ||
| 337 | + | ||
| 338 | + ```python | ||
| 339 | + pn.SegmentedControl(segments=["Day", "Week", "Month"], selected_index=0, | ||
| 340 | + on_change=handler, tint_color="#007AFF") | ||
| 341 | + ``` | ||
| 342 | + | ||
| 343 | + - `segments` — list of segment label strings | ||
| 344 | + - `selected_index` — index of the selected segment | ||
| 345 | + - `on_change` — callback `(int) -> None` with the new index | ||
| 346 | + - `tint_color` — accent color for the selected segment | ||
| 347 | + | ||
| 348 | + | Platform | Native view | | ||
| 349 | + |----------|--------------------------| | ||
| 350 | + | Android | toggle row (`LinearLayout` of buttons) | | ||
| 351 | + | iOS | `UISegmentedControl` | | ||
| 352 | + | ||
| 353 | + ## DatePicker | ||
| 354 | + | ||
| 355 | + ```python | ||
| 356 | + pn.DatePicker(value="2026-05-31", mode="date", on_change=handler, | ||
| 357 | + minimum="2026-01-01", maximum="2026-12-31") | ||
| 358 | + ``` | ||
| 359 | + | ||
| 360 | + ISO-8601 string values: `"YYYY-MM-DD"` (date), `"HH:MM"` (time), | ||
| 361 | + `"YYYY-MM-DDTHH:MM"` (datetime). | ||
| 362 | + | ||
| 363 | + - `value` — current selection (ISO-8601 string) | ||
| 364 | + - `mode` — `"date"` (default), `"time"`, `"datetime"` | ||
| 365 | + - `on_change` — callback `(str) -> None` with the new ISO-8601 string | ||
| 366 | + - `minimum` / `maximum` — selectable bounds | ||
| 367 | + | ||
| 368 | + | Platform | Native view | | ||
| 369 | + |----------|--------------------------| | ||
| 370 | + | Android | `DatePickerDialog` / `TimePickerDialog` | | ||
| 371 | + | iOS | `UIDatePicker` | | ||
| 372 | + | ||
| 373 | + ## Picker | ||
| 374 | + | ||
| 375 | + ```python | ||
| 376 | + pn.Picker(value=selected, items=[{"value": "a", "label": "Apple"}], | ||
| 377 | + on_change=handler, placeholder="Select…") | ||
| 378 | + ``` | ||
| 379 | + | ||
| 380 | + Native dropdown / select. `items` is a list of `{"value": Any, "label": str}`. | ||
| 381 | + | ||
| 382 | + ## RefreshControl | ||
| 383 | + | ||
| 384 | + ```python | ||
| 385 | + pn.ScrollView(child, refresh_control=pn.RefreshControl( | ||
| 386 | + refreshing=loading, on_refresh=reload, tint_color="#007AFF")) | ||
| 387 | + ``` | ||
| 388 | + | ||
| 389 | + Pull-to-refresh spec (a plain dict) passed to a `ScrollView` or `FlatList`. | ||
| 390 | + | ||
| 391 | + - `refreshing` — drive the spinner from state | ||
| 392 | + - `on_refresh` — callback `() -> None` when pulled past threshold | ||
| 393 | + - `tint_color` — spinner color | ||
| 394 | + | ||
| 228 | 395 | ## TabBar | |
| 229 | 396 | ||
| 230 | 397 | ```python | |
@@ -253,10 +420,26 @@ Native tab bar — typically created automatically by `Tab.Navigator`. | |||
| 253 | 420 | ||
| 254 | 421 | ```python | |
| 255 | 422 | pn.FlatList(data=items, render_item=render_fn, key_extractor=key_fn, | |
| 256 | - separator_height=1, style={"background_color": "#FFF"}) | ||
| 423 | + item_height=44, separator_height=1, | ||
| 424 | + horizontal=False, num_columns=1, | ||
| 425 | + list_header=pn.Text("Header"), list_footer=pn.Text("Footer"), | ||
| 426 | + list_empty=pn.Text("Nothing here"), | ||
| 427 | + on_end_reached=load_more, on_end_reached_threshold=0.5, | ||
| 428 | + refresh_control=pn.RefreshControl(refreshing=loading, on_refresh=reload), | ||
| 429 | + content_container_style={"padding": 8}, | ||
| 430 | + style={"background_color": "#FFF"}) | ||
| 257 | 431 | ``` | |
| 258 | 432 | ||
| 259 | 433 | - `data` — list of items | |
| 260 | 434 | - `render_item` — `(item, index) -> Element` function | |
| 261 | 435 | - `key_extractor` — `(item, index) -> str` for stable keys | |
| 436 | + - `item_height` — fixed row height; enables native virtualization | ||
| 262 | 437 | - `separator_height` — spacing between items | |
| 438 | + - `horizontal` — lay rows out left-to-right (eager backend) | ||
| 439 | + - `num_columns` — render as a grid of N columns (eager backend) | ||
| 440 | + - `list_header` / `list_footer` — elements rendered once above / below rows | ||
| 441 | + - `list_empty` — element rendered when `data` is empty | ||
| 442 | + - `on_end_reached` — callback `() -> None` near the end (virtualized) | ||
| 443 | + - `on_end_reached_threshold` — fraction-of-viewport trigger distance | ||
| 444 | + - `content_container_style` — style for the inner content wrapper | ||
| 445 | + - `refresh_control` — pull-to-refresh spec (see [`RefreshControl`](#refreshcontrol)) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,15 +1,17 @@ | |||
| 1 | 1 | # Native modules | |
| 2 | 2 | ||
| 3 | 3 | Cross-platform wrappers around device APIs that are not part of the | |
| 4 | - view tree: camera and photo gallery, GPS, app-scoped file I/O, and | ||
| 5 | - local notifications. Each module is implemented twice (once per | ||
| 4 | + view tree: camera, GPS, file I/O, notifications, clipboard, share | ||
| 5 | + sheet, deep links, permissions, connectivity, secure storage, battery, | ||
| 6 | + haptics, and biometrics. Each module is implemented twice (once per | ||
| 6 | 7 | platform) and dispatches at runtime based on the `IS_ANDROID` and | |
| 7 | - `IS_IOS` flags from `pythonnative.utils`. | ||
| 8 | + `IS_IOS` flags from `pythonnative.utils`, with a safe desktop fallback. | ||
| 8 | 9 | ||
| 9 | - Apart from `FileSystem`, every public method is a coroutine: ``await | ||
| 10 | - Camera.take_photo()``, ``await Location.get_current()``, and so on. | ||
| 11 | - For the call-site patterns and the runtime they're scheduled on, see | ||
| 12 | - the [Async + data guide](../guides/async.md). | ||
| 10 | + Both synchronous and coroutine APIs exist (chosen to match the | ||
| 11 | + platform call). For the call-site patterns, the reactive | ||
| 12 | + `use_app_state` / `use_net_info` hooks, and the runtime coroutines are | ||
| 13 | + scheduled on, see the [Native modules guide](../guides/native-modules.md) | ||
| 14 | + and the [Async + data guide](../guides/async.md). | ||
| 13 | 15 | ||
| 14 | 16 | ## Camera | |
| 15 | 17 | ||
@@ -47,6 +49,96 @@ the [Async + data guide](../guides/async.md). | |||
| 47 | 49 | members_order: source | |
| 48 | 50 | filters: ["!^_"] | |
| 49 | 51 | ||
| 52 | + ## Clipboard | ||
| 53 | + | ||
| 54 | + ::: pythonnative.native_modules.clipboard | ||
| 55 | + options: | ||
| 56 | + show_root_heading: false | ||
| 57 | + show_root_toc_entry: false | ||
| 58 | + members_order: source | ||
| 59 | + filters: ["!^_"] | ||
| 60 | + | ||
| 61 | + ## Share | ||
| 62 | + | ||
| 63 | + ::: pythonnative.native_modules.share | ||
| 64 | + options: | ||
| 65 | + show_root_heading: false | ||
| 66 | + show_root_toc_entry: false | ||
| 67 | + members_order: source | ||
| 68 | + filters: ["!^_"] | ||
| 69 | + | ||
| 70 | + ## Linking | ||
| 71 | + | ||
| 72 | + ::: pythonnative.native_modules.linking | ||
| 73 | + options: | ||
| 74 | + show_root_heading: false | ||
| 75 | + show_root_toc_entry: false | ||
| 76 | + members_order: source | ||
| 77 | + filters: ["!^_"] | ||
| 78 | + | ||
| 79 | + ## Permissions | ||
| 80 | + | ||
| 81 | + ::: pythonnative.native_modules.permissions | ||
| 82 | + options: | ||
| 83 | + show_root_heading: false | ||
| 84 | + show_root_toc_entry: false | ||
| 85 | + members_order: source | ||
| 86 | + filters: ["!^_"] | ||
| 87 | + | ||
| 88 | + ## App state | ||
| 89 | + | ||
| 90 | + ::: pythonnative.native_modules.app_state | ||
| 91 | + options: | ||
| 92 | + show_root_heading: false | ||
| 93 | + show_root_toc_entry: false | ||
| 94 | + members_order: source | ||
| 95 | + filters: ["!^_"] | ||
| 96 | + | ||
| 97 | + ## Network connectivity | ||
| 98 | + | ||
| 99 | + ::: pythonnative.native_modules.net_info | ||
| 100 | + options: | ||
| 101 | + show_root_heading: false | ||
| 102 | + show_root_toc_entry: false | ||
| 103 | + members_order: source | ||
| 104 | + filters: ["!^_"] | ||
| 105 | + | ||
| 106 | + ## Secure storage | ||
| 107 | + | ||
| 108 | + ::: pythonnative.native_modules.secure_store | ||
| 109 | + options: | ||
| 110 | + show_root_heading: false | ||
| 111 | + show_root_toc_entry: false | ||
| 112 | + members_order: source | ||
| 113 | + filters: ["!^_"] | ||
| 114 | + | ||
| 115 | + ## Battery | ||
| 116 | + | ||
| 117 | + ::: pythonnative.native_modules.battery | ||
| 118 | + options: | ||
| 119 | + show_root_heading: false | ||
| 120 | + show_root_toc_entry: false | ||
| 121 | + members_order: source | ||
| 122 | + filters: ["!^_"] | ||
| 123 | + | ||
| 124 | + ## Haptics & vibration | ||
| 125 | + | ||
| 126 | + ::: pythonnative.native_modules.haptics | ||
| 127 | + options: | ||
| 128 | + show_root_heading: false | ||
| 129 | + show_root_toc_entry: false | ||
| 130 | + members_order: source | ||
| 131 | + filters: ["!^_"] | ||
| 132 | + | ||
| 133 | + ## Biometrics | ||
| 134 | + | ||
| 135 | + ::: pythonnative.native_modules.biometrics | ||
| 136 | + options: | ||
| 137 | + show_root_heading: false | ||
| 138 | + show_root_toc_entry: false | ||
| 139 | + members_order: source | ||
| 140 | + filters: ["!^_"] | ||
| 141 | + | ||
| 50 | 142 | ## Next steps | |
| 51 | 143 | ||
| 52 | 144 | - See guidance and permission setup in [Native modules guide](../guides/native-modules.md). | |
| Back | FazBrowse Home | New Git URL |
0 commit comments