### Start Example App Packager Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/CONTRIBUTING.md Starts the Metro bundler for the example application, which is used to demonstrate and test the library's functionality. This command is run from the project root. ```shell yarn example start ``` -------------------------------- ### Run Example App on Web Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/CONTRIBUTING.md Builds and runs the example application in a web browser. This allows for testing the library's functionality in a web environment. ```shell yarn example web ``` -------------------------------- ### Install Project Dependencies Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/CONTRIBUTING.md Installs all necessary project dependencies for both the library and the example app using Yarn workspaces. This is a prerequisite for all development tasks. ```shell yarn ``` -------------------------------- ### Run Example App on Android Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/CONTRIBUTING.md Builds and runs the example application on an Android device or emulator. This command is essential for testing native code changes and library integration on Android. ```shell yarn example android ``` -------------------------------- ### Run Example App on iOS Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/CONTRIBUTING.md Builds and runs the example application on an iOS simulator or device. This is necessary for testing native code changes and library integration on iOS. ```shell yarn example ios ``` -------------------------------- ### Install React Native Skia List and Dependencies Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/README.md Installs the react-native-skia-list package along with its core dependencies: @shopify/react-native-skia, react-native-keyboard-controller, react-native-reanimated, and react-native-gesture-handler. ```sh yarn add react-native-skia-list @shopify/react-native-skia react-native-keyboard-controller react-native-reanimated react-native-gesture-handler ``` -------------------------------- ### SkiaFlatList Example for Virtualized Lists in React Native Source: https://context7.com/samuelscheit/react-native-skia-list/llms.txt This example demonstrates the usage of SkiaFlatList for creating high-performance virtualized lists in React Native. It shows how to set up data, extract keys, estimate item heights, transform raw data into renderable Skia paragraphs, and render these items efficiently. The component utilizes Skia's rendering engine for improved performance, especially with large datasets. Dependencies include @shopify/react-native-skia, react-native-skia-list, react-native-safe-area-context, react-native-gesture-handler, and react-native-keyboard-controller. ```tsx import type {} from "@shopify/react-native-skia/lib/typescript/src/renderer/HostComponents"; import { Skia } from "@shopify/react-native-skia"; import { SkiaFlatList } from "react-native-skia-list"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { GestureHandlerRootView } from "react-native-gesture-handler"; import { KeyboardProvider } from "react-native-keyboard-controller"; // Create reusable ParagraphBuilder for efficient text rendering const paragraphBuilder = Skia.ParagraphBuilder.Make({ textStyle: { color: Skia.Color("black"), fontSize: 20, }, }); export default function MessageList() { const safeArea = useSafeAreaInsets(); return ( Array.from({ length: 1000 }, (_, i) => ({ id: i, text: `Message ${i}` }))} keyExtractor={(item) => `${item.id}`} estimatedItemHeight={50} // Transform raw data into renderable format (called once per item) transformItem={(item, index, id, state) => { "worklet"; paragraphBuilder.reset(); return paragraphBuilder.addText(item.text).build(); }} // Render the visual representation (called on visibility changes) renderItem={(paragraph, index, state, element) => { "worklet"; const { width } = state.layout.value; paragraph.layout(width); const height = paragraph.getHeight(); if (!element) return height; // Height-only calculation element.addChild( SkiaDomApi.ParagraphNode({ paragraph, x: 0, y: 0, width, }) ); return height; }} onEndReached={() => { console.log("Load more items"); }} onEndReachedThreshold={500} style={{ flex: 1 }} /> ); } ``` -------------------------------- ### SkiaFlatList Component Usage Example Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/docs/docs/api/FlatList/index.md Demonstrates how to use the SkiaFlatList component to render a list of items. It shows how to initialize data, transform items for Skia rendering, and render each item with its layout and dimensions. Note that this component cannot render React Native components inside it. ```tsx // needed for SkiaDomApi type import type {} from "@shopify/react-native-skia/lib/typescript/src/renderer/HostComponents"; import { Skia } from "@shopify/react-native-skia"; import { SkiaFlatList } from "react-native-skia-list"; import { useSafeAreaInsets } from "react-native-safe-area-context"; // Create a Skia ParagraphBuilder that will be used to build the paragraph for each item const paragraphBuilder = Skia.ParagraphBuilder.Make({ textStyle: { color: Skia.Color("black"), fontSize: 20, }, }); export default function Test2() { const safeArea = useSafeAreaInsets(); return ( [0, 1, 2, 3, 4, 5, 6, 7, 8]} // To optimize performance for the initial mount you can provide a transformItem function // It will be called once for each item when it is mounted the first time transformItem={(item, index, id, state) => { "worklet"; paragraphBuilder.reset(); // reuses the paragraphBuilder for each item return paragraphBuilder.addText(`Item ${item}`).build(); }} // renderItem will be called whenever an item visibility changes renderItem={(item, index, state, element) => { "worklet"; const { width } = state.layout.value; item.layout(width); // calculates the paragraph layout const height = item.getHeight(); // gets the height of the paragraph // element is a Skia.GroupNode or will be undefined if only the height of the element is needed if (!element) return height; element.addChild( // see the following link for all element types // https://github.com/Shopify/react-native-skia/blob/5c38b27d72cea9c158290adb7d23c6109369ac2f/packages/skia/src/renderer/HostComponents.ts#L72-L191 SkiaDomApi.ParagraphNode({ paragraph: item, x: 0, y: 0, width, }), ); return height; }} /> ); } ``` -------------------------------- ### Manage SkiaScrollView State with useSkiaScrollView Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/docs/docs/api/ScrollView/index.md This example shows how to use the `useSkiaScrollView` hook to initialize and manage the state of a SkiaScrollView. The hook returns a state object that can be used to manipulate the scroll view's content and appearance. ```tsx const state = useSkiaScrollView({ height: 1000 }); state.content.value.addChild(SkiaDomApi.RectNode({ width: 100, height: 100, x: 0, y: 0 })); ``` -------------------------------- ### SkiaScrollView State Management with useSkiaScrollView Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/docs/docs/api/ScrollView/index.md Illustrates manual state management for SkiaScrollView using `useSkiaScrollView`. This example demonstrates how to access and modify the scroll view's transformation matrix, for instance, to skew the list content. ```tsx const list = useSkiaScrollView({ height: 1000 }); // do something with the list, e.g. skew the list content: list.matrix.value[1] = 0.1; ``` -------------------------------- ### Dynamic Content Updates with useSkiaScrollView Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/docs/docs/api/ScrollView/index.md Demonstrates frequent updates to SkiaScrollView content using `useSkiaScrollView` for performance. This example adds and removes Skia RectNodes dynamically at intervals, leveraging imperative updates to bypass React's reconciliation. ```tsx const state = useSkiaScrollView({ height: 1000 }); const content = content.value; let previous = null; // do something with the list content, e.g. add a new rect every 10ms setInterval(() => { if (previous) content.removeChild(previous); previous = SkiaDomApi.RectNode({ width: 100, height: 100, x: Math.random() * 1000, y: Math.random() * 1000 }); content.addChild(previous); }, 10); ``` -------------------------------- ### Get Item from Touch in react-native-skia-list Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/docs/docs/api/FlatList/index.md Returns the item at a specific touch position. It receives the touch event object with 'x' and 'y' coordinates. Returns undefined if no item is found. ```javascript getItemFromTouch: (e) => TapResult | undefined ``` -------------------------------- ### Control Skia ScrollView Imperatively with useSkiaScrollView Source: https://context7.com/samuelscheit/react-native-skia-list/llms.txt Utilize the useSkiaScrollView hook for imperative control over a Skia scroll view. This hook provides direct access to the scroll view's content for DOM manipulation, enabling dynamic addition and removal of elements, and triggering redraws for animated updates. Includes setup for an interval to demonstrate dynamic content. ```tsx import { useSkiaScrollView, SkiaScrollView } from "react-native-skia-list"; import { Skia } from "@shopify/react-native-skia"; import { useEffect } from "react"; function ImperativeScrollView() { const state = useSkiaScrollView({ height: 3000, inverted: false, bounces: true, }); useEffect(() => { const content = state.content.value; // Add elements imperatively for high-performance updates let yOffset = 0; const colors = ["red", "blue", "green", "orange", "purple"]; for (let i = 0; i < 30; i++) { const rect = SkiaDomApi.RectNode({ x: 20, y: yOffset, width: 300, height: 80, color: Skia.Color(colors[i % colors.length]), }); content.addChild(rect); yOffset += 100; } // Trigger redraw after adding elements state.redraw(); // Animate dynamic content updates const interval = setInterval(() => { const randomRect = SkiaDomApi.RectNode({ x: Math.random() * 300, y: Math.random() * 2900, width: 50, height: 50, color: Skia.Color("yellow"), }); content.addChild(randomRect); state.redraw(); // Remove after 1 second setTimeout(() => { content.removeChild(randomRect); state.redraw(); }, 1000); }, 2000); return () => clearInterval(interval); }, []); return ; } ``` -------------------------------- ### SkiaScrollView with Children and Height Prop Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/docs/docs/api/ScrollView/index.md An example of using SkiaScrollView with direct children elements and setting the `height` prop. This is suitable for simpler scenarios where manual state management is not required. Ensure `height` is specified for scrollability. ```tsx ``` -------------------------------- ### Basic Usage of SkiaFlatList in React Native Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/README.md Demonstrates the basic usage of the SkiaFlatList component in a React Native application. It shows how to set up necessary providers, define initial data, and implement item transformation and rendering using Skia's ParagraphBuilder and SkiaDomApi. ```tsx // needed for SkiaDomApi type import type {} from "@shopify/react-native-skia/lib/typescript/src/renderer/HostComponents"; import { Skia } from "@shopify/react-native-skia"; import { SkiaFlatList } from "react-native-skia-list"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { GestureHandlerRootView } from "react-native-gesture-handler"; import { KeyboardProvider } from "react-native-keyboard-controller"; // Create a Skia ParagraphBuilder that will be used to build the text for each item const paragraphBuilder = Skia.ParagraphBuilder.Make({ textStyle: { color: Skia.Color("black"), fontSize: 20, }, }); export default function Test() { const safeArea = useSafeAreaInsets(); return ( [0, 1, 2, 3, 4, 5, 6, 7, 8]} // To optimize performance for the initial mount you can provide a transformItem function // It will be called once for each item when it is mounted the first time transformItem={(item, index, id, state) => { "worklet"; paragraphBuilder.reset(); // reuses the paragraphBuilder for each item const text = `Item ${item}`; return paragraphBuilder.addText(text).build(); }} // renderItem will be called whenever an item visibility changes renderItem={(item, index, state, element) => { "worklet"; const { width } = state.layout.value; item.layout(width); // calculates the paragraph layout const height = item.getHeight(); // gets the height of the paragraph // element is a Skia.GroupNode or will be undefined if only the height of the element is needed if (!element) return height; element.addChild( // see the following link for all element types // https://github.com/Shopify/react-native-skia/blob/5c38b27d72cea9c158290adb7d23c6109369ac2f/packages/skia/src/renderer/HostComponents.ts#L72-L191 SkiaDomApi.ParagraphNode({ paragraph: item, x: 0, y: 0, width, }) ); return height; }} /> ); } ``` -------------------------------- ### List Data Manipulation with useSkiaFlatList Source: https://context7.com/samuelscheit/react-native-skia-list/llms.txt Illustrates various imperative methods provided by `useSkiaFlatList` for dynamic list management, including adding, inserting, removing, and updating items with optional animations. ```tsx import { useSkiaFlatList } from "react-native-skia-list"; function DynamicList() { const list = useSkiaFlatList({ initialData: () => [ { id: "1", text: "Item 1" }, { id: "2", text: "Item 2" }, ], keyExtractor: (item) => item.id, renderItem: (item, index) => { "worklet"; return 50; // Simple height-only example }, }); // Add item to end const addItem = () => { list.append({ id: Date.now().toString(), text: "New Item" }, true); // animated }; // Add item to start const prependItem = () => { list.prepend({ id: Date.now().toString(), text: "First Item" }, true); }; // Insert at specific position const insertItem = () => { list.insertAt({ id: Date.now().toString(), text: "Middle Item" }, 2, true); // index 2, animated }; // Remove by index const removeByIndex = () => { list.removeAt(0, true); // Remove first item, animated }; // Remove by item reference const removeByItem = (item) => { list.removeItem(item, true); }; // Remove by id const removeById = (id) => { list.removeItemId(id, true); }; // Update existing item (must have same id) const updateItem = () => { list.updateItem({ id: "1", text: "Updated Item 1" }); }; // Replace all data const resetList = () => { list.resetData([ { id: "a", text: "New Item A" }, { id: "b", text: "New Item B" }, ]); }; // Force redraw specific item const redrawItem = (index) => { list.redrawItem(index); }; return ; } ``` -------------------------------- ### useSkiaFlatList Hook for Chat Interface Source: https://context7.com/samuelscheit/react-native-skia-list/llms.txt Demonstrates using the `useSkiaFlatList` hook to manage a chat-like list with inverted rendering and dynamic message appending. It utilizes Skia's ParagraphBuilder for text rendering and SkiaDomApi for node manipulation. ```tsx import { useSkiaFlatList, SkiaFlatList } from "react-native-skia-list"; import { Skia } from "@shopify/react-native-skia"; import { useEffect } from "react"; const paragraphBuilder = Skia.ParagraphBuilder.Make({ textStyle: { color: Skia.Color("black"), fontSize: 16 }, }); function ChatScreen() { const list = useSkiaFlatList({ initialData: () => [], estimatedItemHeight: 60, inverted: true, // Chat-style bottom-to-top maintainVisibleContentPosition: true, keyExtractor: (item) => item.id, transformItem: (item, index, id, state) => { "worklet"; paragraphBuilder.reset(); return paragraphBuilder.addText(item.message).build(); }, renderItem: (paragraph, index, state, element) => { "worklet"; const { width } = state.layout.value; paragraph.layout(width - 20); const height = paragraph.getHeight() + 20; if (!element) return height; element.addChild( SkiaDomApi.ParagraphNode({ paragraph, x: 10, y: 10, width: width - 20, }) ); return height; }, onTap: (result, state) => { "worklet"; console.log("Tapped item:", result.item, "at index:", result.index); }, }); useEffect(() => { // Simulate receiving new messages const interval = setInterval(() => { list.prepend({ id: Date.now().toString(), message: `New message at ${new Date().toLocaleTimeString()}`, }, true); // animated }, 3000); return () => clearInterval(interval); }, []); return ; } ``` -------------------------------- ### Publish New Versions Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/CONTRIBUTING.md Utilizes release-it to automate the process of publishing new versions of the library to npm. This includes version bumping, tagging, and release creation. ```shell yarn release ``` -------------------------------- ### Run Unit Tests Source: https://github.com/samuelscheit/react-native-skia-list/blob/main/CONTRIBUTING.md Executes all defined unit tests using Jest. This is crucial for verifying the correctness of code changes and ensuring no regressions are introduced. ```shell yarn test ``` -------------------------------- ### Programmatic Scrolling Control with useSkiaFlatList Source: https://context7.com/samuelscheit/react-native-skia-list/llms.txt Demonstrates how to programmatically control the scroll position of a `SkiaFlatList` using methods like `scrollToStart`, `scrollToEnd`, `scrollToIndex`, and `scrollToItem`. These methods support smooth scrolling animations. ```tsx import { useSkiaFlatList } from "react-native-skia-list"; import { Button, View } from "react-native"; function ScrollableList() { const list = useSkiaFlatList({ initialData: () => Array.from({ length: 100 }, (_, i) => ({ id: `${i}`, text: `Item ${i}` })), keyExtractor: (item) => item.id, renderItem: (item, index) => { "worklet"; return 60; }, }); return (