From aec3c5d9d7f7cd693dd0aa4d4c94bcaa35ee755e Mon Sep 17 00:00:00 2001 From: Julien BIANCHI Date: Tue, 3 Nov 2026 23:06:33 +0100 Subject: [PATCH] docs(mobile): document custom pull-to-refresh indicators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents the `refresh-indicator` and `refresh-indicator-options` list attributes and `NativeList::refreshIndicator()`, and how a plugin registers an indicator from its init function on Android and iOS. The feature is not released yet: both sections say it needs the `nativephp/mobile-ui` release that includes NativePHP/mobile-ui#198. 🤖 Généré avec Claude Code --- .../docs/mobile/4/edge-components/list.md | 40 +++++++++++++++++++ .../docs/mobile/4/plugins/ui-components.md | 35 ++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/resources/views/docs/mobile/4/edge-components/list.md b/resources/views/docs/mobile/4/edge-components/list.md index 2a58f408..394f9f06 100644 --- a/resources/views/docs/mobile/4/edge-components/list.md +++ b/resources/views/docs/mobile/4/edge-components/list.md @@ -35,9 +35,47 @@ Pair with [``](#list-item) for Material3 list rows, or use any inset-grouped style (rounded cards — iOS `.insetGrouped`, grouped cards on Android); `plain` keeps flat rows with plain section headers instead (optional, boolean, default: `false`) - `on-refresh` - Component method called on pull-to-refresh (optional, string) [iOS] +- `refresh-indicator` - Name of a plugin-provided indicator to show instead of the platform spinner when the list is + pulled (optional, string). See [Custom pull-to-refresh indicators](#custom-pull-to-refresh-indicators) +- `refresh-indicator-options` - Settings passed to that indicator (optional, array). Bind it: + `:refresh-indicator-options="[...]"` - `on-end-reached` - Component method called when the user nears the end of the list (optional, string) - `end-reached-buffer` - Number of items from the end at which `on-end-reached` is triggered (optional, integer, default: `3`) +## Custom pull-to-refresh indicators + +Not in `nativephp/mobile-ui` 0.9.0; it needs the release that includes +[NativePHP/mobile-ui#198](https://github.com/NativePHP/mobile-ui/pull/198). + +A list with `on-refresh` shows the platform spinner when pulled. `refresh-indicator` replaces it with an indicator a +plugin provides, such as an animated logo, and `refresh-indicator-options` passes that indicator its settings: + +@verbatim +```blade + + @foreach ($posts as $post) + + @endforeach + +``` +@endverbatim + +If no installed plugin provides an indicator with that name, the list keeps the platform spinner, so a screen can name +an indicator whether or not the app ships it. `nativephp/mobile-ui` ships no indicator of its own: to write one, see +[Pull-to-refresh indicators](../plugins/ui-components#pull-to-refresh-indicators). + +On Android, the indicator fills a 48dp disc styled like the stock indicator, which slides down with the pull. On iOS, +the list runs its own pull-to-refresh and the indicator fills a 40pt square in the gap the pull opens above the rows. +When the pull crosses the refresh threshold, the indicator bounces and the device gives a haptic tap, as the platform +spinners do. + +Only `` supports custom indicators: [``](refreshable) always shows the platform +spinner. + ## Children @@ -343,6 +381,8 @@ NativeList::make( - `showsIndicators(bool $value = true)` - Show scroll indicators - `separator(bool $value = true)` - Render dividers between rows - `onRefresh(string $method)` - Pull-to-refresh handler +- `refreshIndicator(string $name, array $options = [])` - Plugin-provided pull-to-refresh indicator and its settings, + e.g. `->refreshIndicator('lottie', ['asset' => 'animations/logo.lottie'])` - `onEndReached(string $method)` - End-reached handler - `endReachedBuffer(int $items)` - Number of items from the end at which the end-reached handler is triggered (default: `3`) diff --git a/resources/views/docs/mobile/4/plugins/ui-components.md b/resources/views/docs/mobile/4/plugins/ui-components.md index d688d7b8..80013de8 100644 --- a/resources/views/docs/mobile/4/plugins/ui-components.md +++ b/resources/views/docs/mobile/4/plugins/ui-components.md @@ -399,6 +399,41 @@ Because the native half is generated at build time, **a newly added component ne `composer update`. Adding a component to the manifest and only restarting PHP gives you an element that serializes fine and renders nothing. +## Pull-to-Refresh Indicators + +Not in `nativephp/mobile-ui` 0.9.0; it needs the release that includes +[NativePHP/mobile-ui#198](https://github.com/NativePHP/mobile-ui/pull/198). + +A plugin can also provide an indicator that a [``](../edge-components/list#custom-pull-to-refresh-indicators) +shows instead of the platform spinner when an app names it with `refresh-indicator`. This is not a component: register +it yourself, by name, from the plugin's `init_function` (see [Advanced Configuration](advanced-configuration)): + +```kotlin +import com.nativephp.plugins.native_ui.NativeUIRefreshIndicators + +NativeUIRefreshIndicators.register("lottie") { scope -> + // scope.progress(), scope.isRefreshing, scope.options["asset"] +} +``` + +```swift +NativeUIRefreshIndicators.shared.register("lottie") { scope in + // scope.progress, scope.isRefreshing, scope.options["asset"] + AnyView(MyIndicator(scope: scope)) +} +``` + +The scope gives the indicator: + +- `progress` - How far the list is pulled, from `0` at rest to `1` exactly where letting go starts a refresh, so an + indicator that looks complete at `1` tells the user they can let go. On Android it is a function: call it while + drawing so a pull doesn't recompose +- `isRefreshing` - Whether the refresh the pull triggered is running +- `options` - The app's `refresh-indicator-options`, decoded from JSON + +The list sizes and places the indicator: it fills a 48dp disc on Android and a 40pt square on iOS. The list also +handles the bounce and haptic tap when the pull crosses the threshold, so the indicator only draws. + ## Using It in an App Install and register the plugin the [usual way](./using-plugins), then the element is just an element: