@utilitywarehouse/hearth-react-native 0.27.2 → 0.28.0-testid-fix-1
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.
- package/.turbo/turbo-build.log +5 -4
- package/.turbo/turbo-lint.log +70 -69
- package/CHANGELOG.md +149 -0
- package/build/components/Button/ButtonRoot.js +8 -0
- package/build/components/Combobox/Combobox.context.d.ts +13 -0
- package/build/components/Combobox/Combobox.context.js +9 -0
- package/build/components/Combobox/Combobox.d.ts +6 -0
- package/build/components/Combobox/Combobox.js +246 -0
- package/build/components/Combobox/Combobox.props.d.ts +180 -0
- package/build/components/Combobox/Combobox.props.js +1 -0
- package/build/components/Combobox/ComboboxOption.d.ts +6 -0
- package/build/components/Combobox/ComboboxOption.js +56 -0
- package/build/components/Combobox/index.d.ts +4 -0
- package/build/components/Combobox/index.js +3 -0
- package/build/components/DatePicker/TimePicker.d.ts +3 -0
- package/build/components/DatePicker/TimePicker.js +84 -0
- package/build/components/DatePicker/time-picker/animated-math.d.ts +4 -0
- package/build/components/DatePicker/time-picker/animated-math.js +19 -0
- package/build/components/DatePicker/time-picker/period-native.d.ts +6 -0
- package/build/components/DatePicker/time-picker/period-native.js +17 -0
- package/build/components/DatePicker/time-picker/period-picker.d.ts +6 -0
- package/build/components/DatePicker/time-picker/period-picker.js +10 -0
- package/build/components/DatePicker/time-picker/period-web.d.ts +6 -0
- package/build/components/DatePicker/time-picker/period-web.js +21 -0
- package/build/components/DatePicker/time-picker/wheel-native.d.ts +8 -0
- package/build/components/DatePicker/time-picker/wheel-native.js +19 -0
- package/build/components/DatePicker/time-picker/wheel-picker/index.d.ts +2 -0
- package/build/components/DatePicker/time-picker/wheel-picker/index.js +2 -0
- package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker-item.d.ts +16 -0
- package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker-item.js +97 -0
- package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker.d.ts +21 -0
- package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker.js +88 -0
- package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker.style.d.ts +23 -0
- package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker.style.js +21 -0
- package/build/components/DatePicker/time-picker/wheel-web.d.ts +8 -0
- package/build/components/DatePicker/time-picker/wheel-web.js +146 -0
- package/build/components/DatePicker/time-picker/wheel.d.ts +8 -0
- package/build/components/DatePicker/time-picker/wheel.js +10 -0
- package/build/components/List/List.js +2 -2
- package/build/components/Modal/Modal.js +31 -42
- package/build/components/Modal/Modal.web.js +3 -3
- package/build/components/Pagination/Pagination.d.ts +6 -0
- package/build/components/Pagination/Pagination.js +125 -0
- package/build/components/Pagination/Pagination.props.d.ts +26 -0
- package/build/components/Pagination/Pagination.props.js +1 -0
- package/build/components/Pagination/Pagination.utils.d.ts +2 -0
- package/build/components/Pagination/Pagination.utils.js +20 -0
- package/build/components/Pagination/Pagination.utils.test.d.ts +1 -0
- package/build/components/Pagination/Pagination.utils.test.js +16 -0
- package/build/components/Pagination/index.d.ts +2 -0
- package/build/components/Pagination/index.js +1 -0
- package/build/components/SafeAreaView/SafeAreaView.d.ts +5 -0
- package/build/components/SafeAreaView/SafeAreaView.js +117 -0
- package/build/components/SafeAreaView/SafeAreaView.props.d.ts +17 -0
- package/build/components/SafeAreaView/SafeAreaView.props.js +1 -0
- package/build/components/SafeAreaView/index.d.ts +2 -0
- package/build/components/SafeAreaView/index.js +1 -0
- package/build/components/Select/Select.d.ts +1 -1
- package/build/components/Select/Select.js +6 -5
- package/build/components/Select/Select.props.d.ts +4 -0
- package/build/components/Select/SelectOption.d.ts +1 -1
- package/build/components/Select/SelectOption.js +2 -2
- package/build/components/Table/Table.context.d.ts +12 -0
- package/build/components/Table/Table.context.js +9 -0
- package/build/components/Table/Table.d.ts +6 -0
- package/build/components/Table/Table.js +71 -0
- package/build/components/Table/Table.props.d.ts +56 -0
- package/build/components/Table/Table.props.js +1 -0
- package/build/components/Table/Table.utils.d.ts +5 -0
- package/build/components/Table/Table.utils.js +48 -0
- package/build/components/Table/Table.utils.test.d.ts +1 -0
- package/build/components/Table/Table.utils.test.js +71 -0
- package/build/components/Table/TableBody.d.ts +6 -0
- package/build/components/Table/TableBody.js +16 -0
- package/build/components/Table/TableCell.d.ts +10 -0
- package/build/components/Table/TableCell.js +44 -0
- package/build/components/Table/TableHeader.d.ts +6 -0
- package/build/components/Table/TableHeader.js +24 -0
- package/build/components/Table/TableHeaderCell.d.ts +10 -0
- package/build/components/Table/TableHeaderCell.js +97 -0
- package/build/components/Table/TablePagination.d.ts +6 -0
- package/build/components/Table/TablePagination.js +7 -0
- package/build/components/Table/TableRow.d.ts +8 -0
- package/build/components/Table/TableRow.js +25 -0
- package/build/components/Table/index.d.ts +8 -0
- package/build/components/Table/index.js +7 -0
- package/build/components/Timeline/Timeline.d.ts +6 -0
- package/build/components/Timeline/Timeline.js +34 -0
- package/build/components/Timeline/Timeline.props.d.ts +47 -0
- package/build/components/Timeline/Timeline.props.js +1 -0
- package/build/components/Timeline/TimelineItem.d.ts +6 -0
- package/build/components/Timeline/TimelineItem.js +235 -0
- package/build/components/Timeline/index.d.ts +3 -0
- package/build/components/Timeline/index.js +2 -0
- package/build/components/VerificationInput/VerificationInput.js +3 -3
- package/build/components/index.d.ts +5 -0
- package/build/components/index.js +5 -0
- package/build/tokens/components/dark/timeline.d.ts +2 -2
- package/build/tokens/components/dark/timeline.js +2 -2
- package/docs/components/AllComponents.web.tsx +106 -23
- package/docs/llm-docs/unistyles-llms-full.txt +1132 -534
- package/docs/llm-docs/unistyles-llms-small.txt +37 -37
- package/package.json +4 -4
- package/src/components/Button/Button.stories.tsx +43 -7
- package/src/components/Button/ButtonRoot.tsx +8 -0
- package/src/components/Combobox/Combobox.context.ts +26 -0
- package/src/components/Combobox/Combobox.docs.mdx +277 -0
- package/src/components/Combobox/Combobox.figma.tsx +60 -0
- package/src/components/Combobox/Combobox.props.ts +187 -0
- package/src/components/Combobox/Combobox.stories.tsx +233 -0
- package/src/components/Combobox/Combobox.tsx +446 -0
- package/src/components/Combobox/ComboboxOption.tsx +100 -0
- package/src/components/Combobox/index.ts +9 -0
- package/src/components/List/List.tsx +5 -4
- package/src/components/Modal/Modal.tsx +67 -74
- package/src/components/Modal/Modal.web.tsx +3 -3
- package/src/components/Pagination/Pagination.docs.mdx +99 -0
- package/src/components/Pagination/Pagination.figma.tsx +20 -0
- package/src/components/Pagination/Pagination.props.ts +28 -0
- package/src/components/Pagination/Pagination.stories.tsx +88 -0
- package/src/components/Pagination/Pagination.tsx +248 -0
- package/src/components/Pagination/Pagination.utils.test.ts +20 -0
- package/src/components/Pagination/Pagination.utils.ts +37 -0
- package/src/components/Pagination/index.ts +2 -0
- package/src/components/SafeAreaView/SafeAreaView.props.ts +20 -0
- package/src/components/SafeAreaView/SafeAreaView.tsx +173 -0
- package/src/components/SafeAreaView/index.ts +2 -0
- package/src/components/Select/Select.props.ts +4 -0
- package/src/components/Select/Select.tsx +35 -28
- package/src/components/Select/SelectOption.tsx +2 -0
- package/src/components/Table/Table.context.tsx +23 -0
- package/src/components/Table/Table.docs.mdx +239 -0
- package/src/components/Table/Table.figma.tsx +65 -0
- package/src/components/Table/Table.props.ts +65 -0
- package/src/components/Table/Table.stories.tsx +399 -0
- package/src/components/Table/Table.tsx +127 -0
- package/src/components/Table/Table.utils.test.ts +82 -0
- package/src/components/Table/Table.utils.ts +72 -0
- package/src/components/Table/TableBody.tsx +25 -0
- package/src/components/Table/TableCell.tsx +67 -0
- package/src/components/Table/TableHeader.tsx +41 -0
- package/src/components/Table/TableHeaderCell.tsx +136 -0
- package/src/components/Table/TablePagination.tsx +10 -0
- package/src/components/Table/TableRow.tsx +42 -0
- package/src/components/Table/index.ts +16 -0
- package/src/components/Timeline/Timeline.docs.mdx +177 -0
- package/src/components/Timeline/Timeline.figma.tsx +89 -0
- package/src/components/Timeline/Timeline.props.ts +51 -0
- package/src/components/Timeline/Timeline.stories.tsx +102 -0
- package/src/components/Timeline/Timeline.tsx +48 -0
- package/src/components/Timeline/TimelineItem.tsx +293 -0
- package/src/components/Timeline/index.ts +9 -0
- package/src/components/VerificationInput/VerificationInput.tsx +3 -0
- package/src/components/index.ts +5 -0
- package/src/tokens/components/dark/timeline.ts +2 -2
|
@@ -16,6 +16,8 @@ Each configuration is optional but enables advanced features, which are explaine
|
|
|
16
16
|
|
|
17
17
|
### Themes (Optional)
|
|
18
18
|
|
|
19
|
+
[Section titled “Themes (Optional)”](#themes-optional)
|
|
20
|
+
|
|
19
21
|
`Themes` is a JavaScript object where the keys represent unique theme names, and the values are the corresponding theme definitions. For more details, refer to the [theming](/v3/guides/theming) guide.
|
|
20
22
|
|
|
21
23
|
unistyles.ts
|
|
@@ -53,6 +55,8 @@ Unistyles supports any dynamic theme and doesn’t enforce a specific structure.
|
|
|
53
55
|
|
|
54
56
|
### Breakpoints (Optional)
|
|
55
57
|
|
|
58
|
+
[Section titled “Breakpoints (Optional)”](#breakpoints-optional)
|
|
59
|
+
|
|
56
60
|
`Breakpoints` is a JavaScript object where the keys are unique breakpoint names and the values are the corresponding breakpoint values (numbers). Be sure to register at least one breakpoint with a value of 0, as it’s required to simulate the cascading behavior of CSS media queries.
|
|
57
61
|
|
|
58
62
|
unistyles.ts
|
|
@@ -70,6 +74,8 @@ const breakpoints = {
|
|
|
70
74
|
|
|
71
75
|
### Settings (Optional)
|
|
72
76
|
|
|
77
|
+
[Section titled “Settings (Optional)”](#settings-optional)
|
|
78
|
+
|
|
73
79
|
The `Settings` object has been simplified, and in the most recent version, it supports only four properties:
|
|
74
80
|
|
|
75
81
|
* **`adaptiveThemes`** – a boolean that enables or disables adaptive themes [learn more](/v3/guides/theming#adaptive-themes)
|
|
@@ -108,6 +114,8 @@ In the Unistyles 3.0 setting both `initialTheme` and `adaptiveThemes` will cause
|
|
|
108
114
|
|
|
109
115
|
### TypeScript Types (Optional)
|
|
110
116
|
|
|
117
|
+
[Section titled “TypeScript Types (Optional)”](#typescript-types-optional)
|
|
118
|
+
|
|
111
119
|
If your repository is using TypeScript, it is highly recommended to override the library types for optimal autocomplete and type safety regarding your themes and breakpoints:
|
|
112
120
|
|
|
113
121
|
unistyles.ts
|
|
@@ -125,6 +133,8 @@ declare module 'react-native-unistyles' {
|
|
|
125
133
|
|
|
126
134
|
### Set configuration
|
|
127
135
|
|
|
136
|
+
[Section titled “Set configuration”](#set-configuration)
|
|
137
|
+
|
|
128
138
|
The final step in the configuration is to set all the options by calling the `StyleSheet.configure` function:
|
|
129
139
|
|
|
130
140
|
unistyles.ts
|
|
@@ -150,6 +160,8 @@ For expo router users, please refer to the [Expo Router guide](/v3/guides/expo-r
|
|
|
150
160
|
|
|
151
161
|
### Full example
|
|
152
162
|
|
|
163
|
+
[Section titled “Full example”](#full-example)
|
|
164
|
+
|
|
153
165
|
unistyles.ts
|
|
154
166
|
|
|
155
167
|
```tsx
|
|
@@ -216,7 +228,9 @@ We’ve made Unistyles incredibly easy to use. You no longer need the `useStyle`
|
|
|
216
228
|
|
|
217
229
|
### Prerequisites
|
|
218
230
|
|
|
219
|
-
|
|
231
|
+
[Section titled “Prerequisites”](#prerequisites)
|
|
232
|
+
|
|
233
|
+
Unistyles 3.0 is tightly integrated with `Fabric` and the latest versions of React Native. Therefore, you must use the **New Architecture** and at least **React Native 0.78.0**. Additionally, Unistyles relies on `react-native-nitro-modules`.
|
|
220
234
|
|
|
221
235
|
Note
|
|
222
236
|
|
|
@@ -236,16 +250,27 @@ Since Unistyles relies on `Fabric`, it cannot run on the `Old Architecture` or o
|
|
|
236
250
|
|
|
237
251
|
### Installation
|
|
238
252
|
|
|
253
|
+
[Section titled “Installation”](#installation)
|
|
254
|
+
|
|
239
255
|
Install Unistyles and its dependencies
|
|
240
256
|
|
|
241
257
|
```shell
|
|
242
|
-
yarn add react-native-unistyles react-native-nitro-modules
|
|
258
|
+
yarn add react-native-unistyles react-native-nitro-modules
|
|
243
259
|
```
|
|
244
260
|
|
|
245
261
|
Caution
|
|
246
262
|
|
|
247
263
|
To avoid unexpected behaviors always use a fixed version of `react-native-nitro-modules`. Check compatibility table [here](https://github.com/jpudysz/react-native-unistyles?tab=readme-ov-file#installation).
|
|
248
264
|
|
|
265
|
+
react-native-edge-to-edge is optional since v3.1.0
|
|
266
|
+
|
|
267
|
+
`react-native-edge-to-edge` is no longer a required dependency. We **strongly recommend** setting `edgeToEdgeEnabled=true` in your `android/gradle.properties` — it enforces translucent system bars on modals, disables legacy StatusBar hacks, and enables additional React Native core fixes.
|
|
268
|
+
|
|
269
|
+
* **Expo SDK 54+**: Already enabled automatically
|
|
270
|
+
* **Expo SDK 53 / Bare React Native**: Add `edgeToEdgeEnabled=true` to `android/gradle.properties`
|
|
271
|
+
|
|
272
|
+
You can still install `react-native-edge-to-edge` alongside this property for ecosystem compatibility (e.g. `react-native-bootsplash`, `react-native-permissions`). Learn more [here](/v3/other/dependencies).
|
|
273
|
+
|
|
249
274
|
Add babel plugin:
|
|
250
275
|
|
|
251
276
|
babel.config.js
|
|
@@ -327,13 +352,15 @@ Finish installation based on your platform:
|
|
|
327
352
|
|
|
328
353
|
### As easy as React Native StyleSheet
|
|
329
354
|
|
|
355
|
+
[Section titled “As easy as React Native StyleSheet”](#as-easy-as-react-native-stylesheet)
|
|
356
|
+
|
|
330
357
|
Getting started with Unistyles couldn’t be easier. Simply replace React Native’s `StyleSheet` with the `StyleSheet` exported from Unistyles. From that moment, you’ll be using a `StyleSheet` with superpowers 🦸🏼♂️.
|
|
331
358
|
|
|
332
359
|
Example.tsx
|
|
333
360
|
|
|
334
|
-
```
|
|
335
|
-
import { StyleSheet } from 'react-native'
|
|
336
|
-
import { StyleSheet } from 'react-native-unistyles'
|
|
361
|
+
```diff
|
|
362
|
+
-import { StyleSheet } from 'react-native'
|
|
363
|
+
+import { StyleSheet } from 'react-native-unistyles'
|
|
337
364
|
|
|
338
365
|
|
|
339
366
|
const MyComponent = () => {
|
|
@@ -372,6 +399,8 @@ To get the most out of Unistyles, it’s important to understand how it works an
|
|
|
372
399
|
|
|
373
400
|
### 1. StyleSheets
|
|
374
401
|
|
|
402
|
+
[Section titled “1. StyleSheets”](#1-stylesheets)
|
|
403
|
+
|
|
375
404
|
A typical app consists of many `StyleSheets`. A `StyleSheet` is a JavaScript object that holds one or many styles. Each style is associated with a native view. What’s more important is that each `StyleSheet` is unique, tailored to the needs of the view, or to a shared component.
|
|
376
405
|
|
|
377
406
|

|
|
@@ -380,6 +409,8 @@ Your app’s StyleSheets
|
|
|
380
409
|
|
|
381
410
|
### 2. Babel plugin: dependencies
|
|
382
411
|
|
|
412
|
+
[Section titled “2. Babel plugin: dependencies”](#2-babel-plugin-dependencies)
|
|
413
|
+
|
|
383
414
|
Unistyles needs to understand your `StyleSheet` dependencies in order to update them only when necessary. This process begins when Babel transforms your app’s code. At this stage, the Unistyles Babel plugin scans your `StyleSheets` and determines the dependencies for each style:
|
|
384
415
|
|
|
385
416
|
```ts
|
|
@@ -402,6 +433,8 @@ const styles = StyleSheet.create((theme, rt) => ({
|
|
|
402
433
|
|
|
403
434
|
### 3. Babel plugin: component factory
|
|
404
435
|
|
|
436
|
+
[Section titled “3. Babel plugin: component factory”](#3-babel-plugin-component-factory)
|
|
437
|
+
|
|
405
438
|
As you already know, Unistyles has no components. This means your native view hierarchy remains exactly the same as in your original code. The Babel plugin processes your components through our component factory to borrow `refs` and bind the `ShadowNode` with `Unistyle`.
|
|
406
439
|
|
|
407
440
|
You might be wondering, what is `Unistyle`? We refer to it as your `StyleSheet` style that has been parsed by the Unistyles compiler, and with the attached `C++` state.
|
|
@@ -416,6 +449,8 @@ Learn more on how the Babel plugin works [here](/v3/other/babel-plugin).
|
|
|
416
449
|
|
|
417
450
|
### 4. StyleSheet registry
|
|
418
451
|
|
|
452
|
+
[Section titled “4. StyleSheet registry”](#4-stylesheet-registry)
|
|
453
|
+
|
|
419
454
|
We don’t just extract metadata from your styles. We do the same for your `StyleSheet`. On the C++ side, we know exactly which `StyleSheet` is static, which depends on a `theme`, and which `Unistyles` it contains. At this point, your app’s `StyleSheets` are reconstructed on the C++ side and stored in native C++ `StyleSheets`, which contain the parsed `Unistyles`.
|
|
420
455
|
|
|
421
456
|

|
|
@@ -430,6 +465,8 @@ Unistyles workflow
|
|
|
430
465
|
|
|
431
466
|
### 5. Reacting to events
|
|
432
467
|
|
|
468
|
+
[Section titled “5. Reacting to events”](#5-reacting-to-events)
|
|
469
|
+
|
|
433
470
|
When you access your `StyleSheet` styles in your component, you’ll get a regular JS object as expected. If your component re-renders, we simply return the same `Unistyle` that’s already parsed and stored in the cache.
|
|
434
471
|
|
|
435
472
|
To visualize the true power of `Unistyles`, imagine that some event occurs, such as:
|
|
@@ -450,6 +487,8 @@ Affected styles are then re-computed to reflect the new state of your app.
|
|
|
450
487
|
|
|
451
488
|
### 6. Shadow Tree updates
|
|
452
489
|
|
|
490
|
+
[Section titled “6. Shadow Tree updates”](#6-shadow-tree-updates)
|
|
491
|
+
|
|
453
492
|
With the list of affected styles, we can now browse the `ShadowRegistry`, where we keep the bindings between `ShadowNode` and `Unistyles`. In other words, we know which `component` relies on which `style`. With all this information, we can translate the update into atomic `ShadowTree` instructions.
|
|
454
493
|
|
|
455
494
|
With Unistyles 2.0 or any other library, we would need to re-render your entire app to reflect the changes:
|
|
@@ -482,6 +521,8 @@ Unistyles is a cross-platform library that enables you to share up to 100% of yo
|
|
|
482
521
|
|
|
483
522
|
### Why should you use Unistyles?
|
|
484
523
|
|
|
524
|
+
[Section titled “Why should you use Unistyles?”](#why-should-you-use-unistyles)
|
|
525
|
+
|
|
485
526
|
* Guarantees no re-renders across the entire app (no hooks, no context—just pure JSI bindings)
|
|
486
527
|
* Doesn’t pollute your native view hierarchy, you can use any component you want
|
|
487
528
|
* Includes a cross-platform parser written in C++, ensuring consistent output across all platforms
|
|
@@ -502,58 +543,58 @@ The migration process is quite simple, but it can be tedious since you’ll need
|
|
|
502
543
|
|
|
503
544
|
`UnistylesRegistry` can be easily replaced with `StyleSheet.configure` as it follows the same syntax. `Themes` and `Breakpoints` work exactly the same. For `Settings` we removed 4 out of 6 options:
|
|
504
545
|
|
|
505
|
-
```
|
|
506
|
-
import { UnistylesRegistry } from 'react-native-unistyles'
|
|
507
|
-
import { StyleSheet } from 'react-native-unistyles'
|
|
546
|
+
```diff
|
|
547
|
+
-import { UnistylesRegistry } from 'react-native-unistyles'
|
|
548
|
+
+import { StyleSheet } from 'react-native-unistyles'
|
|
508
549
|
|
|
509
550
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
551
|
+
- UnistylesRegistry.addConfig({
|
|
552
|
+
- adaptiveThemes: false,
|
|
553
|
+
- initialTheme: 'dark',
|
|
554
|
+
- plugins: [...],
|
|
555
|
+
- experimentalCSSMediaQueries: true,
|
|
556
|
+
- windowResizeDebounceTimeMs: 100,
|
|
557
|
+
- disableAnimatedInsets: true
|
|
558
|
+
- })
|
|
518
559
|
|
|
519
560
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
561
|
+
+ StyleSheet.configure({
|
|
562
|
+
+ settings: {
|
|
563
|
+
+ adaptiveThemes: false, // works exactly the same like in 2.0
|
|
564
|
+
+ initialTheme: 'dark', // works exactly the same like in 2.0
|
|
524
565
|
// plugins are removed, instead transform your styles with static functions
|
|
525
566
|
// experimentalCSSMediaQueries: these options is also removed, and enabled by default with custom parser
|
|
526
567
|
// windowResizeDebounceTimeMs: removed, there is no debouncing anymore. Styles are updated with CSS media queries
|
|
527
568
|
// disableAnimatedInsets: removed, insets won't re-render your views
|
|
528
|
-
|
|
529
|
-
|
|
569
|
+
+ }
|
|
570
|
+
+ })
|
|
530
571
|
```
|
|
531
572
|
|
|
532
573
|
3. Import `StyleSheet` from `react-native-unistyles`:
|
|
533
574
|
|
|
534
|
-
```
|
|
535
|
-
import { createStyleSheet, useStyles } from 'react-native-unistyles'
|
|
536
|
-
import { StyleSheet } from 'react-native-unistyles'
|
|
575
|
+
```diff
|
|
576
|
+
-import { createStyleSheet, useStyles } from 'react-native-unistyles'
|
|
577
|
+
+import { StyleSheet } from 'react-native-unistyles'
|
|
537
578
|
```
|
|
538
579
|
|
|
539
580
|
4. Replace `createStyleSheet` with `StyleSheet.create`:
|
|
540
581
|
|
|
541
|
-
```
|
|
542
|
-
const stylesheet = createStyleSheet(theme => ({
|
|
543
|
-
const stylesheet = StyleSheet.create(theme => ({
|
|
582
|
+
```diff
|
|
583
|
+
-const stylesheet = createStyleSheet(theme => ({
|
|
584
|
+
+const stylesheet = StyleSheet.create(theme => ({
|
|
544
585
|
```
|
|
545
586
|
|
|
546
587
|
5. Remove all occurrences of `useStyles` hook:
|
|
547
588
|
|
|
548
|
-
```
|
|
549
|
-
const { styles } = useStyles(stylesheet)
|
|
589
|
+
```diff
|
|
590
|
+
-const { styles } = useStyles(stylesheet)
|
|
550
591
|
```
|
|
551
592
|
|
|
552
593
|
6. Rename your `stylesheet` to `styles`:
|
|
553
594
|
|
|
554
|
-
```
|
|
555
|
-
const stylesheet = StyleSheet.create(theme => ({
|
|
556
|
-
const styles = StyleSheet.create(theme => ({
|
|
595
|
+
```diff
|
|
596
|
+
-const stylesheet = StyleSheet.create(theme => ({
|
|
597
|
+
+const styles = StyleSheet.create(theme => ({
|
|
557
598
|
```
|
|
558
599
|
|
|
559
600
|
7. If you used `useInitialTheme`, remove it and set initial theme in `StyleSheet.configure`:
|
|
@@ -577,15 +618,15 @@ The migration process is quite simple, but it can be tedious since you’ll need
|
|
|
577
618
|
|
|
578
619
|
8. If you need to access your `theme` in component, refactor it to use `withUnistyles`:
|
|
579
620
|
|
|
580
|
-
```
|
|
621
|
+
```diff
|
|
581
622
|
import { Button } from 'react-native'
|
|
582
|
-
import { useStyles } from 'react-native-unistyles'
|
|
583
|
-
import { withUnistyles } from 'react-native-unistyles'
|
|
623
|
+
-import { useStyles } from 'react-native-unistyles'
|
|
624
|
+
+import { withUnistyles } from 'react-native-unistyles'
|
|
584
625
|
|
|
585
626
|
|
|
586
|
-
const UniButton = withUnistyles(Button, theme => ({
|
|
627
|
+
+const UniButton = withUnistyles(Button, theme => ({
|
|
587
628
|
color: theme.colors.primary
|
|
588
|
-
}))
|
|
629
|
+
+}))
|
|
589
630
|
|
|
590
631
|
|
|
591
632
|
const MyButton = () => {
|
|
@@ -594,7 +635,7 @@ The migration process is quite simple, but it can be tedious since you’ll need
|
|
|
594
635
|
|
|
595
636
|
|
|
596
637
|
const MyButton = () => {
|
|
597
|
-
const { theme } = useStyles(stylesheet)
|
|
638
|
+
-const { theme } = useStyles(stylesheet)
|
|
598
639
|
|
|
599
640
|
|
|
600
641
|
return <Button color={theme.colors.primary} />
|
|
@@ -640,22 +681,22 @@ The migration process is quite simple, but it can be tedious since you’ll need
|
|
|
640
681
|
|
|
641
682
|
11. If you used `UnistylesProvider`, remove it as it’s not available anymore:
|
|
642
683
|
|
|
643
|
-
```
|
|
644
|
-
import { UnistylesProvider } from 'react-native-unistyles'
|
|
684
|
+
```diff
|
|
685
|
+
-import { UnistylesProvider } from 'react-native-unistyles'
|
|
645
686
|
|
|
646
687
|
|
|
647
|
-
|
|
688
|
+
-<UnistylesProvider>
|
|
648
689
|
<App />
|
|
649
|
-
|
|
690
|
+
-</UnistylesProvider>
|
|
650
691
|
```
|
|
651
692
|
|
|
652
693
|
12. If you want to move your component based on keyboard position, use `ime` inset:
|
|
653
694
|
|
|
654
|
-
```
|
|
695
|
+
```diff
|
|
655
696
|
const style = StyleSheet.create({
|
|
656
697
|
container: {
|
|
657
698
|
paddingBottom: rt.insets.bottom // bottom is no longer dynamic
|
|
658
|
-
paddingBottom: rt.insets.ime
|
|
699
|
+
+paddingBottom: rt.insets.ime
|
|
659
700
|
}
|
|
660
701
|
})
|
|
661
702
|
```
|
|
@@ -664,43 +705,43 @@ The migration process is quite simple, but it can be tedious since you’ll need
|
|
|
664
705
|
|
|
665
706
|
14. Some `UnistylesRuntime` methods have been removed:
|
|
666
707
|
|
|
667
|
-
```
|
|
668
|
-
UnistylesRuntime.addPlugin(plugin) // Unistyles has no plugins anymore
|
|
669
|
-
UnistylesRuntime.removePlugin(plugin) // Unistyles has no plugins anymore
|
|
670
|
-
UnistylesRuntime.statusBar.setColor(color) // removed due to Android 15 deprecation
|
|
671
|
-
UnistylesRuntime.navigationBar.setColor(color) // removed due to Android 15 deprecation
|
|
708
|
+
```diff
|
|
709
|
+
-UnistylesRuntime.addPlugin(plugin) // Unistyles has no plugins anymore
|
|
710
|
+
-UnistylesRuntime.removePlugin(plugin) // Unistyles has no plugins anymore
|
|
711
|
+
-UnistylesRuntime.statusBar.setColor(color) // removed due to Android 15 deprecation
|
|
712
|
+
-UnistylesRuntime.navigationBar.setColor(color) // removed due to Android 15 deprecation
|
|
672
713
|
```
|
|
673
714
|
|
|
674
715
|
15. `UnistylesRuntime` methods that accepted `color` and `alpha` have been changed to accept `color` only. Each method supports **any** color that is respected by React Native:
|
|
675
716
|
|
|
676
|
-
```
|
|
677
|
-
UnistylesRuntime.setRootViewBackgroundColor(color, alpha) // no need for separate alpha
|
|
678
|
-
UnistylesRuntime.setRootViewBackgroundColor(color) // accepts any color
|
|
717
|
+
```diff
|
|
718
|
+
-UnistylesRuntime.setRootViewBackgroundColor(color, alpha) // no need for separate alpha
|
|
719
|
+
+UnistylesRuntime.setRootViewBackgroundColor(color) // accepts any color
|
|
679
720
|
```
|
|
680
721
|
|
|
681
722
|
16. `hairlineWidth` has been moved from `UnistylesRuntime` to `StyleSheet`. Use `StyleSheet.hairlineWidth` instead:
|
|
682
723
|
|
|
683
|
-
```
|
|
684
|
-
UnistylesRuntime.hairlineWidth // no longer available
|
|
685
|
-
StyleSheet.hairlineWidth // matches StyleSheet API
|
|
724
|
+
```diff
|
|
725
|
+
-UnistylesRuntime.hairlineWidth // no longer available
|
|
726
|
+
+StyleSheet.hairlineWidth // matches StyleSheet API
|
|
686
727
|
```
|
|
687
728
|
|
|
688
729
|
17. If your app used variants, move config to `styles.useVariants` instead:
|
|
689
730
|
|
|
690
|
-
```
|
|
691
|
-
import { useStyles } from 'react-native-unistyles'
|
|
692
|
-
import { StyleSheet } from 'react-native-unistyles'
|
|
731
|
+
```diff
|
|
732
|
+
-import { useStyles } from 'react-native-unistyles'
|
|
733
|
+
+import { StyleSheet } from 'react-native-unistyles'
|
|
693
734
|
|
|
694
735
|
|
|
695
736
|
const MyComponent = () => {
|
|
696
|
-
const { styles } = useStyles(stylesheet, {
|
|
737
|
+
-const { styles } = useStyles(stylesheet, {
|
|
697
738
|
variant1: 'primary',
|
|
698
739
|
variant2: 'secondary'
|
|
699
|
-
})
|
|
700
|
-
styles.useVariants({
|
|
740
|
+
-})
|
|
741
|
+
+styles.useVariants({
|
|
701
742
|
variant1: 'primary',
|
|
702
743
|
variant2: 'secondary'
|
|
703
|
-
})
|
|
744
|
+
+})
|
|
704
745
|
|
|
705
746
|
|
|
706
747
|
return <View style={styles.container} />
|
|
@@ -790,6 +831,8 @@ Unistyles provides its own mocks to help you test your components. Follow this g
|
|
|
790
831
|
|
|
791
832
|
### Including Mocks
|
|
792
833
|
|
|
834
|
+
[Section titled “Including Mocks”](#including-mocks)
|
|
835
|
+
|
|
793
836
|
You don’t need to mock anything manually, as Unistyles supplies all necessary mocks for its core and for `NitroModules`. To use them, simply include `react-native-unistyles/mocks` in your `jest.setup.ts` file.
|
|
794
837
|
|
|
795
838
|
package.json
|
|
@@ -807,6 +850,8 @@ package.json
|
|
|
807
850
|
|
|
808
851
|
### Include Unistyles Configuration
|
|
809
852
|
|
|
853
|
+
[Section titled “Include Unistyles Configuration”](#include-unistyles-configuration)
|
|
854
|
+
|
|
810
855
|
Each `StyleSheet` requires a configuration object passed to the `StyleSheet.configure` function. This is also true in the test environment. Extend the configuration from the previous step by including the file where you configure Unistyles.
|
|
811
856
|
|
|
812
857
|
package.json
|
|
@@ -829,10 +874,14 @@ You must include configuration file **after** the mocks as they provide all nece
|
|
|
829
874
|
|
|
830
875
|
### Babel Plugin
|
|
831
876
|
|
|
877
|
+
[Section titled “Babel Plugin”](#babel-plugin)
|
|
878
|
+
|
|
832
879
|
The Babel plugin is automatically disabled in the `jest` test environment or when `NODE_ENV === 'test'`.
|
|
833
880
|
|
|
834
881
|
### Understanding the role of mocks
|
|
835
882
|
|
|
883
|
+
[Section titled “Understanding the role of mocks”](#understanding-the-role-of-mocks)
|
|
884
|
+
|
|
836
885
|
Mocks contain basic logic to correctly execute Unistyles code. The Jest environment does not provide a `screen` (width and height), pixel ratio, or any other values from `UnistylesRuntime`.
|
|
837
886
|
|
|
838
887
|
You should never test how Unistyles parses your styles, whether your component has certain styles, or if it is visible. These tests can result in false positives.
|
|
@@ -849,6 +898,8 @@ This guide will explain when you should consider using Unistyles and when it’s
|
|
|
849
898
|
|
|
850
899
|
### When should you use Unistyles?
|
|
851
900
|
|
|
901
|
+
[Section titled “When should you use Unistyles?”](#when-should-you-use-unistyles)
|
|
902
|
+
|
|
852
903
|
Unistyles is recommended for projects that:
|
|
853
904
|
|
|
854
905
|
* leverage the New Architecture and care about performance and memory usage
|
|
@@ -861,18 +912,24 @@ Unistyles is recommended for projects that:
|
|
|
861
912
|
|
|
862
913
|
### When is Unistyles not the best option?
|
|
863
914
|
|
|
915
|
+
[Section titled “When is Unistyles not the best option?”](#when-is-unistyles-not-the-best-option)
|
|
916
|
+
|
|
864
917
|
* You’re looking for a component library (Unistyles has no components, instead we encourage you to build your own design system specifically tailored to your project)
|
|
865
|
-
* You use Tailwind on the web, as Unistyles has no native bindings to process `classNames` on the native side. Instead, we recommend using [
|
|
918
|
+
* You use Tailwind on the web, as Unistyles has no native bindings to process `classNames` on the native side. Instead, we recommend using [uniwind](https://uniwind.dev/)
|
|
866
919
|
* You’re building a super simple app that doesn’t require theme changes or any advanced features. In this case, stick with React Native’s `StyleSheet` and consider updating to Unistyles 3.0 when it will be more efficient
|
|
867
920
|
|
|
868
921
|
### When you can’t use Unistyles 3.0?
|
|
869
922
|
|
|
923
|
+
[Section titled “When you can’t use Unistyles 3.0?”](#when-you-cant-use-unistyles-30)
|
|
924
|
+
|
|
870
925
|
* In Expo Go apps, as Unistyles is not (yet) selected by the Expo team
|
|
871
926
|
* In apps that can’t update to the New Architecture. Instead, consider using [Unistyles 2.0](https://v2.unistyl.es/start/introduction/)
|
|
872
927
|
* In apps that target unsupported platforms (eg. TV, Windows, macOS etc.), again consider using [Unistyles 2.0](https://v2.unistyl.es/start/introduction/)
|
|
873
928
|
|
|
874
929
|
### Other alternatives
|
|
875
930
|
|
|
931
|
+
[Section titled “Other alternatives”](#other-alternatives)
|
|
932
|
+
|
|
876
933
|
To find other alternatives, please check the latest [State of React Native survey](https://stateofreactnative.com/).
|
|
877
934
|
|
|
878
935
|
# Avoiding keyboard
|
|
@@ -885,6 +942,8 @@ Unistyles dynamically recalculates your styles based on their dependencies. To l
|
|
|
885
942
|
|
|
886
943
|
### Usage
|
|
887
944
|
|
|
945
|
+
[Section titled “Usage”](#usage)
|
|
946
|
+
|
|
888
947
|
```tsx
|
|
889
948
|
import { TextInput, View } from 'react-native'
|
|
890
949
|
import { StyleSheet } from 'react-native-unistyles'
|
|
@@ -933,6 +992,8 @@ For this we recommend following the guidelines provided by [Expo](https://docs.e
|
|
|
933
992
|
|
|
934
993
|
## How to create custom web components
|
|
935
994
|
|
|
995
|
+
[Section titled “How to create custom web components”](#how-to-create-custom-web-components)
|
|
996
|
+
|
|
936
997
|
In order to create custom web components, you need to use `getWebProps` function. It takes a `StyleProp` and returns an object with `className` and `ref` properties.
|
|
937
998
|
|
|
938
999
|
src/components/Header.tsx
|
|
@@ -1017,14 +1078,16 @@ If you’re creating multiplatform app, remember to create a native fallback for
|
|
|
1017
1078
|
|
|
1018
1079
|
### Modify main entry
|
|
1019
1080
|
|
|
1081
|
+
[Section titled “Modify main entry”](#modify-main-entry)
|
|
1082
|
+
|
|
1020
1083
|
Expo Router resolves routes differently than expected. Also, Unistyles 3.0 is parsing your `StyleSheets` as soon as you import file containing it. This combination may cause some issues. To prevent that you need to modify your main entry file:
|
|
1021
1084
|
|
|
1022
1085
|
package.json
|
|
1023
1086
|
|
|
1024
|
-
```
|
|
1087
|
+
```diff
|
|
1025
1088
|
{
|
|
1026
|
-
"main": "expo-router/entry"
|
|
1027
|
-
"main": "index.ts"
|
|
1089
|
+
-"main": "expo-router/entry"
|
|
1090
|
+
+"main": "index.ts"
|
|
1028
1091
|
}
|
|
1029
1092
|
```
|
|
1030
1093
|
|
|
@@ -1045,6 +1108,8 @@ With this setup, we will ensure that Unistyles is initialized before any other c
|
|
|
1045
1108
|
|
|
1046
1109
|
### Expo Router Web - Static rendering
|
|
1047
1110
|
|
|
1111
|
+
[Section titled “Expo Router Web - Static rendering”](#expo-router-web---static-rendering)
|
|
1112
|
+
|
|
1048
1113
|
Caution
|
|
1049
1114
|
|
|
1050
1115
|
This is the default option since Expo SDK 52.
|
|
@@ -1070,11 +1135,11 @@ In this file, initialize Unistyles by importing the config file:
|
|
|
1070
1135
|
|
|
1071
1136
|
+html.tsx
|
|
1072
1137
|
|
|
1073
|
-
```
|
|
1138
|
+
```diff
|
|
1074
1139
|
import React from 'react'
|
|
1075
1140
|
import { ScrollViewStyleReset } from 'expo-router/html'
|
|
1076
1141
|
import { type PropsWithChildren } from 'react'
|
|
1077
|
-
import '../unistyles' // <-- file that initializes Unistyles
|
|
1142
|
+
+import '../unistyles' // <-- file that initializes Unistyles
|
|
1078
1143
|
|
|
1079
1144
|
|
|
1080
1145
|
export default function Root({ children }: PropsWithChildren) {
|
|
@@ -1092,12 +1157,16 @@ While using Unistyles, it’s crucial to understand how styles need to be merged
|
|
|
1092
1157
|
|
|
1093
1158
|
### Introduction
|
|
1094
1159
|
|
|
1160
|
+
[Section titled “Introduction”](#introduction)
|
|
1161
|
+
|
|
1095
1162
|
In the early versions of Unistyles 3.0, we tried to solve this issue with a Babel plugin. However, it was too complex to maintain various edge cases (especially with `Pressable`), and developers frequently encountered many `Unistyles: Style is not bound!` errors.
|
|
1096
1163
|
|
|
1097
1164
|
With the new approach, we shift the responsibility of merging styles to the user. In other words, the Babel plugin will no longer convert your style tags from objects to arrays.
|
|
1098
1165
|
|
|
1099
1166
|
### How to merge multiple styles
|
|
1100
1167
|
|
|
1168
|
+
[Section titled “How to merge multiple styles”](#how-to-merge-multiple-styles)
|
|
1169
|
+
|
|
1101
1170
|
Unistyles doesn’t provide any extra API for merging styles. Instead, we encourage you to use the `[]` syntax supported by React Native components.
|
|
1102
1171
|
|
|
1103
1172
|
```tsx
|
|
@@ -1123,12 +1192,16 @@ It’s critical to ship Unistyles 3.0 apps without this warning, as it can cause
|
|
|
1123
1192
|
|
|
1124
1193
|
### Reanimated
|
|
1125
1194
|
|
|
1195
|
+
[Section titled “Reanimated”](#reanimated)
|
|
1196
|
+
|
|
1126
1197
|
In older versions of Reanimated, the `Animated` component was flattening your styles array, causing warnings and only allowing to pass a **single** unistyles to an `Animated` component ([original issue](https://github.com/jpudysz/react-native-unistyles/issues/512)).
|
|
1127
1198
|
|
|
1128
1199
|
However, from `react-native-reanimated@3.17.2` or `react-native-reanimated@4.0.0-beta.3`, styles are no longer flattened.
|
|
1129
1200
|
|
|
1130
1201
|
### Spreading a single Unistyle
|
|
1131
1202
|
|
|
1203
|
+
[Section titled “Spreading a single Unistyle”](#spreading-a-single-unistyle)
|
|
1204
|
+
|
|
1132
1205
|
Another problematic case is spreading a single Unistyle and merging it, e.g., with inline styles:
|
|
1133
1206
|
|
|
1134
1207
|
```tsx
|
|
@@ -1141,6 +1214,8 @@ Also, keep in mind that restoring the C++ state takes unnecessary extra time, so
|
|
|
1141
1214
|
|
|
1142
1215
|
### Summary
|
|
1143
1216
|
|
|
1217
|
+
[Section titled “Summary”](#summary)
|
|
1218
|
+
|
|
1144
1219
|
* Use the `[]` syntax to merge styles
|
|
1145
1220
|
* Avoid spreading Unistyles
|
|
1146
1221
|
* Avoid merging your styles with the spread operator
|
|
@@ -1156,10 +1231,14 @@ React Compiler is a build-time tool that automatically optimizes your React app.
|
|
|
1156
1231
|
|
|
1157
1232
|
## With Expo
|
|
1158
1233
|
|
|
1234
|
+
[Section titled “With Expo”](#with-expo)
|
|
1235
|
+
|
|
1159
1236
|
For Expo projects, simply follow the [official Expo guide](https://docs.expo.dev/guides/react-compiler/). No additional configuration changes are necessary!
|
|
1160
1237
|
|
|
1161
1238
|
## With Bare React Native
|
|
1162
1239
|
|
|
1240
|
+
[Section titled “With Bare React Native”](#with-bare-react-native)
|
|
1241
|
+
|
|
1163
1242
|
For bare React Native projects, refer to the [official React guide](https://react.dev/learn/react-compiler#usage-with-babel) with one key adjustment:
|
|
1164
1243
|
|
|
1165
1244
|
Ensure that the React Compiler runs *after* the Unistyles Babel plugin. Failure to do so may result in errors because Unistyles needs to process `Variants` before the React Compiler does. You can read more about the Babel plugin [here](/v3/other/babel-plugin).
|
|
@@ -1168,12 +1247,12 @@ Here’s a sample configuration for your `babel.config.js`:
|
|
|
1168
1247
|
|
|
1169
1248
|
babel.config.js
|
|
1170
1249
|
|
|
1171
|
-
```
|
|
1250
|
+
```diff
|
|
1172
1251
|
module.exports = function () {
|
|
1173
1252
|
return {
|
|
1174
1253
|
plugins: [
|
|
1175
1254
|
['react-native-unistyles/plugin'], // Must run before react-compiler
|
|
1176
|
-
'babel-plugin-react-compiler',
|
|
1255
|
+
+'babel-plugin-react-compiler',
|
|
1177
1256
|
// Add other plugins here
|
|
1178
1257
|
]
|
|
1179
1258
|
}
|
|
@@ -1195,6 +1274,8 @@ Reanimated works the best with Unistyles in:
|
|
|
1195
1274
|
|
|
1196
1275
|
### Access theme in worklets
|
|
1197
1276
|
|
|
1277
|
+
[Section titled “Access theme in worklets”](#access-theme-in-worklets)
|
|
1278
|
+
|
|
1198
1279
|
Using the theme from `UnistylesRuntime.getTheme()` will not trigger worklet updates. Importing it from `useUnistyles` will cause a re-render.
|
|
1199
1280
|
|
|
1200
1281
|
That’s why to use Unistyles theme in worklets (e.g. in `useAnimatedStyle`), you need to import a special hook from `react-native-unistyles/reanimated`.
|
|
@@ -1224,6 +1305,8 @@ Note
|
|
|
1224
1305
|
|
|
1225
1306
|
### Animating variant colors
|
|
1226
1307
|
|
|
1308
|
+
[Section titled “Animating variant colors”](#animating-variant-colors)
|
|
1309
|
+
|
|
1227
1310
|
It’s possible to reuse Unistyles variant colors and animate them using the `useAnimatedStyle` hook.
|
|
1228
1311
|
|
|
1229
1312
|
Define your variants with a `color` property:
|
|
@@ -1275,6 +1358,8 @@ const animatedStyle = useAnimatedStyle(() => {
|
|
|
1275
1358
|
|
|
1276
1359
|
### Merging styles
|
|
1277
1360
|
|
|
1361
|
+
[Section titled “Merging styles”](#merging-styles)
|
|
1362
|
+
|
|
1278
1363
|
When you want to use `Unistyles` styles in `Animated` components, never mix them with `Reanimated` styles:
|
|
1279
1364
|
|
|
1280
1365
|
```tsx
|
|
@@ -1295,7 +1380,7 @@ export const MyAnimatedComponent = () => {
|
|
|
1295
1380
|
}
|
|
1296
1381
|
|
|
1297
1382
|
|
|
1298
|
-
const
|
|
1383
|
+
const styles = StyleSheet.create(theme => ({
|
|
1299
1384
|
container: {
|
|
1300
1385
|
flex: 1,
|
|
1301
1386
|
justifyContent: 'center',
|
|
@@ -1326,7 +1411,7 @@ export const MyAnimatedComponent = () => {
|
|
|
1326
1411
|
}
|
|
1327
1412
|
|
|
1328
1413
|
|
|
1329
|
-
const
|
|
1414
|
+
const styles = StyleSheet.create(theme => ({
|
|
1330
1415
|
container: {
|
|
1331
1416
|
flex: 1,
|
|
1332
1417
|
justifyContent: 'center',
|
|
@@ -1346,6 +1431,8 @@ Unistyles 3.0 is fully compatible with Next.js Server Side Rendering (SSR). We
|
|
|
1346
1431
|
|
|
1347
1432
|
### Usage
|
|
1348
1433
|
|
|
1434
|
+
[Section titled “Usage”](#usage)
|
|
1435
|
+
|
|
1349
1436
|
* App router
|
|
1350
1437
|
|
|
1351
1438
|
To use server-side rendered styles, create the following **client-side** component:
|
|
@@ -1388,9 +1475,9 @@ Unistyles 3.0 is fully compatible with Next.js Server Side Rendering (SSR). We
|
|
|
1388
1475
|
|
|
1389
1476
|
layout.tsx
|
|
1390
1477
|
|
|
1391
|
-
```
|
|
1392
|
-
import '../unistyles'
|
|
1393
|
-
import { Style } from '../Style'
|
|
1478
|
+
```diff
|
|
1479
|
+
+import '../unistyles'
|
|
1480
|
+
+import { Style } from '../Style'
|
|
1394
1481
|
|
|
1395
1482
|
|
|
1396
1483
|
export default function RootLayout({
|
|
@@ -1401,9 +1488,9 @@ Unistyles 3.0 is fully compatible with Next.js Server Side Rendering (SSR). We
|
|
|
1401
1488
|
return (
|
|
1402
1489
|
<html lang="en">
|
|
1403
1490
|
<body>
|
|
1404
|
-
|
|
1491
|
+
+<Style>
|
|
1405
1492
|
{children}
|
|
1406
|
-
|
|
1493
|
+
+</Style>
|
|
1407
1494
|
</body>
|
|
1408
1495
|
</html>
|
|
1409
1496
|
);
|
|
@@ -1418,6 +1505,8 @@ Unistyles 3.0 is fully compatible with Next.js Server Side Rendering (SSR). We
|
|
|
1418
1505
|
|
|
1419
1506
|
### Config (Optional)
|
|
1420
1507
|
|
|
1508
|
+
[Section titled “Config (Optional)”](#config-optional)
|
|
1509
|
+
|
|
1421
1510
|
`useServerUnistyles` accepts an optional config object:
|
|
1422
1511
|
|
|
1423
1512
|
* **`includeRNWStyles`** – a boolean that enables or disables injecting React Native Web default CSS styles. Defaults to `true`.
|
|
@@ -1428,68 +1517,74 @@ Unistyles 3.0 is fully compatible with Next.js Server Side Rendering (SSR). We
|
|
|
1428
1517
|
|
|
1429
1518
|
\_document.tsx
|
|
1430
1519
|
|
|
1431
|
-
```
|
|
1432
|
-
import { getServerUnistyles, resetServerUnistyles } from 'react-native-unistyles/server'
|
|
1520
|
+
```diff
|
|
1521
|
+
+import { getServerUnistyles, resetServerUnistyles } from 'react-native-unistyles/server'
|
|
1433
1522
|
|
|
1434
1523
|
|
|
1435
1524
|
export default class Document extends NextDocument {
|
|
1436
|
-
static async getInitialProps({ renderPage }: DocumentContext) {
|
|
1437
|
-
const page = await renderPage()
|
|
1438
|
-
const styles = getServerUnistyles()
|
|
1525
|
+
+static async getInitialProps({ renderPage }: DocumentContext) {
|
|
1526
|
+
+const page = await renderPage()
|
|
1527
|
+
+const styles = getServerUnistyles()
|
|
1439
1528
|
|
|
1440
1529
|
|
|
1441
|
-
resetServerUnistyles()
|
|
1530
|
+
+resetServerUnistyles()
|
|
1442
1531
|
|
|
1443
1532
|
|
|
1444
|
-
return {
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1533
|
+
+return {
|
|
1534
|
+
+...page,
|
|
1535
|
+
+ styles
|
|
1536
|
+
+ }
|
|
1537
|
+
+ }
|
|
1449
1538
|
```
|
|
1450
1539
|
|
|
1451
1540
|
And add the following use effect to your `_app.tsx`
|
|
1452
1541
|
|
|
1453
1542
|
\_app.tsx
|
|
1454
1543
|
|
|
1455
|
-
```
|
|
1456
|
-
import { hydrateServerUnistyles } from 'react-native-unistyles/server'
|
|
1544
|
+
```diff
|
|
1545
|
+
+import { hydrateServerUnistyles } from 'react-native-unistyles/server'
|
|
1457
1546
|
|
|
1458
1547
|
|
|
1459
1548
|
{/* JSX of your component */}
|
|
1460
|
-
useEffect(() => {
|
|
1461
|
-
hydrateServerUnistyles()
|
|
1462
|
-
|
|
1549
|
+
+useEffect(() => {
|
|
1550
|
+
+hydrateServerUnistyles()
|
|
1551
|
+
+ }, [])
|
|
1463
1552
|
```
|
|
1464
1553
|
|
|
1465
1554
|
### Config (Optional)
|
|
1466
1555
|
|
|
1556
|
+
[Section titled “Config (Optional)”](#config-optional-1)
|
|
1557
|
+
|
|
1467
1558
|
`getServerUnistyles` accepts an optional config object:
|
|
1468
1559
|
|
|
1469
1560
|
* **`includeRNWStyles`** – a boolean that enables or disables injecting React Native Web default CSS styles. Defaults to `true`.
|
|
1470
1561
|
|
|
1471
1562
|
## Troubleshooting
|
|
1472
1563
|
|
|
1564
|
+
[Section titled “Troubleshooting”](#troubleshooting)
|
|
1565
|
+
|
|
1473
1566
|
### Hydration error
|
|
1474
1567
|
|
|
1568
|
+
[Section titled “Hydration error”](#hydration-error)
|
|
1569
|
+
|
|
1475
1570
|
If you’re not using adaptive themes, you might encounter hydration error on your root html element. This is because unistyles is adding a className to it based on the current theme.
|
|
1476
1571
|
|
|
1477
1572
|
To fix this simply add `suppressHydrationWarning` to your root html element.
|
|
1478
1573
|
|
|
1479
1574
|
layout.tsx
|
|
1480
1575
|
|
|
1481
|
-
```
|
|
1482
|
-
|
|
1483
|
-
|
|
1576
|
+
```diff
|
|
1577
|
+
-<html lang="en">
|
|
1578
|
+
+<html lang="en" suppressHydrationWarning>
|
|
1484
1579
|
```
|
|
1485
1580
|
|
|
1486
1581
|
Or you can directly add the className to your root html element.
|
|
1487
1582
|
|
|
1488
1583
|
layout.tsx
|
|
1489
1584
|
|
|
1490
|
-
```
|
|
1491
|
-
|
|
1492
|
-
|
|
1585
|
+
```diff
|
|
1586
|
+
-<html lang="en">
|
|
1587
|
+
+<html lang="en" className="dark">
|
|
1493
1588
|
```
|
|
1494
1589
|
|
|
1495
1590
|
# Theming
|
|
@@ -1506,6 +1601,8 @@ Theming is optional. If you don’t register themes with [StyleSheet.configure](
|
|
|
1506
1601
|
|
|
1507
1602
|
### Create a theme
|
|
1508
1603
|
|
|
1604
|
+
[Section titled “Create a theme”](#create-a-theme)
|
|
1605
|
+
|
|
1509
1606
|
You can organize your themes however you want:
|
|
1510
1607
|
|
|
1511
1608
|
```tsx
|
|
@@ -1570,6 +1667,8 @@ It’s not recommended to use themes with different shapes. Unistyles allows tha
|
|
|
1570
1667
|
|
|
1571
1668
|
### Select theme
|
|
1572
1669
|
|
|
1670
|
+
[Section titled “Select theme”](#select-theme)
|
|
1671
|
+
|
|
1573
1672
|
If you’ve registered more than one theme, Unistyles won’t know which one is the initial one. At this point, you have 3 options:
|
|
1574
1673
|
|
|
1575
1674
|
* If you know the initial theme upfront, select it with `settings` from [StyleSheet.configure](/v3/start/configuration#settings-optional)
|
|
@@ -1603,6 +1702,8 @@ It’s not possible to use `async` functions with `initialTheme` option.
|
|
|
1603
1702
|
|
|
1604
1703
|
### Get the current theme
|
|
1605
1704
|
|
|
1705
|
+
[Section titled “Get the current theme”](#get-the-current-theme)
|
|
1706
|
+
|
|
1606
1707
|
To get the current theme you can access it in the `StyleSheet.create` function:
|
|
1607
1708
|
|
|
1608
1709
|
```tsx
|
|
@@ -1635,6 +1736,8 @@ Caution
|
|
|
1635
1736
|
|
|
1636
1737
|
### Get the current theme name
|
|
1637
1738
|
|
|
1739
|
+
[Section titled “Get the current theme name”](#get-the-current-theme-name)
|
|
1740
|
+
|
|
1638
1741
|
To get the current theme name, import `UnistylesRuntime`:
|
|
1639
1742
|
|
|
1640
1743
|
```tsx
|
|
@@ -1651,6 +1754,8 @@ export const UserTheme = () => (
|
|
|
1651
1754
|
|
|
1652
1755
|
### Adaptive themes
|
|
1653
1756
|
|
|
1757
|
+
[Section titled “Adaptive themes”](#adaptive-themes)
|
|
1758
|
+
|
|
1654
1759
|
Adaptive themes allow Unistyles to automatically manage the selection of your themes based on device color scheme settings. To enable this, you need to meet two conditions:
|
|
1655
1760
|
|
|
1656
1761
|
* register two themes with reserved names `light` and `dark`:
|
|
@@ -1685,6 +1790,8 @@ Setting initial theme and enabling adaptive themes at the same time will throw a
|
|
|
1685
1790
|
|
|
1686
1791
|
### Toggle adaptive themes during runtime
|
|
1687
1792
|
|
|
1793
|
+
[Section titled “Toggle adaptive themes during runtime”](#toggle-adaptive-themes-during-runtime)
|
|
1794
|
+
|
|
1688
1795
|
To toggle adaptive themes support at any point, use `UnistylesRuntime`:
|
|
1689
1796
|
|
|
1690
1797
|
```tsx
|
|
@@ -1704,6 +1811,8 @@ With adaptive themes disabled, you can now manually change the theme.
|
|
|
1704
1811
|
|
|
1705
1812
|
### Check if adaptive themes are enabled
|
|
1706
1813
|
|
|
1814
|
+
[Section titled “Check if adaptive themes are enabled”](#check-if-adaptive-themes-are-enabled)
|
|
1815
|
+
|
|
1707
1816
|
To check if adaptive themes are enabled, use `UnistylesRuntime` again:
|
|
1708
1817
|
|
|
1709
1818
|
```tsx
|
|
@@ -1720,6 +1829,8 @@ export const AdaptiveThemes = () => (
|
|
|
1720
1829
|
|
|
1721
1830
|
### Get device color scheme
|
|
1722
1831
|
|
|
1832
|
+
[Section titled “Get device color scheme”](#get-device-color-scheme)
|
|
1833
|
+
|
|
1723
1834
|
Check your device color preference with `UnistylesRuntime`:
|
|
1724
1835
|
|
|
1725
1836
|
```tsx
|
|
@@ -1744,6 +1855,8 @@ If your app’s theme is not changing based on device settings, please refer to
|
|
|
1744
1855
|
|
|
1745
1856
|
### Change theme
|
|
1746
1857
|
|
|
1858
|
+
[Section titled “Change theme”](#change-theme)
|
|
1859
|
+
|
|
1747
1860
|
To change the theme at any time, simply call `setTheme` function:
|
|
1748
1861
|
|
|
1749
1862
|
```tsx
|
|
@@ -1765,6 +1878,8 @@ Calling this function with enabled adaptive themes will throw an error.
|
|
|
1765
1878
|
|
|
1766
1879
|
### Update theme during runtime
|
|
1767
1880
|
|
|
1881
|
+
[Section titled “Update theme during runtime”](#update-theme-during-runtime)
|
|
1882
|
+
|
|
1768
1883
|
Unistyles allows you to update your theme during runtime. This is useful if you want to show the user interface with default colors and later alter theme based on user preferences.
|
|
1769
1884
|
|
|
1770
1885
|
If you update the currently selected theme, it will be automatically applied, and Unistyles will notify all stylesheets about the change. Otherwise, theme will be updated silently.
|
|
@@ -1792,6 +1907,8 @@ export const UpdateTheme = ({ selectedColors }) => (
|
|
|
1792
1907
|
|
|
1793
1908
|
### Update rootView background color during runtime
|
|
1794
1909
|
|
|
1910
|
+
[Section titled “Update rootView background color during runtime”](#update-rootview-background-color-during-runtime)
|
|
1911
|
+
|
|
1795
1912
|
You can also change dynamically the root view background color with `UnistylesRuntime`:
|
|
1796
1913
|
|
|
1797
1914
|
```tsx
|
|
@@ -1825,12 +1942,16 @@ If you start working with Unistyles 3.0, it might be unclear why some views are
|
|
|
1825
1942
|
|
|
1826
1943
|
### Problem 1: Babel
|
|
1827
1944
|
|
|
1945
|
+
[Section titled “Problem 1: Babel”](#problem-1-babel)
|
|
1946
|
+
|
|
1828
1947
|
To leverage ShadowTree updates and avoid unnecessary re-renders, Unistyles must process both `StyleSheets` and your components. By default, the Babel plugin looks for `react-native-unistyles` imports and always ignores the `node_modules` folder.
|
|
1829
1948
|
|
|
1830
1949
|
If you separate `StyleSheets` from your components, it’s your responsibility to configure Babel to detect components that lack a Unistyles import. We’ve added plenty of options, so be sure to [check them out](/v3/other/babel-plugin##extra-configuration).
|
|
1831
1950
|
|
|
1832
1951
|
### Problem 2: Dependency detection
|
|
1833
1952
|
|
|
1953
|
+
[Section titled “Problem 2: Dependency detection”](#problem-2-dependency-detection)
|
|
1954
|
+
|
|
1834
1955
|
Unistyles will automatically detect all your dependencies for every `StyleSheet`, but there’s a chance you used custom syntax that isn’t covered by the plugin. If Babel fails to detect some style dependencies, they won’t be updated when necessary.
|
|
1835
1956
|
|
|
1836
1957
|
You can easily debug this issue by adding the following Babel plugin configuration:
|
|
@@ -1859,10 +1980,14 @@ Then, restart the Metro server cache and check the console, where you’ll find
|
|
|
1859
1980
|
|
|
1860
1981
|
### Problem 3: Non React Native components
|
|
1861
1982
|
|
|
1983
|
+
[Section titled “Problem 3: Non React Native components”](#problem-3-non-react-native-components)
|
|
1984
|
+
|
|
1862
1985
|
Unistyles can only update React Native components. If you’re using a third-party component, you’ll need to apply a different strategy. Follow our [decision algorithm](/v3/references/3rd-party-views) to help you choose the best approach.
|
|
1863
1986
|
|
|
1864
1987
|
### Problem 4: Web styles are not applied
|
|
1865
1988
|
|
|
1989
|
+
[Section titled “Problem 4: Web styles are not applied”](#problem-4-web-styles-are-not-applied)
|
|
1990
|
+
|
|
1866
1991
|
This issue indicates that the Babel plugin didn’t detect some of your components. Initially, it may seem like native styles are working correctly, but that’s not the case.
|
|
1867
1992
|
|
|
1868
1993
|
On mobile, styles are returned the same way as in React Native. You can always `console.log` them to inspect the parsed values:
|
|
@@ -1937,6 +2062,8 @@ Breakpoints are user-defined key/value pairs that describe the boundaries of scr
|
|
|
1937
2062
|
|
|
1938
2063
|
### Register breakpoints
|
|
1939
2064
|
|
|
2065
|
+
[Section titled “Register breakpoints”](#register-breakpoints)
|
|
2066
|
+
|
|
1940
2067
|
To register your breakpoints, create an object with **any** keys:
|
|
1941
2068
|
|
|
1942
2069
|
unistyles.ts
|
|
@@ -1981,9 +2108,11 @@ To learn more, follow the configuration [guide](/v3/start/configuration).
|
|
|
1981
2108
|
|
|
1982
2109
|
### How to use breakpoints?
|
|
1983
2110
|
|
|
2111
|
+
[Section titled “How to use breakpoints?”](#how-to-use-breakpoints)
|
|
2112
|
+
|
|
1984
2113
|
Any style can change based on breakpoints. To do this, change a `value` to an `object`:
|
|
1985
2114
|
|
|
1986
|
-
```
|
|
2115
|
+
```diff
|
|
1987
2116
|
const styles = StyleSheet.create(theme => ({
|
|
1988
2117
|
container: {
|
|
1989
2118
|
flex: 1,
|
|
@@ -1991,10 +2120,10 @@ const styles = StyleSheet.create(theme => ({
|
|
|
1991
2120
|
alignItems: 'center',
|
|
1992
2121
|
backgroundColor: theme.colors.background,
|
|
1993
2122
|
backgroundColor: {
|
|
1994
|
-
|
|
2123
|
+
+// your breakpoints
|
|
1995
2124
|
xs: theme.colors.background,
|
|
1996
2125
|
sm: theme.colors.barbie
|
|
1997
|
-
}
|
|
2126
|
+
+}
|
|
1998
2127
|
},
|
|
1999
2128
|
text: {
|
|
2000
2129
|
color: theme.colors.typography
|
|
@@ -2033,6 +2162,8 @@ Breakpoints are also available with [variants](/v3/references/variants/) and [co
|
|
|
2033
2162
|
|
|
2034
2163
|
### Built-in breakpoints `landscape` and `portrait`
|
|
2035
2164
|
|
|
2165
|
+
[Section titled “Built-in breakpoints landscape and portrait”](#built-in-breakpoints-landscape-and-portrait)
|
|
2166
|
+
|
|
2036
2167
|
Even if you don’t use custom breakpoints, you can still utilize Unistyles’ predefined breakpoints available on mobile devices: `portrait` and `landscape`.
|
|
2037
2168
|
|
|
2038
2169
|
* `portrait` will resolve to your device’s width in portrait mode
|
|
@@ -2058,12 +2189,16 @@ const styles = StyleSheet.create(theme => ({
|
|
|
2058
2189
|
|
|
2059
2190
|
### Pixel/Point mode for native breakpoints
|
|
2060
2191
|
|
|
2192
|
+
[Section titled “Pixel/Point mode for native breakpoints”](#pixelpoint-mode-for-native-breakpoints)
|
|
2193
|
+
|
|
2061
2194
|
By default, Unistyles will use `pixels` for native breakpoints. This means that the breakpoints and [mq](/v3/references/media-queries) will be computed based on mobile screen pixels. You can change this behavior by setting `nativeBreakpointsMode` to `points` in your [configuration](/v3/start/configuration#settings-optional).
|
|
2062
2195
|
|
|
2063
2196
|
If `nativeBreakpointsMode` is set to `points`, all breakpoints and `mq` will be computed based on mobile screen points (screen in pixels divided by pixel ratio).
|
|
2064
2197
|
|
|
2065
2198
|
### Show/Hide your components based on breakpoints
|
|
2066
2199
|
|
|
2200
|
+
[Section titled “Show/Hide your components based on breakpoints”](#showhide-your-components-based-on-breakpoints)
|
|
2201
|
+
|
|
2067
2202
|
In order to show or hide your components based on the screen size, you can leverage the `mq` utility and one of the two built-in components: `Display` and `Hide`.
|
|
2068
2203
|
|
|
2069
2204
|
```tsx
|
|
@@ -2098,6 +2233,8 @@ export const CurrentBreakpoint = () => (
|
|
|
2098
2233
|
|
|
2099
2234
|
### Get registered breakpoints
|
|
2100
2235
|
|
|
2236
|
+
[Section titled “Get registered breakpoints”](#get-registered-breakpoints)
|
|
2237
|
+
|
|
2101
2238
|
Access your registered breakpoints object with `UnistylesRuntime`:
|
|
2102
2239
|
|
|
2103
2240
|
```tsx
|
|
@@ -2122,6 +2259,8 @@ Compound variants are a way of applying additional styles when certain condition
|
|
|
2122
2259
|
|
|
2123
2260
|
### Basic usage
|
|
2124
2261
|
|
|
2262
|
+
[Section titled “Basic usage”](#basic-usage)
|
|
2263
|
+
|
|
2125
2264
|
Let’s say you created a base `Typography` component with the following variants:
|
|
2126
2265
|
|
|
2127
2266
|
```tsx
|
|
@@ -2168,6 +2307,8 @@ What if you’ve received a new requirement where the text should be underlined
|
|
|
2168
2307
|
|
|
2169
2308
|
### Usage with Compound variants
|
|
2170
2309
|
|
|
2310
|
+
[Section titled “Usage with Compound variants”](#usage-with-compound-variants)
|
|
2311
|
+
|
|
2171
2312
|
With compound variants, it can be achieved in a more concise way:
|
|
2172
2313
|
|
|
2173
2314
|
```tsx
|
|
@@ -2233,6 +2374,8 @@ It’s also possible to use these values to build responsive layouts based on na
|
|
|
2233
2374
|
|
|
2234
2375
|
### iOS
|
|
2235
2376
|
|
|
2377
|
+
[Section titled “iOS”](#ios)
|
|
2378
|
+
|
|
2236
2379
|
Unistyles’ implementation is based on [Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/typography#Specifications) and the available values are:
|
|
2237
2380
|
|
|
2238
2381
|
`xSmall`, `Small`, `Medium`, `Large`, `xLarge`, `xxLarge`, `xxxLarge`, `unspecified`
|
|
@@ -2243,6 +2386,8 @@ In addition to the above categories, you can also use the [Accessibility sizes](
|
|
|
2243
2386
|
|
|
2244
2387
|
### Android
|
|
2245
2388
|
|
|
2389
|
+
[Section titled “Android”](#android)
|
|
2390
|
+
|
|
2246
2391
|
There is no direct equivalent to the iOS content size category on Android. The implementation is based on [Font Scale](https://developer.android.com/reference/android/content/res/Configuration#fontScale), and the available values are:
|
|
2247
2392
|
|
|
2248
2393
|
`Small`, `Default`, `Large`, `ExtraLarge`, `Huge`
|
|
@@ -2261,10 +2406,14 @@ Mapping is based on the following table:
|
|
|
2261
2406
|
|
|
2262
2407
|
### Web
|
|
2263
2408
|
|
|
2409
|
+
[Section titled “Web”](#web)
|
|
2410
|
+
|
|
2264
2411
|
There is no support for the content size category on the web. Reading the value will always resolve to `unspecified`.
|
|
2265
2412
|
|
|
2266
2413
|
### Usage
|
|
2267
2414
|
|
|
2415
|
+
[Section titled “Usage”](#usage)
|
|
2416
|
+
|
|
2268
2417
|
To get the current `contentSizeCategory`, you need to use `UnistylesRuntime`:
|
|
2269
2418
|
|
|
2270
2419
|
```tsx
|
|
@@ -2296,6 +2445,8 @@ Unistyles provides rich metadata about your device dimensions. This is useful fo
|
|
|
2296
2445
|
|
|
2297
2446
|
### Accessing dimensions
|
|
2298
2447
|
|
|
2448
|
+
[Section titled “Accessing dimensions”](#accessing-dimensions)
|
|
2449
|
+
|
|
2299
2450
|
In order to start using the dimensions metadata, you need to import `UnistylesRuntime`:
|
|
2300
2451
|
|
|
2301
2452
|
```tsx
|
|
@@ -2304,6 +2455,8 @@ import { UnistylesRuntime } from 'react-native-unistyles'
|
|
|
2304
2455
|
|
|
2305
2456
|
### Screen dimensions
|
|
2306
2457
|
|
|
2458
|
+
[Section titled “Screen dimensions”](#screen-dimensions)
|
|
2459
|
+
|
|
2307
2460
|
The most basic dimensions are the screen dimensions. These are the dimensions of the screen that your app is running on. You can access them with the `screen` prop:
|
|
2308
2461
|
|
|
2309
2462
|
```tsx
|
|
@@ -2316,6 +2469,8 @@ UnistylesRuntime.screen.height // eg. 760
|
|
|
2316
2469
|
|
|
2317
2470
|
### Status bar
|
|
2318
2471
|
|
|
2472
|
+
[Section titled “Status bar”](#status-bar)
|
|
2473
|
+
|
|
2319
2474
|
You can access status bar dimensions with the `statusBar` prop:
|
|
2320
2475
|
|
|
2321
2476
|
```tsx
|
|
@@ -2330,6 +2485,8 @@ This prop may be useful for creating custom headers. In most of the cases status
|
|
|
2330
2485
|
|
|
2331
2486
|
### Navigation bar
|
|
2332
2487
|
|
|
2488
|
+
[Section titled “Navigation bar”](#navigation-bar)
|
|
2489
|
+
|
|
2333
2490
|
You can access navigation bar dimensions with `navigationBar` prop:
|
|
2334
2491
|
|
|
2335
2492
|
```tsx
|
|
@@ -2344,6 +2501,8 @@ This prop may be useful for creating custom bottom bars. In most of the cases na
|
|
|
2344
2501
|
|
|
2345
2502
|
### Insets
|
|
2346
2503
|
|
|
2504
|
+
[Section titled “Insets”](#insets)
|
|
2505
|
+
|
|
2347
2506
|
Insets are the safe areas of the screen. They are used to avoid overlapping with system UI elements such as the status bar, navigation bar, and home indicator. You can access them with `insets` prop:
|
|
2348
2507
|
|
|
2349
2508
|
```tsx
|
|
@@ -2379,6 +2538,8 @@ Unistyles automatically reacts when you hide or show status and navigation bars.
|
|
|
2379
2538
|
|
|
2380
2539
|
### Pixel ratio
|
|
2381
2540
|
|
|
2541
|
+
[Section titled “Pixel ratio”](#pixel-ratio)
|
|
2542
|
+
|
|
2382
2543
|
Device Pixel Ratio (DPR) is the ratio between physical pixels and device-independent pixels (DIPs) on a screen. It determines how many physical pixels are used to represent a single CSS pixel.
|
|
2383
2544
|
|
|
2384
2545
|
Most likely, your phone pixel ratio ranges between 1.0 to 3.0 (retina).
|
|
@@ -2389,6 +2550,8 @@ UnistylesRuntime.pixelRatio // eg. 2.0
|
|
|
2389
2550
|
|
|
2390
2551
|
### Font scale
|
|
2391
2552
|
|
|
2553
|
+
[Section titled “Font scale”](#font-scale)
|
|
2554
|
+
|
|
2392
2555
|
Font scale is a ratio between the font size of the device and the default font size. It is used to adjust the size of text on the screen in companion with [content size category](/v3/references/content-size-category/).
|
|
2393
2556
|
|
|
2394
2557
|
```tsx
|
|
@@ -2407,6 +2570,8 @@ With Unistyles 3.0, preferred way of listening for breakpoint changes is with `D
|
|
|
2407
2570
|
|
|
2408
2571
|
### Display
|
|
2409
2572
|
|
|
2573
|
+
[Section titled “Display”](#display)
|
|
2574
|
+
|
|
2410
2575
|
The Display component helps you show its children based on `breakpoints` or `media queries`.
|
|
2411
2576
|
|
|
2412
2577
|
```tsx
|
|
@@ -2451,6 +2616,8 @@ const Component = () => {
|
|
|
2451
2616
|
|
|
2452
2617
|
### Hide
|
|
2453
2618
|
|
|
2619
|
+
[Section titled “Hide”](#hide)
|
|
2620
|
+
|
|
2454
2621
|
On the opposite side, the `Hide` component helps you hide its children based on `breakpoints` or `media queries`. It works exactly the same way as the Display component.
|
|
2455
2622
|
|
|
2456
2623
|
```tsx
|
|
@@ -2488,9 +2655,11 @@ If you need to pass a value from JSX to your `stylesheet` you can do so using a
|
|
|
2488
2655
|
|
|
2489
2656
|
### Usage
|
|
2490
2657
|
|
|
2658
|
+
[Section titled “Usage”](#usage)
|
|
2659
|
+
|
|
2491
2660
|
To use a dynamic function, change **any** stylesheet’s value from an `object` to a `function`:
|
|
2492
2661
|
|
|
2493
|
-
```
|
|
2662
|
+
```diff
|
|
2494
2663
|
const styles = StyleSheet.create(theme => ({
|
|
2495
2664
|
container: {
|
|
2496
2665
|
container: () => ({
|
|
@@ -2498,7 +2667,7 @@ const styles = StyleSheet.create(theme => ({
|
|
|
2498
2667
|
flex: 1,
|
|
2499
2668
|
justifyContent: 'center',
|
|
2500
2669
|
alignItems: 'center'
|
|
2501
|
-
}
|
|
2670
|
+
-}
|
|
2502
2671
|
})
|
|
2503
2672
|
}))
|
|
2504
2673
|
```
|
|
@@ -2537,12 +2706,16 @@ Keep in mind that a dynamic function can accept only serializable arguments. The
|
|
|
2537
2706
|
|
|
2538
2707
|
### iOS
|
|
2539
2708
|
|
|
2709
|
+
[Section titled “iOS”](#ios)
|
|
2710
|
+
|
|
2540
2711
|
Unistyles uses native `SafeAreaInsets` API to handle insets on iOS. This API is stable and works the same across all iOS versions.
|
|
2541
2712
|
|
|
2542
2713
|
Most likely, you’ll never receive incorrect inset values on iOS.
|
|
2543
2714
|
|
|
2544
2715
|
### Android
|
|
2545
2716
|
|
|
2717
|
+
[Section titled “Android”](#android)
|
|
2718
|
+
|
|
2546
2719
|
Unistyles uses `WindowsInsetsCompat` API to handle insets on Android. This API requires your app to have edge to edge layout enabled. In other words, it means that your `StatusBar` is always `translucent` and the app can draw behind the `NavigationBar`. A translucent status bar is also the default when you build your app with Expo. To leverage `WindowInsetsCompat`, Unistyles enables `edgeToEdge` layout by default.
|
|
2547
2720
|
|
|
2548
2721
|
As a result you need to use paddings to draw your app content above system bars. To learn more about `edgeToEdge` layout please check [Window insets in Compose](https://developer.android.com/develop/ui/compose/layouts/insets).
|
|
@@ -2580,9 +2753,11 @@ Apps are edge-to-edge by default on devices running Android 15 if the app is tar
|
|
|
2580
2753
|
|
|
2581
2754
|
[Learn more](https://developer.android.com/about/versions/15/behavior-changes-15)
|
|
2582
2755
|
|
|
2583
|
-
|
|
2756
|
+
Enable edgeToEdgeEnabled in gradle.properties
|
|
2584
2757
|
|
|
2585
|
-
|
|
2758
|
+
We **strongly recommend** setting `edgeToEdgeEnabled=true` in your `android/gradle.properties`. Beyond enabling edge-to-edge layout, it enforces translucent system bars on modals, disables legacy StatusBar hacks, and enables additional React Native core fixes. **Expo SDK 54+** enables this automatically.
|
|
2759
|
+
|
|
2760
|
+
Since v3.1.0, `react-native-edge-to-edge` is optional — you can still install it for ecosystem compatibility. Learn more [here](/v3/other/dependencies#react-native-edge-to-edge-optional-since-v310).
|
|
2586
2761
|
|
|
2587
2762
|
# Media Queries
|
|
2588
2763
|
|
|
@@ -2592,9 +2767,11 @@ Media queries provide more power and allow you to style cross-platform apps with
|
|
|
2592
2767
|
|
|
2593
2768
|
### Basic usage
|
|
2594
2769
|
|
|
2770
|
+
[Section titled “Basic usage”](#basic-usage)
|
|
2771
|
+
|
|
2595
2772
|
To use media queries, you need to import the `mq` utility and convert your value to an `object`:
|
|
2596
2773
|
|
|
2597
|
-
```
|
|
2774
|
+
```diff
|
|
2598
2775
|
import { Stylesheet, mq } from 'react-native-unistyles'
|
|
2599
2776
|
|
|
2600
2777
|
|
|
@@ -2605,9 +2782,9 @@ const styles = Stylesheet.create(theme => ({
|
|
|
2605
2782
|
alignItems: 'center'
|
|
2606
2783
|
backgroundColor: theme.colors.background,
|
|
2607
2784
|
backgroundColor: {
|
|
2608
|
-
[mq.only.width(240, 380)]: theme.colors.background,
|
|
2609
|
-
[mq.only.width(380)]: theme.colors.barbie
|
|
2610
|
-
}
|
|
2785
|
+
+[mq.only.width(240, 380)]: theme.colors.background,
|
|
2786
|
+
+[mq.only.width(380)]: theme.colors.barbie
|
|
2787
|
+
+}
|
|
2611
2788
|
}
|
|
2612
2789
|
}))
|
|
2613
2790
|
```
|
|
@@ -2616,6 +2793,8 @@ The `mq` utility provides Intellisense for quickly building your media queries.
|
|
|
2616
2793
|
|
|
2617
2794
|
### Advanced usage
|
|
2618
2795
|
|
|
2796
|
+
[Section titled “Advanced usage”](#advanced-usage)
|
|
2797
|
+
|
|
2619
2798
|
You can also combine `width` media queries with `height` media queries:
|
|
2620
2799
|
|
|
2621
2800
|
```tsx
|
|
@@ -2678,6 +2857,8 @@ const styles = Stylesheet.create(theme => ({
|
|
|
2678
2857
|
|
|
2679
2858
|
### Reference
|
|
2680
2859
|
|
|
2860
|
+
[Section titled “Reference”](#reference)
|
|
2861
|
+
|
|
2681
2862
|
Available combinations
|
|
2682
2863
|
|
|
2683
2864
|
```shell
|
|
@@ -2712,6 +2893,8 @@ If you pass an invalid range to mq utility eg. (‘xl’, ‘sm’) or (500, 200
|
|
|
2712
2893
|
|
|
2713
2894
|
### Combining media queries with breakpoints
|
|
2714
2895
|
|
|
2896
|
+
[Section titled “Combining media queries with breakpoints”](#combining-media-queries-with-breakpoints)
|
|
2897
|
+
|
|
2715
2898
|
You can mix media queries with breakpoints, but media queries will always have higher priority:
|
|
2716
2899
|
|
|
2717
2900
|
```tsx
|
|
@@ -2734,6 +2917,8 @@ const styles = Stylesheet.create(theme => ({
|
|
|
2734
2917
|
|
|
2735
2918
|
### CSS Media Queries
|
|
2736
2919
|
|
|
2920
|
+
[Section titled “CSS Media Queries”](#css-media-queries)
|
|
2921
|
+
|
|
2737
2922
|
`Breakpoints` and `Media Queries` will be auto converted to Web CSS media queries. Learn more about [Web Media Queries](/v3/references/web-styles#how-it-works).
|
|
2738
2923
|
|
|
2739
2924
|
# Mini Runtime
|
|
@@ -2777,6 +2962,8 @@ To address this, Unistyles 3.0 introduces the concept of a `Scoped Theme`, which
|
|
|
2777
2962
|
|
|
2778
2963
|
### Usage with named theme
|
|
2779
2964
|
|
|
2965
|
+
[Section titled “Usage with named theme”](#usage-with-named-theme)
|
|
2966
|
+
|
|
2780
2967
|
To use scoped theme, you need to import `ScopedTheme` component from `react-native-unistyles`:
|
|
2781
2968
|
|
|
2782
2969
|
```ts
|
|
@@ -2825,6 +3012,8 @@ You can also nest `ScopedTheme` components:
|
|
|
2825
3012
|
|
|
2826
3013
|
### Usage with inverted adaptive theme
|
|
2827
3014
|
|
|
3015
|
+
[Section titled “Usage with inverted adaptive theme”](#usage-with-inverted-adaptive-theme)
|
|
3016
|
+
|
|
2828
3017
|
You can also use `ScopedTheme` with the `invertedAdaptive` prop. This prop cannot be used together with a named `ScopedTheme`, as these options are mutually exclusive. The purpose of `invertedAdaptive` is to apply the opposite adaptive theme to the one that is currently active.
|
|
2829
3018
|
|
|
2830
3019
|
In other words, if your app supports [adaptive themes](/v3/guides/theming#adaptive-themes) and you use `ScopedTheme` with the `invertedAdaptive` prop, it will apply:
|
|
@@ -2858,6 +3047,8 @@ You can also nest other `ScopedThemes` inside `ScopedTheme` with `invertedAdapti
|
|
|
2858
3047
|
|
|
2859
3048
|
### Reset
|
|
2860
3049
|
|
|
3050
|
+
[Section titled “Reset”](#reset)
|
|
3051
|
+
|
|
2861
3052
|
If you wrap multiple children in `ScopedTheme` you can disable scoped theme for some of them by using `reset` prop:
|
|
2862
3053
|
|
|
2863
3054
|
```tsx
|
|
@@ -2884,6 +3075,8 @@ If you wrap multiple children in `ScopedTheme` you can disable scoped theme for
|
|
|
2884
3075
|
|
|
2885
3076
|
### Reading current scoped theme
|
|
2886
3077
|
|
|
3078
|
+
[Section titled “Reading current scoped theme”](#reading-current-scoped-theme)
|
|
3079
|
+
|
|
2887
3080
|
Information about the current `ScopedTheme` is temporary and only available during the component render phase.
|
|
2888
3081
|
|
|
2889
3082
|
For the following example, `themeName` will be different based on the place where we access it:
|
|
@@ -2976,6 +3169,91 @@ const ScopedComponent = () => {
|
|
|
2976
3169
|
</ScopedTheme>
|
|
2977
3170
|
```
|
|
2978
3171
|
|
|
3172
|
+
### Scoped Theme with Suspense
|
|
3173
|
+
|
|
3174
|
+
[Section titled “Scoped Theme with Suspense”](#scoped-theme-with-suspense)
|
|
3175
|
+
|
|
3176
|
+
When using `ScopedTheme` with React’s `Suspense`, there’s an important consideration about component placement due to how React handles suspension and re-rendering.
|
|
3177
|
+
|
|
3178
|
+
React Suspense works by catching promises thrown by child components that are waiting for data. When this happens:
|
|
3179
|
+
|
|
3180
|
+
1. React pauses rendering and shows the fallback content
|
|
3181
|
+
2. Components that successfully rendered before the suspension may be reused
|
|
3182
|
+
3. Parent components might not re-render when the suspended data becomes available
|
|
3183
|
+
|
|
3184
|
+
This means if you place `ScopedTheme` above a component that suspends, the scoped theme might not be applied correctly when the component finally renders:
|
|
3185
|
+
|
|
3186
|
+
```tsx
|
|
3187
|
+
// ❌ This won't work correctly
|
|
3188
|
+
<Suspense fallback={<Loading />}>
|
|
3189
|
+
<ScopedTheme name="dark">
|
|
3190
|
+
<SuspendedComponent /> {/* ScopedTheme already rendered before suspension */}
|
|
3191
|
+
</ScopedTheme>
|
|
3192
|
+
</Suspense>
|
|
3193
|
+
```
|
|
3194
|
+
|
|
3195
|
+
Unistyles ScopedTheme is only available during render phase, we decided to not use `React.Context` to keep the API performant and easy to use.
|
|
3196
|
+
|
|
3197
|
+
To fix this issue, you can move the `ScopedTheme` inside the suspended component:
|
|
3198
|
+
|
|
3199
|
+
```tsx
|
|
3200
|
+
// ✅ Place ScopedTheme inside the component that suspends
|
|
3201
|
+
const SuspendedComponent = () => {
|
|
3202
|
+
const data = useSuspenseQuery(); // This throws a promise
|
|
3203
|
+
|
|
3204
|
+
|
|
3205
|
+
return (
|
|
3206
|
+
<ScopedTheme name="dark">
|
|
3207
|
+
<View style={styles.container}>
|
|
3208
|
+
<Text style={styles.text}>
|
|
3209
|
+
{data.title}
|
|
3210
|
+
</Text>
|
|
3211
|
+
</View>
|
|
3212
|
+
</ScopedTheme>
|
|
3213
|
+
);
|
|
3214
|
+
};
|
|
3215
|
+
|
|
3216
|
+
|
|
3217
|
+
<Suspense fallback={<Loading />}>
|
|
3218
|
+
<SuspendedComponent />
|
|
3219
|
+
</Suspense>
|
|
3220
|
+
```
|
|
3221
|
+
|
|
3222
|
+
The key is to ensure that `ScopedTheme` is rendered **after** the suspension occurs, so that when React re-renders the suspended component tree, the scoped theme context is properly established.
|
|
3223
|
+
|
|
3224
|
+
This pattern ensures that your themed components will render with the correct theme once the suspended data becomes available.
|
|
3225
|
+
|
|
3226
|
+
### Scoped Theme and Hot Module Reloading (HMR)
|
|
3227
|
+
|
|
3228
|
+
[Section titled “Scoped Theme and Hot Module Reloading (HMR)”](#scoped-theme-and-hot-module-reloading-hmr)
|
|
3229
|
+
|
|
3230
|
+
When working with `ScopedTheme` in development, you might notice that Hot Module Reloading doesn’t always update the theme when you make changes to child components. This is a limitation of Metro’s Fast Refresh system.
|
|
3231
|
+
|
|
3232
|
+
Unlike Webpack, Metro’s Fast Refresh only re-runs code in the file you’re actively editing and its direct imports. It doesn’t have a global event system that can notify parent components when child modules change.
|
|
3233
|
+
|
|
3234
|
+
Metro provides these HMR functions:
|
|
3235
|
+
|
|
3236
|
+
```tsx
|
|
3237
|
+
module.hot.accept(fn) // fires if *this* module is updated
|
|
3238
|
+
module.hot.dispose(fn) // fires just before *this* module is replaced
|
|
3239
|
+
```
|
|
3240
|
+
|
|
3241
|
+
However, **neither of these runs** when other modules change. This means that changes in child components won’t trigger a re-render of their parent `ScopedTheme`:
|
|
3242
|
+
|
|
3243
|
+
```tsx
|
|
3244
|
+
<ScopedTheme name="light">
|
|
3245
|
+
<ChildComponent /> {/* Changes in this file won't trigger ScopedTheme to update */}
|
|
3246
|
+
</ScopedTheme>
|
|
3247
|
+
```
|
|
3248
|
+
|
|
3249
|
+
#### Why We Don’t Use React Context
|
|
3250
|
+
|
|
3251
|
+
[Section titled “Why We Don’t Use React Context”](#why-we-dont-use-react-context)
|
|
3252
|
+
|
|
3253
|
+
The “ideal” solution would be to use React Context for theme propagation, which would work seamlessly with HMR. However, we’ve chosen performance over convenience. Using React Context would introduce additional re-renders and overhead that could impact your app’s performance, especially in complex component trees.
|
|
3254
|
+
|
|
3255
|
+
We prioritize keeping the API fast and lightweight, even if it means accepting some development-time limitations with HMR.
|
|
3256
|
+
|
|
2979
3257
|
# StyleSheet
|
|
2980
3258
|
|
|
2981
3259
|
> Learn about StyleSheet in Unistyles 3.0
|
|
@@ -2984,6 +3262,8 @@ const ScopedComponent = () => {
|
|
|
2984
3262
|
|
|
2985
3263
|
### create
|
|
2986
3264
|
|
|
3265
|
+
[Section titled “create”](#create)
|
|
3266
|
+
|
|
2987
3267
|
The `create` function supports all styles that React Native’s StyleSheet does, and it also enables some superpowers 🦸🏼♂️. It can parse your `variants`, `compoundVariants` or `dynamic functions` (even if you haven’t configured Unistyles yet!).
|
|
2988
3268
|
|
|
2989
3269
|
Once you register your `themes` and `breakpoints`, it unlocks even more features, like injecting the current `theme` or `miniRuntime` into your stylesheet. It also assists you with TypeScript autocompletion for your styles.
|
|
@@ -3058,6 +3338,8 @@ Unistyles StyleSheet will automatically react and recalculate your styles if any
|
|
|
3058
3338
|
|
|
3059
3339
|
#### Static StyleSheet
|
|
3060
3340
|
|
|
3341
|
+
[Section titled “Static StyleSheet”](#static-stylesheet)
|
|
3342
|
+
|
|
3061
3343
|
```tsx
|
|
3062
3344
|
import { StyleSheet } from 'react-native-unistyles'
|
|
3063
3345
|
|
|
@@ -3071,6 +3353,8 @@ const styles = StyleSheet.create({
|
|
|
3071
3353
|
|
|
3072
3354
|
#### Themable StyleSheet
|
|
3073
3355
|
|
|
3356
|
+
[Section titled “Themable StyleSheet”](#themable-stylesheet)
|
|
3357
|
+
|
|
3074
3358
|
```tsx
|
|
3075
3359
|
import { StyleSheet } from 'react-native-unistyles'
|
|
3076
3360
|
|
|
@@ -3084,6 +3368,8 @@ const styles = StyleSheet.create(theme => ({
|
|
|
3084
3368
|
|
|
3085
3369
|
#### Themable StyleSheet with `miniRuntime`
|
|
3086
3370
|
|
|
3371
|
+
[Section titled “Themable StyleSheet with miniRuntime”](#themable-stylesheet-with-miniruntime)
|
|
3372
|
+
|
|
3087
3373
|
```tsx
|
|
3088
3374
|
import { StyleSheet } from 'react-native-unistyles'
|
|
3089
3375
|
|
|
@@ -3100,6 +3386,8 @@ Learn more about `miniRuntime` [here](/v3/references/mini-runtime/).
|
|
|
3100
3386
|
|
|
3101
3387
|
### configure
|
|
3102
3388
|
|
|
3389
|
+
[Section titled “configure”](#configure)
|
|
3390
|
+
|
|
3103
3391
|
`StyleSheet.configure` is used to configure Unistyles. It accepts an object with the following properties:
|
|
3104
3392
|
|
|
3105
3393
|
* `themes` your apps themes
|
|
@@ -3112,8 +3400,48 @@ The `configure` function **must** be called before you import any component that
|
|
|
3112
3400
|
|
|
3113
3401
|
You can learn more about how to configure Unistyles [here](/v3/start/configuration).
|
|
3114
3402
|
|
|
3403
|
+
### addChangeListener Since v3.1.0
|
|
3404
|
+
|
|
3405
|
+
[Section titled “addChangeListener ”](#addchangelistener)
|
|
3406
|
+
|
|
3407
|
+
`StyleSheet.addChangeListener` is an advanced API for integrations, custom hooks, and animation helpers that need to react to runtime dependency updates.
|
|
3408
|
+
|
|
3409
|
+
For regular styles, you do not need to use it manually. Unistyles already recalculates `StyleSheet.create` output whenever the relevant dependencies change.
|
|
3410
|
+
|
|
3411
|
+
Signature:
|
|
3412
|
+
|
|
3413
|
+
```ts
|
|
3414
|
+
addChangeListener(
|
|
3415
|
+
onChanged: (dependencies: Array<UnistyleDependency>) => void
|
|
3416
|
+
): () => void
|
|
3417
|
+
```
|
|
3418
|
+
|
|
3419
|
+
The callback receives one or more `UnistyleDependency` values describing what changed. The function returns an unsubscribe callback.
|
|
3420
|
+
|
|
3421
|
+
```tsx
|
|
3422
|
+
import { useEffect } from 'react'
|
|
3423
|
+
import { StyleSheet, UnistyleDependency } from 'react-native-unistyles'
|
|
3424
|
+
|
|
3425
|
+
|
|
3426
|
+
useEffect(() => {
|
|
3427
|
+
const dispose = StyleSheet.addChangeListener((dependencies) => {
|
|
3428
|
+
if (
|
|
3429
|
+
dependencies.includes(UnistyleDependency.Theme) ||
|
|
3430
|
+
dependencies.includes(UnistyleDependency.Breakpoints)
|
|
3431
|
+
) {
|
|
3432
|
+
// react to theme or breakpoint updates
|
|
3433
|
+
}
|
|
3434
|
+
})
|
|
3435
|
+
|
|
3436
|
+
|
|
3437
|
+
return dispose
|
|
3438
|
+
}, [])
|
|
3439
|
+
```
|
|
3440
|
+
|
|
3115
3441
|
### hairlineWidth
|
|
3116
3442
|
|
|
3443
|
+
[Section titled “hairlineWidth”](#hairlinewidth)
|
|
3444
|
+
|
|
3117
3445
|
`StyleSheet.hairlineWidth` is a static value representing the smallest value that can be drawn on your device. It’s helpful for borders or dividers.
|
|
3118
3446
|
|
|
3119
3447
|
```tsx
|
|
@@ -3130,14 +3458,20 @@ const styles = StyleSheet.create(theme => ({
|
|
|
3130
3458
|
|
|
3131
3459
|
### compose
|
|
3132
3460
|
|
|
3461
|
+
[Section titled “compose”](#compose)
|
|
3462
|
+
|
|
3133
3463
|
Maps to React Native’s [compose function](https://reactnative.dev/docs/stylesheet#compose).
|
|
3134
3464
|
|
|
3135
3465
|
### flatten
|
|
3136
3466
|
|
|
3467
|
+
[Section titled “flatten”](#flatten)
|
|
3468
|
+
|
|
3137
3469
|
Maps to React Native’s [flatten function](https://reactnative.dev/docs/stylesheet#flatten).
|
|
3138
3470
|
|
|
3139
3471
|
### absoluteFillObject
|
|
3140
3472
|
|
|
3473
|
+
[Section titled “absoluteFillObject”](#absolutefillobject)
|
|
3474
|
+
|
|
3141
3475
|
Returns following object:
|
|
3142
3476
|
|
|
3143
3477
|
```ts
|
|
@@ -3152,6 +3486,8 @@ Returns following object:
|
|
|
3152
3486
|
|
|
3153
3487
|
### absoluteFill
|
|
3154
3488
|
|
|
3489
|
+
[Section titled “absoluteFill”](#absolutefill)
|
|
3490
|
+
|
|
3155
3491
|
Returns following object:
|
|
3156
3492
|
|
|
3157
3493
|
```ts
|
|
@@ -3172,6 +3508,8 @@ Unistyles Runtime is a powerful feature that allows you to access platform speci
|
|
|
3172
3508
|
|
|
3173
3509
|
### Usage
|
|
3174
3510
|
|
|
3511
|
+
[Section titled “Usage”](#usage)
|
|
3512
|
+
|
|
3175
3513
|
You can import `UnistylesRuntime` from `react-native-unistyles`:
|
|
3176
3514
|
|
|
3177
3515
|
```tsx
|
|
@@ -3182,6 +3520,8 @@ and use it anywhere in your code, even outside a React component.
|
|
|
3182
3520
|
|
|
3183
3521
|
### Available getters
|
|
3184
3522
|
|
|
3523
|
+
[Section titled “Available getters”](#available-getters)
|
|
3524
|
+
|
|
3185
3525
|
| Name | Type | Description |
|
|
3186
3526
|
| ------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
3187
3527
|
| colorScheme | string | Get your device’s color scheme. Available options `dark`, `light` or `unspecified` |
|
|
@@ -3203,6 +3543,8 @@ and use it anywhere in your code, even outside a React component.
|
|
|
3203
3543
|
|
|
3204
3544
|
## Setters
|
|
3205
3545
|
|
|
3546
|
+
[Section titled “Setters”](#setters)
|
|
3547
|
+
|
|
3206
3548
|
| Name | Type | Description |
|
|
3207
3549
|
| -------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
3208
3550
|
| setTheme | (themeName: string) => void | Change the current theme |
|
|
@@ -3215,12 +3557,16 @@ and use it anywhere in your code, even outside a React component.
|
|
|
3215
3557
|
|
|
3216
3558
|
### Why `UnistylesRuntime` doesn’t re-render my component?
|
|
3217
3559
|
|
|
3560
|
+
[Section titled “Why UnistylesRuntime doesn’t re-render my component?”](#why-unistylesruntime-doesnt-re-render-my-component)
|
|
3561
|
+
|
|
3218
3562
|
You should think of `UnistylesRuntime` as a JavaScript object. It’s not a React hook, so it doesn’t re-render your component when eg. screen size or breakpoint changes. Instead it will return up to date value whenever you access it.
|
|
3219
3563
|
|
|
3220
3564
|
If you’re looking for a way to get fresh values and re-render your component, please check [useUnistyles](/v3/references/use-unistyles) hook.
|
|
3221
3565
|
|
|
3222
3566
|
### How to re-render my stylesheets based on `UnistylesRuntime`?
|
|
3223
3567
|
|
|
3568
|
+
[Section titled “How to re-render my stylesheets based on UnistylesRuntime?”](#how-to-re-render-my-stylesheets-based-on-unistylesruntime)
|
|
3569
|
+
|
|
3224
3570
|
You can do that while accessing [miniRuntime](/v3/references/mini-runtime/) in your `StyleSheet`:
|
|
3225
3571
|
|
|
3226
3572
|
One example could be reading device width and height:
|
|
@@ -3259,6 +3605,8 @@ Follow our [decision algorithm](/v3/references/3rd-party-views) to learn when to
|
|
|
3259
3605
|
|
|
3260
3606
|
### When to use it?
|
|
3261
3607
|
|
|
3608
|
+
[Section titled “When to use it?”](#when-to-use-it)
|
|
3609
|
+
|
|
3262
3610
|
If you’re using `react-native`, or `react-native-reanimated` components, you should avoid this hook. Unistyles updates these views via the ShadowTree without causing **any re-renders**.
|
|
3263
3611
|
|
|
3264
3612
|
Consider using this hook only if:
|
|
@@ -3270,6 +3618,8 @@ Consider using this hook only if:
|
|
|
3270
3618
|
|
|
3271
3619
|
### How to use it?
|
|
3272
3620
|
|
|
3621
|
+
[Section titled “How to use it?”](#how-to-use-it)
|
|
3622
|
+
|
|
3273
3623
|
This is a standard hook that exposes `theme` and `rt` ([runtime](/v3/references/mini-runtime)) properties. You can import it from `react-native-unistyles`:
|
|
3274
3624
|
|
|
3275
3625
|
```tsx
|
|
@@ -3311,6 +3661,8 @@ rt.insets // reading this value will automatically subscribe to insets changes
|
|
|
3311
3661
|
|
|
3312
3662
|
### Why isn’t it recommended?
|
|
3313
3663
|
|
|
3664
|
+
[Section titled “Why isn’t it recommended?”](#why-isnt-it-recommended)
|
|
3665
|
+
|
|
3314
3666
|
We encourage using `withUnistyles` instead because it ensures only a single component is re-rendered instead of multiple components or the entire app. If you use this hook in a root component, you lose all the benefits of ShadowTree updates and trigger full app re-renders on every change.
|
|
3315
3667
|
|
|
3316
3668
|
Learn more about [How Unistyles works?](/v3/start/how-unistyles-works) to understand why this is not ideal.
|
|
@@ -3319,10 +3671,14 @@ Another advantage of `withUnistyles` is that it tracks style dependencies, ensur
|
|
|
3319
3671
|
|
|
3320
3672
|
### How to use it correctly?
|
|
3321
3673
|
|
|
3674
|
+
[Section titled “How to use it correctly?”](#how-to-use-it-correctly)
|
|
3675
|
+
|
|
3322
3676
|
If you must use this hook, follow these best practices:
|
|
3323
3677
|
|
|
3324
3678
|
#### 1. Use it only for a single component
|
|
3325
3679
|
|
|
3680
|
+
[Section titled “1. Use it only for a single component”](#1-use-it-only-for-a-single-component)
|
|
3681
|
+
|
|
3326
3682
|
```tsx
|
|
3327
3683
|
import { useUnistyles } from 'react-native-unistyles'
|
|
3328
3684
|
import Icon from 'react-native-cool-icons/MaterialIcons'
|
|
@@ -3343,6 +3699,8 @@ Like `withUnistyles`, create a new component and use the hook there. Avoid using
|
|
|
3343
3699
|
|
|
3344
3700
|
#### 2. Use it with `react-navigation` components like `Stack` or `Tabs`
|
|
3345
3701
|
|
|
3702
|
+
[Section titled “2. Use it with react-navigation components like Stack or Tabs”](#2-use-it-with-react-navigation-components-like-stack-or-tabs)
|
|
3703
|
+
|
|
3346
3704
|
```tsx
|
|
3347
3705
|
import { Stack } from 'expo-router'
|
|
3348
3706
|
|
|
@@ -3369,12 +3727,18 @@ This is allowed because `react-navigation` does not re-render screens on style p
|
|
|
3369
3727
|
|
|
3370
3728
|
#### 3. Migration from Unistyles 2.0
|
|
3371
3729
|
|
|
3730
|
+
[Section titled “3. Migration from Unistyles 2.0”](#3-migration-from-unistyles-20)
|
|
3731
|
+
|
|
3372
3732
|
If you’re migrating from version 2.0 to 3.0, you can use `useUnistyles` to access the theme and runtime in your components. This works similarly to the `useStyles` hook in 2.0. Once migration is complete, refactor your code to align with Unistyles 3.0 principles.
|
|
3373
3733
|
|
|
3374
3734
|
### Bad Practices
|
|
3375
3735
|
|
|
3736
|
+
[Section titled “Bad Practices”](#bad-practices)
|
|
3737
|
+
|
|
3376
3738
|
#### 1. Using it with complex components:
|
|
3377
3739
|
|
|
3740
|
+
[Section titled “1. Using it with complex components:”](#1-using-it-with-complex-components)
|
|
3741
|
+
|
|
3378
3742
|
```tsx
|
|
3379
3743
|
import { useUnistyles } from 'react-native-unistyles'
|
|
3380
3744
|
import { Blurhash } from 'react-native-blurhash'
|
|
@@ -3404,6 +3768,8 @@ This will re-render multiple components unnecessarily. Instead move `Blurhash` t
|
|
|
3404
3768
|
|
|
3405
3769
|
#### 2. Using it at the root level:
|
|
3406
3770
|
|
|
3771
|
+
[Section titled “2. Using it at the root level:”](#2-using-it-at-the-root-level)
|
|
3772
|
+
|
|
3407
3773
|
```tsx
|
|
3408
3774
|
import { useUnistyles } from 'react-native-unistyles'
|
|
3409
3775
|
|
|
@@ -3424,6 +3790,8 @@ Using the hook at the root level eliminates all Unistyles benefits, causing your
|
|
|
3424
3790
|
|
|
3425
3791
|
#### 3. Using it with `react-native` components:
|
|
3426
3792
|
|
|
3793
|
+
[Section titled “3. Using it with react-native components:”](#3-using-it-with-react-native-components)
|
|
3794
|
+
|
|
3427
3795
|
```tsx
|
|
3428
3796
|
import { useUnistyles } from 'react-native-unistyles'
|
|
3429
3797
|
import { Text } from 'react-native'
|
|
@@ -3451,6 +3819,8 @@ Variants helps you to create a more flexible and reusable stylesheet eg. for you
|
|
|
3451
3819
|
|
|
3452
3820
|
### Basic usage
|
|
3453
3821
|
|
|
3822
|
+
[Section titled “Basic usage”](#basic-usage)
|
|
3823
|
+
|
|
3454
3824
|
Variants are objects that can be nested in any style object:
|
|
3455
3825
|
|
|
3456
3826
|
```tsx
|
|
@@ -3528,6 +3898,8 @@ const styles = StyleSheet.create(theme => ({
|
|
|
3528
3898
|
|
|
3529
3899
|
### Selecting variants
|
|
3530
3900
|
|
|
3901
|
+
[Section titled “Selecting variants”](#selecting-variants)
|
|
3902
|
+
|
|
3531
3903
|
With your named groups, you can now select any variant from your stylesheet using the `useVariants`:
|
|
3532
3904
|
|
|
3533
3905
|
```tsx
|
|
@@ -3554,6 +3926,8 @@ TypeScript will provide perfect autocompletion for your variants, ensuring accur
|
|
|
3554
3926
|
|
|
3555
3927
|
### Selecting variants with boolean values
|
|
3556
3928
|
|
|
3929
|
+
[Section titled “Selecting variants with boolean values”](#selecting-variants-with-boolean-values)
|
|
3930
|
+
|
|
3557
3931
|
You can also use boolean values to select variants:
|
|
3558
3932
|
|
|
3559
3933
|
```tsx
|
|
@@ -3614,6 +3988,8 @@ Boolean variants respects other rules, eg. `false` is not equal to `default`. To
|
|
|
3614
3988
|
|
|
3615
3989
|
### Default variant
|
|
3616
3990
|
|
|
3991
|
+
[Section titled “Default variant”](#default-variant)
|
|
3992
|
+
|
|
3617
3993
|
You can define a `default` variant that will be used when you don’t pass any variant to the `useVariants` hook:
|
|
3618
3994
|
|
|
3619
3995
|
```tsx
|
|
@@ -3639,6 +4015,8 @@ const styles = StyleSheet.create(theme => ({
|
|
|
3639
4015
|
|
|
3640
4016
|
### Options to select the variant
|
|
3641
4017
|
|
|
4018
|
+
[Section titled “Options to select the variant”](#options-to-select-the-variant)
|
|
4019
|
+
|
|
3642
4020
|
If you pass `undefined` or `empty object` Unsityles will try to find the `default` variant in your stylesheet:
|
|
3643
4021
|
|
|
3644
4022
|
```tsx
|
|
@@ -3666,6 +4044,8 @@ styles.useVariants({
|
|
|
3666
4044
|
|
|
3667
4045
|
### Pass variants as component props
|
|
3668
4046
|
|
|
4047
|
+
[Section titled “Pass variants as component props”](#pass-variants-as-component-props)
|
|
4048
|
+
|
|
3669
4049
|
Variants were designed to be used as component props:
|
|
3670
4050
|
|
|
3671
4051
|
```tsx
|
|
@@ -3694,6 +4074,8 @@ const Component: React.FunctionComponent = ({ color, size }) => {
|
|
|
3694
4074
|
|
|
3695
4075
|
### Infer TypeScript type for your variants
|
|
3696
4076
|
|
|
4077
|
+
[Section titled “Infer TypeScript type for your variants”](#infer-typescript-type-for-your-variants)
|
|
4078
|
+
|
|
3697
4079
|
Instead of using `enum` or `strings` with `|` , you can use `UnistylesVariants` to infer the type of your variants:
|
|
3698
4080
|
|
|
3699
4081
|
```tsx
|
|
@@ -3723,6 +4105,8 @@ const styles = ...
|
|
|
3723
4105
|
|
|
3724
4106
|
### Defining the same variant across multiple styles
|
|
3725
4107
|
|
|
4108
|
+
[Section titled “Defining the same variant across multiple styles”](#defining-the-same-variant-across-multiple-styles)
|
|
4109
|
+
|
|
3726
4110
|
It’s possible to define the same variant group across multiple styles:
|
|
3727
4111
|
|
|
3728
4112
|
```tsx
|
|
@@ -3862,6 +4246,8 @@ Unistyles comes with some web-only features that are not available with React Na
|
|
|
3862
4246
|
|
|
3863
4247
|
### Web only styles
|
|
3864
4248
|
|
|
4249
|
+
[Section titled “Web only styles”](#web-only-styles)
|
|
4250
|
+
|
|
3865
4251
|
In Unistyles, you can use web-specific styles for your web app under the `_web` key.
|
|
3866
4252
|
|
|
3867
4253
|
```ts
|
|
@@ -3899,7 +4285,7 @@ const styles = StyleSheet.create({
|
|
|
3899
4285
|
|
|
3900
4286
|
The `transform` property on the web should be a string:
|
|
3901
4287
|
|
|
3902
|
-
```
|
|
4288
|
+
```diff
|
|
3903
4289
|
const styles = StyleSheet.create({
|
|
3904
4290
|
container: {
|
|
3905
4291
|
flex: 1,
|
|
@@ -3914,7 +4300,7 @@ const styles = StyleSheet.create({
|
|
|
3914
4300
|
|
|
3915
4301
|
If you want to use React Native specific styles on web simply move them to the `style` level:
|
|
3916
4302
|
|
|
3917
|
-
```
|
|
4303
|
+
```diff
|
|
3918
4304
|
const styles = StyleSheet.create({
|
|
3919
4305
|
container: {
|
|
3920
4306
|
flex: 1,
|
|
@@ -3932,6 +4318,8 @@ You can also use variants, breakpoints, and other Unistyles features under the `
|
|
|
3932
4318
|
|
|
3933
4319
|
### Pseudo elements
|
|
3934
4320
|
|
|
4321
|
+
[Section titled “Pseudo elements”](#pseudo-elements)
|
|
4322
|
+
|
|
3935
4323
|
Unistyles also introduces a way to use **any** pseudo-elements and selectors in your web styles.
|
|
3936
4324
|
|
|
3937
4325
|
```ts
|
|
@@ -3954,6 +4342,8 @@ As you can see, `:` and `::` have been replaced with `_` for easier usage.
|
|
|
3954
4342
|
|
|
3955
4343
|
### Injecting custom classNames
|
|
3956
4344
|
|
|
4345
|
+
[Section titled “Injecting custom classNames”](#injecting-custom-classnames)
|
|
4346
|
+
|
|
3957
4347
|
If you want to write some part of your app with plain CSS, you can add custom `classNames` to your styles:
|
|
3958
4348
|
|
|
3959
4349
|
```ts
|
|
@@ -3969,7 +4359,7 @@ const styles = StyleSheet.create({
|
|
|
3969
4359
|
|
|
3970
4360
|
The `_classNames` key under the `_web` key will be injected into the DOM element as a `className`. You can pass a string or an array of strings into it:
|
|
3971
4361
|
|
|
3972
|
-
```
|
|
4362
|
+
```diff
|
|
3973
4363
|
const styles = StyleSheet.create({
|
|
3974
4364
|
container: {
|
|
3975
4365
|
flex: 1,
|
|
@@ -3996,6 +4386,8 @@ const styles = StyleSheet.create({
|
|
|
3996
4386
|
|
|
3997
4387
|
### CSS Variables
|
|
3998
4388
|
|
|
4389
|
+
[Section titled “CSS Variables”](#css-variables)
|
|
4390
|
+
|
|
3999
4391
|
Unistyles 3.0 converts all your themes to CSS variables by default, eliminating heavy JS processing when changing the theme and allowing the CSS engine to take over.
|
|
4000
4392
|
|
|
4001
4393
|
In more detail, it converts all **strings** into CSS variables. For example, if we have the following theme:
|
|
@@ -4022,10 +4414,14 @@ After conversion, Unistyles will use CSS variable instead of string to reference
|
|
|
4022
4414
|
|
|
4023
4415
|
##### If you’re using `adaptiveThemes`
|
|
4024
4416
|
|
|
4417
|
+
[Section titled “If you’re using adaptiveThemes”](#if-youre-using-adaptivethemes)
|
|
4418
|
+
|
|
4025
4419
|
CSS variables will be placed under the `@media (prefers-color-scheme)` [query](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) ensuring that the app will automatically switch to the new theme.
|
|
4026
4420
|
|
|
4027
4421
|
##### If you’re not using `adaptiveThemes`
|
|
4028
4422
|
|
|
4423
|
+
[Section titled “If you’re not using adaptiveThemes”](#if-youre-not-using-adaptivethemes)
|
|
4424
|
+
|
|
4029
4425
|
Class of your html root element will be updated to match the new one.
|
|
4030
4426
|
|
|
4031
4427
|
Caution
|
|
@@ -4034,8 +4430,12 @@ It’s possible to [disable](/v3/start/configuration#settings-optional) this fea
|
|
|
4034
4430
|
|
|
4035
4431
|
### When to disable CSS variables?
|
|
4036
4432
|
|
|
4433
|
+
[Section titled “When to disable CSS variables?”](#when-to-disable-css-variables)
|
|
4434
|
+
|
|
4037
4435
|
##### When you have different size variables / functions in your themes
|
|
4038
4436
|
|
|
4437
|
+
[Section titled “When you have different size variables / functions in your themes”](#when-you-have-different-size-variables--functions-in-your-themes)
|
|
4438
|
+
|
|
4039
4439
|
```tsx
|
|
4040
4440
|
// ❌ Not OK
|
|
4041
4441
|
const regularTheme = {
|
|
@@ -4080,6 +4480,8 @@ const darkTheme = {
|
|
|
4080
4480
|
|
|
4081
4481
|
##### When you use conditions to style your components instead of relying on the same theme values
|
|
4082
4482
|
|
|
4483
|
+
[Section titled “When you use conditions to style your components instead of relying on the same theme values”](#when-you-use-conditions-to-style-your-components-instead-of-relying-on-the-same-theme-values)
|
|
4484
|
+
|
|
4083
4485
|
```tsx
|
|
4084
4486
|
// ❌ Not OK
|
|
4085
4487
|
const styles = StyleSheet.create(theme => ({
|
|
@@ -4112,6 +4514,8 @@ Unistyles Web is independent from React Native Web, utilizing a custom web parse
|
|
|
4112
4514
|
|
|
4113
4515
|
### How It Works
|
|
4114
4516
|
|
|
4517
|
+
[Section titled “How It Works”](#how-it-works)
|
|
4518
|
+
|
|
4115
4519
|
Unistyles web parser generates unique `classNames` for your styles and assigns them to corresponding DOM elements. This ensures that only the necessary styles are applied, avoiding redundancy. Additionally, media queries are automatically created based on your `breakpoints`, eliminating the need for recalculation on every resize.
|
|
4116
4520
|
|
|
4117
4521
|
Example:
|
|
@@ -4161,6 +4565,8 @@ Will produce the following CSS output:
|
|
|
4161
4565
|
|
|
4162
4566
|
### Updating Styles
|
|
4163
4567
|
|
|
4568
|
+
[Section titled “Updating Styles”](#updating-styles)
|
|
4569
|
+
|
|
4164
4570
|
When you change your app’s theme, Unistyles automatically updates your CSS without triggering any re-renders. This applies to dynamic functions and variants as well.
|
|
4165
4571
|
|
|
4166
4572
|
For instance, if you define your styles dynamically:
|
|
@@ -4200,6 +4606,8 @@ The CSS will automatically update to:
|
|
|
4200
4606
|
|
|
4201
4607
|
### Limitations
|
|
4202
4608
|
|
|
4609
|
+
[Section titled “Limitations”](#limitations)
|
|
4610
|
+
|
|
4203
4611
|
Due to Unistyles custom parser, styles cannot be accessed directly as they would be with React Native Web. Passing styles to the `RNW` parser would modify them and generate unnecessary new classes.
|
|
4204
4612
|
|
|
4205
4613
|
As a result, when you try to `console.log` the styles, there will be no output:
|
|
@@ -4218,6 +4626,8 @@ console.log(styles) // {}
|
|
|
4218
4626
|
|
|
4219
4627
|
### Web-Only Features
|
|
4220
4628
|
|
|
4629
|
+
[Section titled “Web-Only Features”](#web-only-features)
|
|
4630
|
+
|
|
4221
4631
|
Unistyles includes some features specific to the web. Learn more about them [here](/v3/references/web-only).
|
|
4222
4632
|
|
|
4223
4633
|
# withUnistyles
|
|
@@ -4228,6 +4638,8 @@ Before reading this guide, make sure that you understand [How Unistyles works](/
|
|
|
4228
4638
|
|
|
4229
4639
|
### Why do you need it?
|
|
4230
4640
|
|
|
4641
|
+
[Section titled “Why do you need it?”](#why-do-you-need-it)
|
|
4642
|
+
|
|
4231
4643
|
* Unistyles cannot retrieve `ShadowNode` from third-party components because they might not expose a native view via the ref prop
|
|
4232
4644
|
|
|
4233
4645
|
```ts
|
|
@@ -4284,6 +4696,8 @@ Caution
|
|
|
4284
4696
|
|
|
4285
4697
|
### Auto mapping for `style` and `contentContainerStyle` props
|
|
4286
4698
|
|
|
4699
|
+
[Section titled “Auto mapping for style and contentContainerStyle props”](#auto-mapping-for-style-and-contentcontainerstyle-props)
|
|
4700
|
+
|
|
4287
4701
|
If your component expects the `style` or `contentContainerStyle` prop, Unistyles will automatically handle the mapping under the hood. You just need to wrap your custom view in `withUnistyles`. We will also respect your style dependencies, so, for example, the `Blurhash` component will only re-render when the theme changes.
|
|
4288
4702
|
|
|
4289
4703
|
```ts
|
|
@@ -4317,6 +4731,8 @@ const styles = StyleSheet.create(theme => ({
|
|
|
4317
4731
|
|
|
4318
4732
|
### Mapping custom props to Unistyles styles
|
|
4319
4733
|
|
|
4734
|
+
[Section titled “Mapping custom props to Unistyles styles”](#mapping-custom-props-to-unistyles-styles)
|
|
4735
|
+
|
|
4320
4736
|
If you need to ensure your component updates but it doesn’t use `style` or `contentContainerStyle` props, you can use `mappings`:
|
|
4321
4737
|
|
|
4322
4738
|
```ts
|
|
@@ -4344,6 +4760,8 @@ TypeScript will autocomplete all your props, so there is no need to specify type
|
|
|
4344
4760
|
|
|
4345
4761
|
### Custom mappings for external props
|
|
4346
4762
|
|
|
4763
|
+
[Section titled “Custom mappings for external props”](#custom-mappings-for-external-props)
|
|
4764
|
+
|
|
4347
4765
|
Sometimes, you might want to map your props based on a function or value that is only accessible within the component. For example, if you are using `FlashList` and want to modify the `numColumns` prop based on a condition. Using `mappings` in `withUnistyles` is not an option because it doesn’t allow referencing other props.
|
|
4348
4766
|
|
|
4349
4767
|
```tsx
|
|
@@ -4419,6 +4837,8 @@ Components that use `uniProps` are also aware of your dependencies. In the examp
|
|
|
4419
4837
|
|
|
4420
4838
|
### Props resolution priority
|
|
4421
4839
|
|
|
4840
|
+
[Section titled “Props resolution priority”](#props-resolution-priority)
|
|
4841
|
+
|
|
4422
4842
|
We will respect your order of prop resolution, applying them with the following priority:
|
|
4423
4843
|
|
|
4424
4844
|
1. Global mappings
|
|
@@ -4475,6 +4895,8 @@ Let’s discuss the responsibilities of the Babel plugin:
|
|
|
4475
4895
|
|
|
4476
4896
|
### 1. Detecting StyleSheet dependencies
|
|
4477
4897
|
|
|
4898
|
+
[Section titled “1. Detecting StyleSheet dependencies”](#1-detecting-stylesheet-dependencies)
|
|
4899
|
+
|
|
4478
4900
|
Each `StyleSheet` is different. One might rely on a `theme`, another on `miniRuntime`, and so on. The same applies to `styles`. Each style depends on different things. For example, you can wrap your app in a `View` that safeguards your app from rendering behind the notch or navigation bar. Another style might be used in your `Typography` component and provides text color based on the apps’ theme.
|
|
4479
4901
|
|
|
4480
4902
|
Should the `Typography` style re-calculate on an `insets` change? Or should the `View` that relies on insets re-render on a theme change?
|
|
@@ -4534,10 +4956,14 @@ We put a lot of effort into making dependency detection as accurate as possible,
|
|
|
4534
4956
|
|
|
4535
4957
|
### 2. Attaching unique id to each StyleSheet
|
|
4536
4958
|
|
|
4959
|
+
[Section titled “2. Attaching unique id to each StyleSheet”](#2-attaching-unique-id-to-each-stylesheet)
|
|
4960
|
+
|
|
4537
4961
|
This helps us identify your `StyleSheet` while you’re developing your app and trigger multiple `hot-reloads`. Such identification is required to swap your `StyleSheet` with another one, ensuring that you get up-to-date values during reloads. This feature does not affect your app in production, as the bundle never reloads in that environment.
|
|
4538
4962
|
|
|
4539
4963
|
### 3. Component factory (borrowing ref)
|
|
4540
4964
|
|
|
4965
|
+
[Section titled “3. Component factory (borrowing ref)”](#3-component-factory-borrowing-ref)
|
|
4966
|
+
|
|
4541
4967
|
This is the most crucial part—without it, Unistyles won’t be able to update your views from C++. In the early versions of Unistyles 3.0, we tried solving this problem by using the `ref` prop, but it wasn’t reliable enough. Many developers use different style syntaxes, making it impossible to support all of them.
|
|
4542
4968
|
|
|
4543
4969
|
Instead, we decided to leave the user’s `ref` as is and transfer the implementation from Babel to our component factory. This way we have more control and we have an unified way of registering your `ShadowNodes`.
|
|
@@ -4617,6 +5043,8 @@ import { Image } from 'react-native-unistyles/components/native/Image'
|
|
|
4617
5043
|
|
|
4618
5044
|
### 4. Creating scopes for stateless variants
|
|
4619
5045
|
|
|
5046
|
+
[Section titled “4. Creating scopes for stateless variants”](#4-creating-scopes-for-stateless-variants)
|
|
5047
|
+
|
|
4620
5048
|
When you use variants, each time you call `useVariants`, a new scope is created. This scope contains a local copy of stylesheet that won’t affect other components. This feature is similar to time travel, allowing you to explore different states of your styles with different calls to `useVariants`.
|
|
4621
5049
|
|
|
4622
5050
|
From your perspective, using variants is simple: you just need to call the `useVariants` hook:
|
|
@@ -4670,6 +5098,8 @@ By leveraging such scopes, we ensure support for any level of nesting!
|
|
|
4670
5098
|
|
|
4671
5099
|
### Extra configuration
|
|
4672
5100
|
|
|
5101
|
+
[Section titled “Extra configuration”](#extra-configuration)
|
|
5102
|
+
|
|
4673
5103
|
The Babel plugin comes with a few additional options to extend its usage.
|
|
4674
5104
|
|
|
4675
5105
|
Caution
|
|
@@ -4678,6 +5108,8 @@ By default babel plugin will look for any `react-native-unistyles` import to sta
|
|
|
4678
5108
|
|
|
4679
5109
|
### `root` (required)
|
|
4680
5110
|
|
|
5111
|
+
[Section titled “root (required)”](#root-required)
|
|
5112
|
+
|
|
4681
5113
|
All files within the specified root folder will be processed by the Babel plugin. If you need to process extra folders, use with `autoProcessPaths` option.
|
|
4682
5114
|
|
|
4683
5115
|
```js
|
|
@@ -4699,6 +5131,8 @@ In that case:
|
|
|
4699
5131
|
|
|
4700
5132
|
### `autoProcessImports`
|
|
4701
5133
|
|
|
5134
|
+
[Section titled “autoProcessImports”](#autoprocessimports)
|
|
5135
|
+
|
|
4702
5136
|
This configuration should be used when you want to process files containing specific imports. It can be useful for monorepos that use Unistyles with absolute paths, such as `@codemask/styles`.
|
|
4703
5137
|
|
|
4704
5138
|
```js
|
|
@@ -4709,6 +5143,8 @@ This configuration should be used when you want to process files containing spec
|
|
|
4709
5143
|
|
|
4710
5144
|
### `autoRemapImports`
|
|
4711
5145
|
|
|
5146
|
+
[Section titled “autoRemapImports”](#autoremapimports)
|
|
5147
|
+
|
|
4712
5148
|
This is the most powerful option, but most likely, you won’t need to use it. It allows you to remap uncommon imports to Unistyles components.
|
|
4713
5149
|
|
|
4714
5150
|
This may happen if a 3rd library does not import `react-native` components directly, but instead uses its own factory or a relative path. Unistyles uses it internally to support the following imports from `react-native` internals:
|
|
@@ -4756,6 +5192,8 @@ If you use raw `react-native` imports within your code, Unistyles will auto map
|
|
|
4756
5192
|
|
|
4757
5193
|
### `autoProcessPaths`
|
|
4758
5194
|
|
|
5195
|
+
[Section titled “autoProcessPaths”](#autoprocesspaths)
|
|
5196
|
+
|
|
4759
5197
|
This configuration is unrelated to the `root`, `autoProcessImports`, and `autoRemapImports` options and can be used alongside them. By default, the Babel plugin ignores `node_modules`. However, you can extend these paths to attempt converting 3rd components into Unistyles compatible ones. Within these paths, we will replace `react-native` imports with `react-native-unistyles` factories that borrow component refs. [Read more](/v3/other/babel-plugin#3-component-factory-borrowing-ref).
|
|
4760
5198
|
|
|
4761
5199
|
Defaults to:
|
|
@@ -4766,14 +5204,20 @@ Defaults to:
|
|
|
4766
5204
|
|
|
4767
5205
|
### `debug`
|
|
4768
5206
|
|
|
5207
|
+
[Section titled “debug”](#debug)
|
|
5208
|
+
|
|
4769
5209
|
In order to list detected dependencies by the Babel plugin you can enable the `debug` flag. It will `console.log` name of the file and component with Unistyles dependencies.
|
|
4770
5210
|
|
|
4771
5211
|
### Usage with React Compiler
|
|
4772
5212
|
|
|
5213
|
+
[Section titled “Usage with React Compiler”](#usage-with-react-compiler)
|
|
5214
|
+
|
|
4773
5215
|
Check [this guide](/v3/guides/react-compiler) for more details.
|
|
4774
5216
|
|
|
4775
5217
|
#### Usage in `babel.config.js`
|
|
4776
5218
|
|
|
5219
|
+
[Section titled “Usage in babel.config.js”](#usage-in-babelconfigjs)
|
|
5220
|
+
|
|
4777
5221
|
You can apply any of the options above as follows:
|
|
4778
5222
|
|
|
4779
5223
|
babel.config.js
|
|
@@ -4810,10 +5254,12 @@ module.exports = function (api) {
|
|
|
4810
5254
|
|
|
4811
5255
|
> Learn about Unistyles dependencies
|
|
4812
5256
|
|
|
4813
|
-
Unistyles 3.0 minimizes dependencies to keep your app as lightweight as possible. In the latest version, we’ve opted to include only
|
|
5257
|
+
Unistyles 3.0 minimizes dependencies to keep your app as lightweight as possible. In the latest version, we’ve opted to include only the essential dependencies that are shaping the future of the React Native ecosystem.
|
|
4814
5258
|
|
|
4815
5259
|
### Nitro Modules
|
|
4816
5260
|
|
|
5261
|
+
[Section titled “Nitro Modules”](#nitro-modules)
|
|
5262
|
+
|
|
4817
5263
|
Developed by: [Marc Rousavy](https://github.com/mrousavy)
|
|
4818
5264
|
|
|
4819
5265
|
[Nitro modules](https://nitro.margelo.com/) help Unistyles speed up development time by offering remarkable solutions:
|
|
@@ -4826,15 +5272,29 @@ Developed by: [Marc Rousavy](https://github.com/mrousavy)
|
|
|
4826
5272
|
|
|
4827
5273
|
We highly encourage you to give Nitro a star ⭐ or support Marc through sponsorship.
|
|
4828
5274
|
|
|
4829
|
-
### React Native Edge to Edge
|
|
5275
|
+
### React Native Edge to Edge (optional since v3.1.0)
|
|
5276
|
+
|
|
5277
|
+
[Section titled “React Native Edge to Edge (optional since v3.1.0)”](#react-native-edge-to-edge-optional-since-v310)
|
|
4830
5278
|
|
|
4831
5279
|
Developed by: [Mathieu Acthernoene](https://github.com/zoontek)
|
|
4832
5280
|
|
|
4833
|
-
[React Native Edge to Edge](https://github.com/zoontek/react-native-edge-to-edge) is a library aimed at unifying the handling of edge-to-edge layouts on Android.
|
|
5281
|
+
[React Native Edge to Edge](https://github.com/zoontek/react-native-edge-to-edge) is a library aimed at unifying the handling of edge-to-edge layouts on Android.
|
|
5282
|
+
|
|
5283
|
+
Since v3.1.0, `react-native-edge-to-edge` is **optional** as a direct dependency. Unistyles enables edge-to-edge layout automatically on Android if `react-native-edge-to-edge` is not installed.
|
|
4834
5284
|
|
|
4835
|
-
|
|
5285
|
+
However, we **strongly recommend** enabling `edgeToEdgeEnabled` in your `gradle.properties`:
|
|
4836
5286
|
|
|
4837
|
-
|
|
5287
|
+
android/gradle.properties
|
|
5288
|
+
|
|
5289
|
+
```properties
|
|
5290
|
+
edgeToEdgeEnabled=true
|
|
5291
|
+
```
|
|
5292
|
+
|
|
5293
|
+
This property does more than just enabling edge-to-edge layout — it enforces `statusBarTranslucent` / `navigationBarTranslucent` on modals, disables `backgroundColor` / `translucent` on `StatusBar`, and enables additional fixes in React Native core. **Expo SDK 54+** enables this automatically.
|
|
5294
|
+
|
|
5295
|
+
You can still install `react-native-edge-to-edge` alongside this property if you use libraries that detect it for ecosystem-wide edge-to-edge detection (e.g. `react-native-bootsplash`, `react-native-permissions`).
|
|
5296
|
+
|
|
5297
|
+
If you use any of Mathieu’s libraries, we encourage you to give them a star ⭐ and support him through sponsorship.
|
|
4838
5298
|
|
|
4839
5299
|
# For library authors
|
|
4840
5300
|
|
|
@@ -4844,6 +5304,8 @@ Unistyles is highly extensible and can be used to build UI kits and various othe
|
|
|
4844
5304
|
|
|
4845
5305
|
## Using Unistyles in your library
|
|
4846
5306
|
|
|
5307
|
+
[Section titled “Using Unistyles in your library”](#using-unistyles-in-your-library)
|
|
5308
|
+
|
|
4847
5309
|
`StyleSheet.configure` **must** be invoked as soon as possible, before any user code references any `StyleSheet` from your library.
|
|
4848
5310
|
|
|
4849
5311
|
You can then call `StyleSheet.configure` multiple times to override configurations. However, keep in mind that `StyleSheet.configure` makes a roundtrip to C++, which can add a few `ms` to your app’s startup time.
|
|
@@ -4852,14 +5314,20 @@ To manipulate your config without replacing it, use [UnistylesRuntime](/v3/refer
|
|
|
4852
5314
|
|
|
4853
5315
|
## Unistyles never re-renders
|
|
4854
5316
|
|
|
5317
|
+
[Section titled “Unistyles never re-renders”](#unistyles-never-re-renders)
|
|
5318
|
+
|
|
4855
5319
|
Unistyles’ C++ core ensures that your components never re-render. Instead, they are updated directly from C++ and `Shadow Tree`.
|
|
4856
5320
|
|
|
4857
5321
|
## No React Context - no additional setup
|
|
4858
5322
|
|
|
5323
|
+
[Section titled “No React Context - no additional setup”](#no-react-context---no-additional-setup)
|
|
5324
|
+
|
|
4859
5325
|
Unistyles does not use the React Context API. This means that users do not need to wrap their app with a `Provider`, reducing boilerplate code and making your library more user-friendly.
|
|
4860
5326
|
|
|
4861
5327
|
## New architecture only
|
|
4862
5328
|
|
|
5329
|
+
[Section titled “New architecture only”](#new-architecture-only)
|
|
5330
|
+
|
|
4863
5331
|
Unistyles won’t re-render your components unless you want to. While it requires enabling the New Architecture, we believe this trade-off is worthwhile, as more apps are expected to transition to the New Architecture in the coming months.
|
|
4864
5332
|
|
|
4865
5333
|
Note
|
|
@@ -4868,6 +5336,8 @@ As of June 2nd, 2025, Old Architecture is [frozen](https://github.com/reactwg/re
|
|
|
4868
5336
|
|
|
4869
5337
|
## Minimum requirements
|
|
4870
5338
|
|
|
5339
|
+
[Section titled “Minimum requirements”](#minimum-requirements)
|
|
5340
|
+
|
|
4871
5341
|
Unistyles is compatible with:
|
|
4872
5342
|
|
|
4873
5343
|
* React Native version >= 0.78
|
|
@@ -4877,16 +5347,22 @@ Unistyles is compatible with:
|
|
|
4877
5347
|
|
|
4878
5348
|
## Out of the box support for Web
|
|
4879
5349
|
|
|
5350
|
+
[Section titled “Out of the box support for Web”](#out-of-the-box-support-for-web)
|
|
5351
|
+
|
|
4880
5352
|
Building a UI kit for both React Native and Web couldn’t be easier. Unistyles automatically manages your styles and converts them into CSS classes.
|
|
4881
5353
|
|
|
4882
5354
|
## Babel config
|
|
4883
5355
|
|
|
5356
|
+
[Section titled “Babel config”](#babel-config)
|
|
5357
|
+
|
|
4884
5358
|
Make sure to instruct your users to add [autoProcessPaths](/v3/other/babel-plugin#extra-configuration) babel option. It will whitelist your `ui-kit` and process your files even though there are in `node_modules` folder.
|
|
4885
5359
|
|
|
4886
5360
|
You can also consider publishing your UI kit with babel transforms in place. Keep in mind that it could break [testing](/v3/start/testing) views with your components.
|
|
4887
5361
|
|
|
4888
5362
|
## Why to choose Unistyles?
|
|
4889
5363
|
|
|
5364
|
+
[Section titled “Why to choose Unistyles?”](#why-to-choose-unistyles)
|
|
5365
|
+
|
|
4890
5366
|
Unistyles offers a unique architecture unavailable in any other library. Fully compatible with the React Native StyleSheet API, it is easy to use and extend.
|
|
4891
5367
|
|
|
4892
5368
|
By avoiding component abstraction, Unistyles gives you the freedom to create your own. It supports various platforms and is designed to be easily extendable.
|
|
@@ -4909,22 +5385,30 @@ We’re so exited about Unistyles 3.0 core and can’t wait for the new possibil
|
|
|
4909
5385
|
|
|
4910
5386
|
### Why sponsor Unistyles?
|
|
4911
5387
|
|
|
5388
|
+
[Section titled “Why sponsor Unistyles?”](#why-sponsor-unistyles)
|
|
5389
|
+
|
|
4912
5390
|
* **Advancing Innovation**: Your sponsorship helps in the continuous innovation and improvement of Unistyles. This support is crucial for developing new features and maintaining the library
|
|
4913
5391
|
* **Benefit for Developers and Companies**: Both individual developers and large companies that profit from using Unistyles stand to gain from its enhancements. Your support ensures that Unistyles remains a cutting-edge tool in your development arsenal
|
|
4914
5392
|
* **Limited Free Time Challenge**: The development of innovative libraries like Unistyles is often constrained by the limited free time of creators. Sponsorship can provide the necessary resources for dedicated development time
|
|
4915
5393
|
|
|
4916
5394
|
### How to sponsor?
|
|
4917
5395
|
|
|
5396
|
+
[Section titled “How to sponsor?”](#how-to-sponsor)
|
|
5397
|
+
|
|
4918
5398
|
* **Github Sponsorship**: [link](https://github.com/sponsors/jpudysz)
|
|
4919
5399
|
* **Ko-Fi**: [link](https://ko-fi.com/jpudysz)
|
|
4920
5400
|
|
|
4921
5401
|
### Free options
|
|
4922
5402
|
|
|
5403
|
+
[Section titled “Free options”](#free-options)
|
|
5404
|
+
|
|
4923
5405
|
* **Sharing Unistyles**: A free yet impactful way to support us is by sharing information about Unistyles within your network. Spreading the word helps increase our visibility and user base
|
|
4924
5406
|
* **Shoutout**: Give us a shoutout on X or Reddit. Public endorsements and mentions can significantly boost our project’s presence and reach
|
|
4925
5407
|
|
|
4926
5408
|
### Other options
|
|
4927
5409
|
|
|
5410
|
+
[Section titled “Other options”](#other-options)
|
|
5411
|
+
|
|
4928
5412
|
Hire Codemask team
|
|
4929
5413
|
|
|
4930
5414
|
If you’re looking to hire a skilled React Native team, Codemask is open for collaboration. We offer expertise and quality in building React Native applications.
|
|
@@ -4943,22 +5427,32 @@ As a Codemask CTO I’m open to share my knowledge and expertise in the React Na
|
|
|
4943
5427
|
|
|
4944
5428
|
### Can I run Unistyles on Expo Go?
|
|
4945
5429
|
|
|
5430
|
+
[Section titled “Can I run Unistyles on Expo Go?”](#can-i-run-unistyles-on-expo-go)
|
|
5431
|
+
|
|
4946
5432
|
No, Unistyles includes custom native code, which means it does not support Expo Go.
|
|
4947
5433
|
|
|
4948
5434
|
### What happened to `macOS`, `windows`, `visionOS`, `tvOS` support?
|
|
4949
5435
|
|
|
5436
|
+
[Section titled “What happened to macOS, windows, visionOS, tvOS support?”](#what-happened-to-macos-windows-visionos-tvos-support)
|
|
5437
|
+
|
|
4950
5438
|
For now they’re not available. We’re seeking sponsors to help us add support, as they are rarely used by our customers.
|
|
4951
5439
|
|
|
4952
5440
|
### Can I run Unistyles on `Old Architecture`?
|
|
4953
5441
|
|
|
5442
|
+
[Section titled “Can I run Unistyles on Old Architecture?”](#can-i-run-unistyles-on-old-architecture)
|
|
5443
|
+
|
|
4954
5444
|
No, Unistyles is tightly integrated with `Fabric`. There are no plans to support `Old Architecture`.
|
|
4955
5445
|
|
|
4956
5446
|
### We are not ready to upgrade. What will happen with version `2.0`?
|
|
4957
5447
|
|
|
5448
|
+
[Section titled “We are not ready to upgrade. What will happen with version 2.0?”](#we-are-not-ready-to-upgrade-what-will-happen-with-version-20)
|
|
5449
|
+
|
|
4958
5450
|
We understand that some apps require more time to migrate to the `New Architecture`. We plan to support Unistyles 2.0 for a few more months or stable React Native versions.
|
|
4959
5451
|
|
|
4960
5452
|
### Adaptive mode doesn’t work for me
|
|
4961
5453
|
|
|
5454
|
+
[Section titled “Adaptive mode doesn’t work for me”](#adaptive-mode-doesnt-work-for-me)
|
|
5455
|
+
|
|
4962
5456
|
To enable adaptive mode, you need to register two themes named `light` and `dark` and set the `adaptiveThemes` flag to true within `StyleSheet.configure`.
|
|
4963
5457
|
|
|
4964
5458
|
If your app still doesn’t automatically switch themes, ensure that:
|
|
@@ -4971,6 +5465,8 @@ If your app still doesn’t automatically switch themes, ensure that:
|
|
|
4971
5465
|
|
|
4972
5466
|
### ld.lld: error: Undefined symbols margelo::nitro::\*
|
|
4973
5467
|
|
|
5468
|
+
[Section titled “ld.lld: error: Undefined symbols margelo::nitro::\*”](#ldlld-error-undefined-symbols-margelonitro)
|
|
5469
|
+
|
|
4974
5470
|
This error occurs due to the strong caching mechanism in Android Studio. The cache can even survive the `expo prebuild --clean` command in Expo projects.
|
|
4975
5471
|
|
|
4976
5472
|
To clean the cache, please follow these steps:
|
|
@@ -5010,24 +5506,26 @@ After cleaning up, your components folder should look like this:
|
|
|
5010
5506
|
|
|
5011
5507
|
### ThemedText
|
|
5012
5508
|
|
|
5509
|
+
[Section titled “ThemedText”](#themedtext)
|
|
5510
|
+
|
|
5013
5511
|
The default `ThemedText` component is a perfect candidate for a Unistyles refactor. It contains conditional style logic directly in the JSX - a pattern we can significantly improve.
|
|
5014
5512
|
|
|
5015
5513
|
First, let’s swap the `StyleSheet` import and remove unnecessary `useThemeColor` hook.
|
|
5016
5514
|
|
|
5017
5515
|
components/ThemedText.tsx
|
|
5018
5516
|
|
|
5019
|
-
```
|
|
5020
|
-
import { StyleSheet, Text, type TextProps } from 'react-native';
|
|
5021
|
-
import { Text, type TextProps } from 'react-native';
|
|
5022
|
-
import { StyleSheet } from 'react-native-unistyles';
|
|
5023
|
-
import { useThemeColor } from '@/hooks/useThemeColor';
|
|
5517
|
+
```diff
|
|
5518
|
+
-import { StyleSheet, Text, type TextProps } from 'react-native';
|
|
5519
|
+
+import { Text, type TextProps } from 'react-native';
|
|
5520
|
+
+import { StyleSheet } from 'react-native-unistyles';
|
|
5521
|
+
-import { useThemeColor } from '@/hooks/useThemeColor';
|
|
5024
5522
|
```
|
|
5025
5523
|
|
|
5026
5524
|
The original component used the `useThemeColor` hook to get a color based on the current theme. We’ll replace this imperative logic with a dynamic function in our stylesheet. A dynamic function is a Unistyles feature that allows a style to accept arguments. Let’s create one called `textColor` to handle the `lightColor` and `darkColor` props.
|
|
5027
5525
|
|
|
5028
5526
|
components/ThemedText.tsx
|
|
5029
5527
|
|
|
5030
|
-
```
|
|
5528
|
+
```diff
|
|
5031
5529
|
export function ThemedText({
|
|
5032
5530
|
style,
|
|
5033
5531
|
lightColor,
|
|
@@ -5035,14 +5533,14 @@ export function ThemedText({
|
|
|
5035
5533
|
type = 'default',
|
|
5036
5534
|
...rest
|
|
5037
5535
|
}: ThemedTextProps) {
|
|
5038
|
-
const color = useThemeColor({ light: lightColor, dark: darkColor });
|
|
5536
|
+
-const color = useThemeColor({ light: lightColor, dark: darkColor });
|
|
5039
5537
|
|
|
5040
5538
|
|
|
5041
5539
|
return (
|
|
5042
5540
|
<Text
|
|
5043
5541
|
style={[
|
|
5044
|
-
{ color },
|
|
5045
|
-
styles.textColor(lightColor, darkColor),
|
|
5542
|
+
- { color },
|
|
5543
|
+
+ styles.textColor(lightColor, darkColor),
|
|
5046
5544
|
type === 'default' ? styles.default : undefined,
|
|
5047
5545
|
type === 'title' ? styles.title : undefined,
|
|
5048
5546
|
type === 'defaultSemiBold' ? styles.defaultSemiBold : undefined,
|
|
@@ -5061,9 +5559,9 @@ const styles = StyleSheet.create({
|
|
|
5061
5559
|
fontSize: 16,
|
|
5062
5560
|
lineHeight: 24,
|
|
5063
5561
|
},
|
|
5064
|
-
textColor: (lightColor?: string, darkColor?: string) => ({
|
|
5065
|
-
|
|
5066
|
-
}),
|
|
5562
|
+
+textColor: (lightColor?: string, darkColor?: string) => ({
|
|
5563
|
+
+// todo
|
|
5564
|
+
+}),
|
|
5067
5565
|
defaultSemiBold: {
|
|
5068
5566
|
fontSize: 16,
|
|
5069
5567
|
lineHeight: 24,
|
|
@@ -5104,7 +5602,7 @@ Let’s complete our dynamic function:
|
|
|
5104
5602
|
|
|
5105
5603
|
components/ThemedText.tsx
|
|
5106
5604
|
|
|
5107
|
-
```
|
|
5605
|
+
```diff
|
|
5108
5606
|
export function ThemedText({
|
|
5109
5607
|
style,
|
|
5110
5608
|
lightColor,
|
|
@@ -5129,14 +5627,14 @@ export function ThemedText({
|
|
|
5129
5627
|
}
|
|
5130
5628
|
|
|
5131
5629
|
|
|
5132
|
-
const styles = StyleSheet.create({
|
|
5630
|
+
-const styles = StyleSheet.create({
|
|
5133
5631
|
const styles = StyleSheet.create((theme, rt) => ({
|
|
5134
5632
|
default: {
|
|
5135
5633
|
fontSize: 16,
|
|
5136
5634
|
lineHeight: 24,
|
|
5137
5635
|
},
|
|
5138
5636
|
textColor: (lightColor: string, darkColor: string) => ({
|
|
5139
|
-
|
|
5637
|
+
-// todo
|
|
5140
5638
|
color: rt.colorScheme === 'dark' ? darkColor : lightColor,
|
|
5141
5639
|
}),
|
|
5142
5640
|
defaultSemiBold: {
|
|
@@ -5158,7 +5656,7 @@ export function ThemedText({
|
|
|
5158
5656
|
fontSize: 16,
|
|
5159
5657
|
color: '#0a7ea4',
|
|
5160
5658
|
},
|
|
5161
|
-
})
|
|
5659
|
+
-})
|
|
5162
5660
|
}));
|
|
5163
5661
|
```
|
|
5164
5662
|
|
|
@@ -5166,7 +5664,7 @@ Next, let’s tackle the chain of conditional checks for the type prop. This is
|
|
|
5166
5664
|
|
|
5167
5665
|
components/ThemedText.tsx
|
|
5168
5666
|
|
|
5169
|
-
```
|
|
5667
|
+
```diff
|
|
5170
5668
|
export function ThemedText({
|
|
5171
5669
|
style,
|
|
5172
5670
|
lightColor,
|
|
@@ -5178,11 +5676,11 @@ export function ThemedText({
|
|
|
5178
5676
|
<Text
|
|
5179
5677
|
style={[
|
|
5180
5678
|
styles.textColor(lightColor, darkColor),
|
|
5181
|
-
type === 'default' ? styles.default : undefined,
|
|
5182
|
-
type === 'title' ? styles.title : undefined,
|
|
5183
|
-
type === 'defaultSemiBold' ? styles.defaultSemiBold : undefined,
|
|
5184
|
-
type === 'subtitle' ? styles.subtitle : undefined,
|
|
5185
|
-
type === 'link' ? styles.link : undefined,
|
|
5679
|
+
- type === 'default' ? styles.default : undefined,
|
|
5680
|
+
- type === 'title' ? styles.title : undefined,
|
|
5681
|
+
- type === 'defaultSemiBold' ? styles.defaultSemiBold : undefined,
|
|
5682
|
+
- type === 'subtitle' ? styles.subtitle : undefined,
|
|
5683
|
+
- type === 'link' ? styles.link : undefined,
|
|
5186
5684
|
style,
|
|
5187
5685
|
]}
|
|
5188
5686
|
{...rest}
|
|
@@ -5195,7 +5693,7 @@ const styles = StyleSheet.create((theme, rt) => ({
|
|
|
5195
5693
|
default: {
|
|
5196
5694
|
fontSize: 16,
|
|
5197
5695
|
lineHeight: 24,
|
|
5198
|
-
},
|
|
5696
|
+
-},
|
|
5199
5697
|
textColor: (lightColor?: string, darkColor?: string) => ({
|
|
5200
5698
|
color: rt.colorScheme === 'dark' ? darkColor : lightColor,
|
|
5201
5699
|
}),
|
|
@@ -5203,27 +5701,27 @@ const styles = StyleSheet.create((theme, rt) => ({
|
|
|
5203
5701
|
fontSize: 16,
|
|
5204
5702
|
lineHeight: 24,
|
|
5205
5703
|
fontWeight: '600',
|
|
5206
|
-
},
|
|
5704
|
+
-},
|
|
5207
5705
|
title: {
|
|
5208
5706
|
fontSize: 32,
|
|
5209
5707
|
fontWeight: 'bold',
|
|
5210
5708
|
lineHeight: 32,
|
|
5211
|
-
},
|
|
5709
|
+
-},
|
|
5212
5710
|
subtitle: {
|
|
5213
5711
|
fontSize: 20,
|
|
5214
5712
|
fontWeight: 'bold',
|
|
5215
|
-
},
|
|
5713
|
+
-},
|
|
5216
5714
|
link: {
|
|
5217
5715
|
lineHeight: 30,
|
|
5218
5716
|
fontSize: 16,
|
|
5219
5717
|
color: '#0a7ea4',
|
|
5220
|
-
},
|
|
5718
|
+
-},
|
|
5221
5719
|
}));
|
|
5222
5720
|
```
|
|
5223
5721
|
|
|
5224
5722
|
components/ThemedText.tsx
|
|
5225
5723
|
|
|
5226
|
-
```
|
|
5724
|
+
```diff
|
|
5227
5725
|
export function ThemedText({
|
|
5228
5726
|
style,
|
|
5229
5727
|
lightColor,
|
|
@@ -5231,14 +5729,14 @@ export function ThemedText({
|
|
|
5231
5729
|
type = 'default',
|
|
5232
5730
|
...rest
|
|
5233
5731
|
}: ThemedTextProps) {
|
|
5234
|
-
styles.useVariants({ type })
|
|
5732
|
+
+ styles.useVariants({ type })
|
|
5235
5733
|
|
|
5236
5734
|
|
|
5237
5735
|
return (
|
|
5238
5736
|
<Text
|
|
5239
5737
|
style={[
|
|
5240
5738
|
styles.textColor(lightColor, darkColor),
|
|
5241
|
-
styles.textType,
|
|
5739
|
+
+ styles.textType,
|
|
5242
5740
|
style,
|
|
5243
5741
|
]}
|
|
5244
5742
|
{...rest}
|
|
@@ -5254,29 +5752,29 @@ const styles = StyleSheet.create((theme, rt) => ({
|
|
|
5254
5752
|
default: {
|
|
5255
5753
|
fontSize: 16,
|
|
5256
5754
|
lineHeight: 24,
|
|
5257
|
-
},
|
|
5755
|
+
+},
|
|
5258
5756
|
defaultSemiBold: {
|
|
5259
5757
|
fontSize: 16,
|
|
5260
5758
|
lineHeight: 24,
|
|
5261
5759
|
fontWeight: '600',
|
|
5262
|
-
},
|
|
5760
|
+
+},
|
|
5263
5761
|
title: {
|
|
5264
5762
|
fontSize: 32,
|
|
5265
5763
|
fontWeight: 'bold',
|
|
5266
5764
|
lineHeight: 32,
|
|
5267
|
-
},
|
|
5765
|
+
+},
|
|
5268
5766
|
subtitle: {
|
|
5269
5767
|
fontSize: 20,
|
|
5270
5768
|
fontWeight: 'bold',
|
|
5271
|
-
},
|
|
5769
|
+
+},
|
|
5272
5770
|
link: {
|
|
5273
5771
|
lineHeight: 30,
|
|
5274
5772
|
fontSize: 16,
|
|
5275
5773
|
color: '#0a7ea4',
|
|
5276
|
-
},
|
|
5277
|
-
}
|
|
5278
|
-
}
|
|
5279
|
-
},
|
|
5774
|
+
+},
|
|
5775
|
+
+}
|
|
5776
|
+
+}
|
|
5777
|
+
+},
|
|
5280
5778
|
textColor: (lightColor?: string, darkColor?: string) => ({
|
|
5281
5779
|
color: rt.colorScheme === 'dark' ? darkColor : lightColor,
|
|
5282
5780
|
}),
|
|
@@ -5303,16 +5801,16 @@ Instead of specifying `type` prop manually, we can use `UnistylesVariants` gener
|
|
|
5303
5801
|
|
|
5304
5802
|
components/ThemedText.tsx
|
|
5305
5803
|
|
|
5306
|
-
```
|
|
5307
|
-
import { StyleSheet } from 'react-native-unistyles';
|
|
5308
|
-
import { StyleSheet, type UnistylesVariants } from 'react-native-unistyles';
|
|
5804
|
+
```diff
|
|
5805
|
+
-import { StyleSheet } from 'react-native-unistyles';
|
|
5806
|
+
+import { StyleSheet, type UnistylesVariants } from 'react-native-unistyles';
|
|
5309
5807
|
|
|
5310
5808
|
|
|
5311
|
-
export type ThemedTextProps = TextProps & {
|
|
5312
|
-
export type ThemedTextProps = TextProps & UnistylesVariants<typeof styles> & {
|
|
5809
|
+
-export type ThemedTextProps = TextProps & {
|
|
5810
|
+
+export type ThemedTextProps = TextProps & UnistylesVariants<typeof styles> & {
|
|
5313
5811
|
lightColor?: string;
|
|
5314
5812
|
darkColor?: string;
|
|
5315
|
-
type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
|
|
5813
|
+
- type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
|
|
5316
5814
|
};
|
|
5317
5815
|
|
|
5318
5816
|
|
|
@@ -5320,8 +5818,8 @@ export function ThemedText({
|
|
|
5320
5818
|
style,
|
|
5321
5819
|
lightColor,
|
|
5322
5820
|
darkColor,
|
|
5323
|
-
type = 'default',
|
|
5324
|
-
type,
|
|
5821
|
+
-type = 'default',
|
|
5822
|
+
+type,
|
|
5325
5823
|
...rest
|
|
5326
5824
|
}: ThemedTextProps) {
|
|
5327
5825
|
```
|
|
@@ -5332,29 +5830,29 @@ Let’s remove those props and use the theme object directly.
|
|
|
5332
5830
|
|
|
5333
5831
|
components/ThemedText.tsx
|
|
5334
5832
|
|
|
5335
|
-
```
|
|
5833
|
+
```diff
|
|
5336
5834
|
import { Text, type TextProps } from 'react-native';
|
|
5337
5835
|
import { StyleSheet, type UnistylesVariants } from 'react-native-unistyles';
|
|
5338
5836
|
|
|
5339
5837
|
|
|
5340
|
-
export type ThemedTextProps = TextProps & UnistylesVariants<typeof styles>
|
|
5341
|
-
export type ThemedTextProps = TextProps & UnistylesVariants<typeof styles> & {
|
|
5342
|
-
lightColor?: string;
|
|
5343
|
-
darkColor?: string;
|
|
5344
|
-
};
|
|
5838
|
+
+export type ThemedTextProps = TextProps & UnistylesVariants<typeof styles>
|
|
5839
|
+
-export type ThemedTextProps = TextProps & UnistylesVariants<typeof styles> & {
|
|
5840
|
+
- lightColor?: string;
|
|
5841
|
+
- darkColor?: string;
|
|
5842
|
+
- };
|
|
5345
5843
|
|
|
5346
5844
|
|
|
5347
5845
|
export function ThemedText({
|
|
5348
5846
|
style,
|
|
5349
|
-
lightColor,
|
|
5350
|
-
darkColor,
|
|
5847
|
+
-lightColor,
|
|
5848
|
+
-darkColor,
|
|
5351
5849
|
...rest
|
|
5352
5850
|
}: ThemedTextProps) {
|
|
5353
5851
|
return (
|
|
5354
5852
|
<Text
|
|
5355
5853
|
style={[
|
|
5356
|
-
styles.textColor(lightColor, darkColor),
|
|
5357
|
-
styles.textColor,
|
|
5854
|
+
- styles.textColor(lightColor, darkColor),
|
|
5855
|
+
+ styles.textColor,
|
|
5358
5856
|
styles.textType,
|
|
5359
5857
|
style,
|
|
5360
5858
|
]}
|
|
@@ -5364,14 +5862,14 @@ export function ThemedText({
|
|
|
5364
5862
|
}
|
|
5365
5863
|
|
|
5366
5864
|
|
|
5367
|
-
const styles = StyleSheet.create(theme => ({
|
|
5865
|
+
+const styles = StyleSheet.create(theme => ({
|
|
5368
5866
|
const styles = StyleSheet.create((theme, rt) => ({
|
|
5369
|
-
textColor: (lightColor?: string, darkColor?: string) => ({
|
|
5867
|
+
-textColor: (lightColor?: string, darkColor?: string) => ({
|
|
5370
5868
|
color: rt.colorScheme === 'dark' ? darkColor : lightColor,
|
|
5371
|
-
}),
|
|
5869
|
+
-}),
|
|
5372
5870
|
textColor: {
|
|
5373
5871
|
color: theme.colors.typography
|
|
5374
|
-
},
|
|
5872
|
+
+},
|
|
5375
5873
|
textType: {
|
|
5376
5874
|
variants: {
|
|
5377
5875
|
type: {
|
|
@@ -5414,6 +5912,8 @@ Curious to learn more about variants? Check out the [variants guide](/v3/referen
|
|
|
5414
5912
|
|
|
5415
5913
|
### ThemedView - your turn!
|
|
5416
5914
|
|
|
5915
|
+
[Section titled “ThemedView - your turn!”](#themedview---your-turn)
|
|
5916
|
+
|
|
5417
5917
|
Now is the time to refactor the `ThemedView` component. This one is much simpler. Based on what you’ve learned, try refactoring it yourself to use the `theme.colors.background` property.
|
|
5418
5918
|
|
|
5419
5919
|
Once you’re done, check your work against the solution below:
|
|
@@ -5442,6 +5942,8 @@ const styles = StyleSheet.create(theme => ({
|
|
|
5442
5942
|
|
|
5443
5943
|
### Constants and hooks
|
|
5444
5944
|
|
|
5945
|
+
[Section titled “Constants and hooks”](#constants-and-hooks)
|
|
5946
|
+
|
|
5445
5947
|
Lastly, we can remove the `constants` and `hooks` folders, as they are now redundant. Your final project structure should be clean and organized.
|
|
5446
5948
|
|
|
5447
5949
|
* app/
|
|
@@ -5497,6 +5999,8 @@ Before we start building our own features, let’s adapt the default Expo Router
|
|
|
5497
5999
|
|
|
5498
6000
|
### App folder
|
|
5499
6001
|
|
|
6002
|
+
[Section titled “App folder”](#app-folder)
|
|
6003
|
+
|
|
5500
6004
|
Let’s start with the root layout file for the entire application.
|
|
5501
6005
|
|
|
5502
6006
|
* app/
|
|
@@ -5515,20 +6019,20 @@ Let’s remove the old theming logic:
|
|
|
5515
6019
|
|
|
5516
6020
|
app/\_layout.tsx
|
|
5517
6021
|
|
|
5518
|
-
```
|
|
5519
|
-
import React from 'react';
|
|
5520
|
-
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
|
|
6022
|
+
```diff
|
|
6023
|
+
+import React from 'react';
|
|
6024
|
+
-import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
|
|
5521
6025
|
import { useFonts } from 'expo-font';
|
|
5522
6026
|
import { Stack } from 'expo-router';
|
|
5523
6027
|
import { StatusBar } from 'expo-status-bar';
|
|
5524
6028
|
import 'react-native-reanimated';
|
|
5525
6029
|
|
|
5526
6030
|
|
|
5527
|
-
import { useColorScheme } from '@/hooks/useColorScheme';
|
|
6031
|
+
-import { useColorScheme } from '@/hooks/useColorScheme';
|
|
5528
6032
|
|
|
5529
6033
|
|
|
5530
6034
|
export default function RootLayout() {
|
|
5531
|
-
const colorScheme = useColorScheme();
|
|
6035
|
+
-const colorScheme = useColorScheme();
|
|
5532
6036
|
const [loaded] = useFonts({
|
|
5533
6037
|
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
|
|
5534
6038
|
});
|
|
@@ -5541,15 +6045,15 @@ export default function RootLayout() {
|
|
|
5541
6045
|
|
|
5542
6046
|
|
|
5543
6047
|
return (
|
|
5544
|
-
|
|
5545
|
-
|
|
6048
|
+
+<React.Fragment>
|
|
6049
|
+
-<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
|
5546
6050
|
<Stack>
|
|
5547
6051
|
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
|
5548
6052
|
<Stack.Screen name="+not-found" />
|
|
5549
6053
|
</Stack>
|
|
5550
6054
|
<StatusBar style="auto" />
|
|
5551
|
-
|
|
5552
|
-
|
|
6055
|
+
-</ThemeProvider>
|
|
6056
|
+
+</React.Fragment>
|
|
5553
6057
|
);
|
|
5554
6058
|
}
|
|
5555
6059
|
```
|
|
@@ -5558,20 +6062,22 @@ Next, for the `+not_found.tsx` file, we only need to swap the `StyleSheet` impor
|
|
|
5558
6062
|
|
|
5559
6063
|
app/+not\_found.tsx
|
|
5560
6064
|
|
|
5561
|
-
```
|
|
5562
|
-
import { StyleSheet } from 'react-native-unistyles';
|
|
5563
|
-
import { StyleSheet } from 'react-native';
|
|
6065
|
+
```diff
|
|
6066
|
+
+import { StyleSheet } from 'react-native-unistyles';
|
|
6067
|
+
-import { StyleSheet } from 'react-native';
|
|
5564
6068
|
```
|
|
5565
6069
|
|
|
5566
6070
|
### (tabs) folder
|
|
5567
6071
|
|
|
6072
|
+
[Section titled “(tabs) folder”](#tabs-folder)
|
|
6073
|
+
|
|
5568
6074
|
This folder contains the layout and screens for your `TabsNavigator`. For the `index.tsx` and `explore.tsx` files, the process is the same: we simply need to replace the standard `StyleSheet` import with the one from Unistyles.
|
|
5569
6075
|
|
|
5570
6076
|
app/(tabs)/index.tsx
|
|
5571
6077
|
|
|
5572
|
-
```
|
|
5573
|
-
import { Platform, StyleSheet } from 'react-native';
|
|
5574
|
-
import { StyleSheet } from 'react-native-unistyles';
|
|
6078
|
+
```diff
|
|
6079
|
+
-import { Platform, StyleSheet } from 'react-native';
|
|
6080
|
+
+import { StyleSheet } from 'react-native-unistyles';
|
|
5575
6081
|
```
|
|
5576
6082
|
|
|
5577
6083
|
For the JSX, we won’t need all these boilerplate components, so we can keep it as simple as possible:
|
|
@@ -5656,47 +6162,47 @@ Let’s refactor tab layout to use our new theme:
|
|
|
5656
6162
|
|
|
5657
6163
|
app/(tabs)/\_layout.tsx
|
|
5658
6164
|
|
|
5659
|
-
```
|
|
6165
|
+
```diff
|
|
5660
6166
|
import { Tabs } from 'expo-router';
|
|
5661
6167
|
import React from 'react';
|
|
5662
|
-
import { Platform } from 'react-native';
|
|
6168
|
+
-import { Platform } from 'react-native';
|
|
5663
6169
|
|
|
5664
6170
|
|
|
5665
|
-
import { HapticTab } from '@/components/HapticTab';
|
|
6171
|
+
-import { HapticTab } from '@/components/HapticTab';
|
|
5666
6172
|
import { IconSymbol } from '@/components/ui/IconSymbol';
|
|
5667
|
-
import TabBarBackground from '@/components/ui/TabBarBackground';
|
|
5668
|
-
import { Colors } from '@/constants/Colors';
|
|
5669
|
-
import { useColorScheme } from '@/hooks/useColorScheme';
|
|
5670
|
-
import { useUnistyles } from 'react-native-unistyles';
|
|
6173
|
+
-import TabBarBackground from '@/components/ui/TabBarBackground';
|
|
6174
|
+
-import { Colors } from '@/constants/Colors';
|
|
6175
|
+
-import { useColorScheme } from '@/hooks/useColorScheme';
|
|
6176
|
+
+import { useUnistyles } from 'react-native-unistyles';
|
|
5671
6177
|
|
|
5672
6178
|
|
|
5673
6179
|
export default function TabLayout() {
|
|
5674
|
-
const colorScheme = useColorScheme();
|
|
5675
|
-
const { theme } = useUnistyles();
|
|
6180
|
+
-const colorScheme = useColorScheme();
|
|
6181
|
+
+const { theme } = useUnistyles();
|
|
5676
6182
|
|
|
5677
6183
|
|
|
5678
6184
|
return (
|
|
5679
6185
|
<Tabs
|
|
5680
6186
|
screenOptions={{
|
|
5681
|
-
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
|
|
5682
|
-
tabBarInactiveTintColor: theme.colors.tint,
|
|
5683
|
-
tabBarActiveTintColor: theme.colors.activeTint,
|
|
5684
|
-
sceneStyle: {
|
|
5685
|
-
backgroundColor: theme.colors.background
|
|
5686
|
-
},
|
|
5687
|
-
tabBarStyle: {
|
|
5688
|
-
backgroundColor: theme.colors.foreground
|
|
5689
|
-
},
|
|
6187
|
+
- tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
|
|
6188
|
+
+ tabBarInactiveTintColor: theme.colors.tint,
|
|
6189
|
+
+ tabBarActiveTintColor: theme.colors.activeTint,
|
|
6190
|
+
+ sceneStyle: {
|
|
6191
|
+
+ backgroundColor: theme.colors.background
|
|
6192
|
+
+ },
|
|
6193
|
+
+ tabBarStyle: {
|
|
6194
|
+
+ backgroundColor: theme.colors.foreground
|
|
6195
|
+
+ },
|
|
5690
6196
|
headerShown: false,
|
|
5691
|
-
tabBarButton: HapticTab,
|
|
5692
|
-
tabBarBackground: TabBarBackground,
|
|
5693
|
-
tabBarStyle: Platform.select({
|
|
5694
|
-
ios: {
|
|
5695
|
-
|
|
5696
|
-
position: 'absolute',
|
|
5697
|
-
},
|
|
5698
|
-
default: {},
|
|
5699
|
-
}),
|
|
6197
|
+
- tabBarButton: HapticTab,
|
|
6198
|
+
- tabBarBackground: TabBarBackground,
|
|
6199
|
+
- tabBarStyle: Platform.select({
|
|
6200
|
+
- ios: {
|
|
6201
|
+
-// Use a transparent background on iOS to show the blur effect
|
|
6202
|
+
- position: 'absolute',
|
|
6203
|
+
- },
|
|
6204
|
+
- default: {},
|
|
6205
|
+
- }),
|
|
5700
6206
|
}}>
|
|
5701
6207
|
<Tabs.Screen
|
|
5702
6208
|
name="index"
|
|
@@ -5739,6 +6245,8 @@ In this final part, we’ll connect all the pieces together using a lightweight
|
|
|
5739
6245
|
|
|
5740
6246
|
### Adding State Management with StanJS
|
|
5741
6247
|
|
|
6248
|
+
[Section titled “Adding State Management with StanJS”](#adding-state-management-with-stanjs)
|
|
6249
|
+
|
|
5742
6250
|
For managing the user’s accent preference across our app, we need a state management solution that’s both lightweight and efficient. After considering various options, we decided to use our in-house library called StanJS.
|
|
5743
6251
|
|
|
5744
6252
|
Note
|
|
@@ -5749,6 +6257,8 @@ StanJS automatically generates setters for your state values and provides excell
|
|
|
5749
6257
|
|
|
5750
6258
|
### Installation and Setup
|
|
5751
6259
|
|
|
6260
|
+
[Section titled “Installation and Setup”](#installation-and-setup)
|
|
6261
|
+
|
|
5752
6262
|
Let’s install StanJS along with MMKV for data persistence:
|
|
5753
6263
|
|
|
5754
6264
|
```bash
|
|
@@ -5765,6 +6275,8 @@ StanJS has built-in MMKV support that makes data persistence effortless.
|
|
|
5765
6275
|
|
|
5766
6276
|
#### Create the Store
|
|
5767
6277
|
|
|
6278
|
+
[Section titled “Create the Store”](#create-the-store)
|
|
6279
|
+
|
|
5768
6280
|
First, let’s set up our store to manage the user’s preferred accent color:
|
|
5769
6281
|
|
|
5770
6282
|
store/store.ts
|
|
@@ -5786,11 +6298,11 @@ Before we can use our store, we need to create the `Accents` type. Let’s add i
|
|
|
5786
6298
|
|
|
5787
6299
|
unistyles.ts
|
|
5788
6300
|
|
|
5789
|
-
```
|
|
6301
|
+
```diff
|
|
5790
6302
|
// ... existing imports and theme definitions
|
|
5791
6303
|
|
|
5792
6304
|
|
|
5793
|
-
export type Accents = keyof typeof lightTheme['colors']['accents']
|
|
6305
|
+
+export type Accents = keyof typeof lightTheme['colors']['accents']
|
|
5794
6306
|
|
|
5795
6307
|
|
|
5796
6308
|
type AppBreakpoints = typeof breakpoints
|
|
@@ -5818,16 +6330,18 @@ export * from './store'
|
|
|
5818
6330
|
|
|
5819
6331
|
### Connecting the Accent Settings
|
|
5820
6332
|
|
|
6333
|
+
[Section titled “Connecting the Accent Settings”](#connecting-the-accent-settings)
|
|
6334
|
+
|
|
5821
6335
|
Now we need to update our accent settings screen to actually save the user’s choice to our store. Currently, it only updates local state that gets lost when the user navigates away.
|
|
5822
6336
|
|
|
5823
6337
|
Let’s update the settings screen to use our StanJS store:
|
|
5824
6338
|
|
|
5825
6339
|
app/settings/settings-accent.tsx
|
|
5826
6340
|
|
|
5827
|
-
```
|
|
6341
|
+
```diff
|
|
5828
6342
|
import { Button } from '@/components/Button'
|
|
5829
6343
|
import { ThemedText } from '@/components/ThemedText'
|
|
5830
|
-
import { useStore } from '@/store'
|
|
6344
|
+
+import { useStore } from '@/store'
|
|
5831
6345
|
import { router } from 'expo-router'
|
|
5832
6346
|
import React, { useState } from 'react'
|
|
5833
6347
|
import { Pressable, ScrollView, View } from 'react-native'
|
|
@@ -5836,10 +6350,10 @@ import { StyleSheet, useUnistyles } from 'react-native-unistyles'
|
|
|
5836
6350
|
|
|
5837
6351
|
export default function SettingsAccentScreen() {
|
|
5838
6352
|
const { theme } = useUnistyles()
|
|
5839
|
-
const { setPreferredAccent, preferredAccent } = useStore()
|
|
6353
|
+
+const { setPreferredAccent, preferredAccent } = useStore()
|
|
5840
6354
|
const allAccents = theme.colors.accents
|
|
5841
|
-
const [selectedAccent, setSelectedAccent] = useState(preferredAccent)
|
|
5842
|
-
const [selectedAccent, setSelectedAccent] = useState('banana')
|
|
6355
|
+
+const [selectedAccent, setSelectedAccent] = useState(preferredAccent)
|
|
6356
|
+
-const [selectedAccent, setSelectedAccent] = useState('banana')
|
|
5843
6357
|
|
|
5844
6358
|
|
|
5845
6359
|
return (
|
|
@@ -5869,7 +6383,7 @@ export default function SettingsAccentScreen() {
|
|
|
5869
6383
|
label="Save"
|
|
5870
6384
|
accent={selectedAccent}
|
|
5871
6385
|
onPress={() => {
|
|
5872
|
-
setPreferredAccent(selectedAccent)
|
|
6386
|
+
+setPreferredAccent(selectedAccent)
|
|
5873
6387
|
router.back()
|
|
5874
6388
|
}}
|
|
5875
6389
|
/>
|
|
@@ -5888,21 +6402,25 @@ The beautiful thing about this approach is that any other component that listens
|
|
|
5888
6402
|
|
|
5889
6403
|
### Making Components Dynamic
|
|
5890
6404
|
|
|
6405
|
+
[Section titled “Making Components Dynamic”](#making-components-dynamic)
|
|
6406
|
+
|
|
5891
6407
|
Now let’s update our components to respond to the user’s accent preference instead of using hardcoded values.
|
|
5892
6408
|
|
|
5893
6409
|
#### Update Button Component
|
|
5894
6410
|
|
|
6411
|
+
[Section titled “Update Button Component”](#update-button-component)
|
|
6412
|
+
|
|
5895
6413
|
The Button component needs to use the store value as a fallback while still allowing accent overrides:
|
|
5896
6414
|
|
|
5897
6415
|
components/Button.tsx
|
|
5898
6416
|
|
|
5899
|
-
```
|
|
6417
|
+
```diff
|
|
5900
6418
|
import { Pressable } from 'react-native'
|
|
5901
6419
|
import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated'
|
|
5902
6420
|
import { StyleSheet, UnistylesVariants } from 'react-native-unistyles'
|
|
5903
6421
|
import { useAnimatedVariantColor } from 'react-native-unistyles/reanimated'
|
|
5904
6422
|
import { ThemedText } from './ThemedText'
|
|
5905
|
-
import { useStore } from '@/store'
|
|
6423
|
+
+import { useStore } from '@/store'
|
|
5906
6424
|
|
|
5907
6425
|
|
|
5908
6426
|
interface ButtonProps extends UnistylesVariants<typeof style> {
|
|
@@ -5916,12 +6434,12 @@ export const Button: React.FunctionComponent<ButtonProps> = ({
|
|
|
5916
6434
|
accent,
|
|
5917
6435
|
onPress
|
|
5918
6436
|
}) => {
|
|
5919
|
-
const { preferredAccent } = useStore()
|
|
6437
|
+
+const { preferredAccent } = useStore()
|
|
5920
6438
|
|
|
5921
6439
|
|
|
5922
6440
|
style.useVariants({
|
|
5923
6441
|
accent: accent ?? preferredAccent
|
|
5924
|
-
accent: accent
|
|
6442
|
+
-accent: accent
|
|
5925
6443
|
})
|
|
5926
6444
|
|
|
5927
6445
|
|
|
@@ -5952,23 +6470,25 @@ This implementation is flexible - it uses the accent prop if provided (like in t
|
|
|
5952
6470
|
|
|
5953
6471
|
#### Update PlayerControls Component
|
|
5954
6472
|
|
|
6473
|
+
[Section titled “Update PlayerControls Component”](#update-playercontrols-component)
|
|
6474
|
+
|
|
5955
6475
|
The PlayerControls component should always use the user’s preferred accent:
|
|
5956
6476
|
|
|
5957
6477
|
components/PlayerControls.tsx
|
|
5958
6478
|
|
|
5959
|
-
```
|
|
6479
|
+
```diff
|
|
5960
6480
|
import { IconSymbol } from '@/components/ui/IconSymbol'
|
|
5961
|
-
import { useStore } from '@/store'
|
|
6481
|
+
+import { useStore } from '@/store'
|
|
5962
6482
|
import { Pressable, View } from 'react-native'
|
|
5963
6483
|
import { StyleSheet } from 'react-native-unistyles'
|
|
5964
6484
|
import { useUnistyles } from 'react-native-unistyles'
|
|
5965
6485
|
|
|
5966
6486
|
|
|
5967
6487
|
export const PlayerControls = () => {
|
|
5968
|
-
const { preferredAccent } = useStore()
|
|
6488
|
+
+const { preferredAccent } = useStore()
|
|
5969
6489
|
const { theme } = useUnistyles()
|
|
5970
|
-
const accent = theme.colors.accents[preferredAccent]
|
|
5971
|
-
const accent = theme.colors.accents['banana']
|
|
6490
|
+
+const accent = theme.colors.accents[preferredAccent]
|
|
6491
|
+
-const accent = theme.colors.accents['banana']
|
|
5972
6492
|
|
|
5973
6493
|
|
|
5974
6494
|
return (
|
|
@@ -6002,7 +6522,7 @@ We need to remove one more `banana` from `[songId].tsx` screen when there is no
|
|
|
6002
6522
|
|
|
6003
6523
|
screens/player/\[songId].tsx
|
|
6004
6524
|
|
|
6005
|
-
```
|
|
6525
|
+
```diff
|
|
6006
6526
|
import { Button } from '@/components/Button'
|
|
6007
6527
|
import { PlayerControls } from '@/components/PlayerControls'
|
|
6008
6528
|
import { ThemedText } from '@/components/ThemedText'
|
|
@@ -6031,7 +6551,7 @@ export default function PlayerScreen() {
|
|
|
6031
6551
|
</ThemedText>
|
|
6032
6552
|
<Button
|
|
6033
6553
|
label="Pick a song"
|
|
6034
|
-
accent="banana"
|
|
6554
|
+
-accent="banana"
|
|
6035
6555
|
onPress={() => router.replace('/')}
|
|
6036
6556
|
/>
|
|
6037
6557
|
</ThemedView>
|
|
@@ -6044,6 +6564,8 @@ export default function PlayerScreen() {
|
|
|
6044
6564
|
|
|
6045
6565
|
### Android
|
|
6046
6566
|
|
|
6567
|
+
[Section titled “Android”](#android)
|
|
6568
|
+
|
|
6047
6569
|
Now let’s test our app on Android to see if there are any platform-specific issues that need addressing.
|
|
6048
6570
|
|
|
6049
6571
|
Running the app on Android, you’ll notice it works correctly overall, but there’s one issue - the TabBar icons are missing! This happens because our `IconSymbol` component uses iOS-specific SF Symbols that don’t exist on Android.
|
|
@@ -6052,23 +6574,23 @@ Let’s fix the icon mappings in our `IconSymbol` component:
|
|
|
6052
6574
|
|
|
6053
6575
|
components/ui/IconSymbol.tsx
|
|
6054
6576
|
|
|
6055
|
-
```
|
|
6577
|
+
```diff
|
|
6056
6578
|
// ... existing imports and code
|
|
6057
6579
|
|
|
6058
6580
|
|
|
6059
6581
|
const MAPPING = {
|
|
6060
|
-
'house.fill': 'home',
|
|
6061
|
-
'paperplane.fill': 'send',
|
|
6062
|
-
'chevron.left.forwardslash.chevron.right': 'code',
|
|
6063
|
-
'chevron.right': 'chevron-right',
|
|
6064
|
-
'music.house': 'queue-music',
|
|
6065
|
-
'play.circle': 'play-circle-outline',
|
|
6066
|
-
'gear.circle': 'settings',
|
|
6067
|
-
'backward.end.fill': 'first-page',
|
|
6068
|
-
'backward.fill': 'fast-rewind',
|
|
6069
|
-
'forward.fill': 'fast-forward',
|
|
6070
|
-
'forward.end.fill': 'last-page',
|
|
6071
|
-
'play.circle.fill': 'play-circle-filled'
|
|
6582
|
+
-'house.fill': 'home',
|
|
6583
|
+
-'paperplane.fill': 'send',
|
|
6584
|
+
-'chevron.left.forwardslash.chevron.right': 'code',
|
|
6585
|
+
-'chevron.right': 'chevron-right',
|
|
6586
|
+
+'music.house': 'queue-music',
|
|
6587
|
+
+'play.circle': 'play-circle-outline',
|
|
6588
|
+
+'gear.circle': 'settings',
|
|
6589
|
+
+'backward.end.fill': 'first-page',
|
|
6590
|
+
+'backward.fill': 'fast-rewind',
|
|
6591
|
+
+'forward.fill': 'fast-forward',
|
|
6592
|
+
+'forward.end.fill': 'last-page',
|
|
6593
|
+
+'play.circle.fill': 'play-circle-filled'
|
|
6072
6594
|
} as IconMapping;
|
|
6073
6595
|
|
|
6074
6596
|
|
|
@@ -6085,6 +6607,8 @@ We could also improve the bottom navigation bar by properly configuring `react-n
|
|
|
6085
6607
|
|
|
6086
6608
|
### Web
|
|
6087
6609
|
|
|
6610
|
+
[Section titled “Web”](#web)
|
|
6611
|
+
|
|
6088
6612
|
When you try to run your app on the web, you will encounter a crash:
|
|
6089
6613
|
|
|
6090
6614
|

|
|
@@ -6141,6 +6665,8 @@ Before we wrap up this section, let’s explore how Unistyles handles responsive
|
|
|
6141
6665
|
|
|
6142
6666
|
### Breakpoints and Media Queries
|
|
6143
6667
|
|
|
6668
|
+
[Section titled “Breakpoints and Media Queries”](#breakpoints-and-media-queries)
|
|
6669
|
+
|
|
6144
6670
|
When your app needs to scale from a phone in your pocket to a large desktop monitor, you face new challenges. Unistyles provides powerful, built-in tools to help you create adaptive and responsive layouts with ease.
|
|
6145
6671
|
|
|
6146
6672
|
The most direct way to create responsive styles is by using **breakpoint objects**. You can turn any style value into an object where the keys are your predefined breakpoint names (`xs`, `sm`, `md`, etc.) and the values are the styles for that specific breakpoint. This enables you to easily create responsive layouts, but only for the properties you need.
|
|
@@ -6149,7 +6675,7 @@ Let’s apply this to our `SongTile` component to make the album art larger on b
|
|
|
6149
6675
|
|
|
6150
6676
|
components/SongTile.tsx
|
|
6151
6677
|
|
|
6152
|
-
```
|
|
6678
|
+
```diff
|
|
6153
6679
|
// ... JSX remains the same
|
|
6154
6680
|
|
|
6155
6681
|
|
|
@@ -6166,12 +6692,12 @@ const style = StyleSheet.create(theme => ({
|
|
|
6166
6692
|
xs: 80,
|
|
6167
6693
|
md: 120,
|
|
6168
6694
|
lg: 200
|
|
6169
|
-
},
|
|
6695
|
+
+},
|
|
6170
6696
|
height: {
|
|
6171
6697
|
xs: 80,
|
|
6172
6698
|
md: 120,
|
|
6173
6699
|
lg: 200
|
|
6174
|
-
},
|
|
6700
|
+
+},
|
|
6175
6701
|
borderRadius: theme.gap(2)
|
|
6176
6702
|
},
|
|
6177
6703
|
textContainer: {
|
|
@@ -6194,9 +6720,9 @@ Let’s modify our `PlayerScreen` to adopt a more traditional web layout on larg
|
|
|
6194
6720
|
|
|
6195
6721
|
app/(tabs)/player/\[songId].tsx
|
|
6196
6722
|
|
|
6197
|
-
```
|
|
6198
|
-
import { StyleSheet } from 'react-native-unistyles'
|
|
6199
|
-
import { mq, StyleSheet } from 'react-native-unistyles'
|
|
6723
|
+
```diff
|
|
6724
|
+
-import { StyleSheet } from 'react-native-unistyles'
|
|
6725
|
+
+import { mq, StyleSheet } from 'react-native-unistyles'
|
|
6200
6726
|
|
|
6201
6727
|
|
|
6202
6728
|
// ... JSX remains the same
|
|
@@ -6213,8 +6739,8 @@ const styles = StyleSheet.create((theme, rt) => ({
|
|
|
6213
6739
|
gap: theme.gap(2),
|
|
6214
6740
|
alignItems: 'center',
|
|
6215
6741
|
justifyContent: {
|
|
6216
|
-
[mq.only.width(600)]: 'center'
|
|
6217
|
-
},
|
|
6742
|
+
+[mq.only.width(600)]: 'center'
|
|
6743
|
+
+},
|
|
6218
6744
|
marginTop: rt.insets.top + theme.gap(3),
|
|
6219
6745
|
},
|
|
6220
6746
|
image: {
|
|
@@ -6233,6 +6759,8 @@ Dive deeper into the `mq` utility and its helpers [here](/v3/references/media-qu
|
|
|
6233
6759
|
|
|
6234
6760
|
### Web styling features
|
|
6235
6761
|
|
|
6762
|
+
[Section titled “Web styling features”](#web-styling-features)
|
|
6763
|
+
|
|
6236
6764
|
While Unistyles excels at universal styling, there are times you’ll want to leverage platform-specific features. On the web, this often means using CSS pseudo-selectors like `:hover` and `:active` for a more native web experience and better performance.
|
|
6237
6765
|
|
|
6238
6766
|
Unistyles makes this incredibly simple with the `_web` property.
|
|
@@ -6261,7 +6789,7 @@ To do this, we’ll modify our container style and add a `_web` key. Inside this
|
|
|
6261
6789
|
|
|
6262
6790
|
components/SettingTile.tsx
|
|
6263
6791
|
|
|
6264
|
-
```
|
|
6792
|
+
```diff
|
|
6265
6793
|
const styles = StyleSheet.create({
|
|
6266
6794
|
container: {
|
|
6267
6795
|
flexDirection: 'row',
|
|
@@ -6271,11 +6799,11 @@ const styles = StyleSheet.create({
|
|
|
6271
6799
|
_web: {
|
|
6272
6800
|
_hover: {
|
|
6273
6801
|
opacity: 0.75
|
|
6274
|
-
},
|
|
6802
|
+
+},
|
|
6275
6803
|
_active: {
|
|
6276
6804
|
opacity: 0.5
|
|
6277
|
-
}
|
|
6278
|
-
}
|
|
6805
|
+
+}
|
|
6806
|
+
+}
|
|
6279
6807
|
}
|
|
6280
6808
|
})
|
|
6281
6809
|
```
|
|
@@ -6294,6 +6822,8 @@ Learn more about all the web-specific features [here](/v3/references/web-only).
|
|
|
6294
6822
|
|
|
6295
6823
|
### What We’ve Built Together
|
|
6296
6824
|
|
|
6825
|
+
[Section titled “What We’ve Built Together”](#what-weve-built-together)
|
|
6826
|
+
|
|
6297
6827
|
Congratulations! You’ve built a complete, cross-platform music application that demonstrates the full power of Unistyles 3.0. Let’s recap what we’ve accomplished:
|
|
6298
6828
|
|
|
6299
6829
|
**Core Features:**
|
|
@@ -6327,6 +6857,8 @@ Congratulations! You’ve built a complete, cross-platform music application tha
|
|
|
6327
6857
|
|
|
6328
6858
|
### Summary
|
|
6329
6859
|
|
|
6860
|
+
[Section titled “Summary”](#summary)
|
|
6861
|
+
|
|
6330
6862
|
You’ve just completed an incredible journey building a full-featured, cross-platform music application with Unistyles 3.0. From initial configuration to advanced theming and state management.
|
|
6331
6863
|
|
|
6332
6864
|
But most importantly, you’ve learned to think in Unistyles. You understand when to use variants vs dynamic functions, how to leverage the runtime for device-aware styling, and how to build components that are both flexible and maintainable.
|
|
@@ -6353,6 +6885,8 @@ During this tutorial, we will cover most of the Unistyles features and best prac
|
|
|
6353
6885
|
|
|
6354
6886
|
### Create new project
|
|
6355
6887
|
|
|
6888
|
+
[Section titled “Create new project”](#create-new-project)
|
|
6889
|
+
|
|
6356
6890
|
First, let’s scaffold a new Expo project using the command line:
|
|
6357
6891
|
|
|
6358
6892
|
```bash
|
|
@@ -6363,9 +6897,11 @@ cd unistyles-tutorial
|
|
|
6363
6897
|
Next, install Unistyles, its dependencies, and Reanimated:
|
|
6364
6898
|
|
|
6365
6899
|
```bash
|
|
6366
|
-
yarn add react-native-reanimated react-native-unistyles react-native-nitro-modules
|
|
6900
|
+
yarn add react-native-reanimated react-native-unistyles react-native-nitro-modules
|
|
6367
6901
|
```
|
|
6368
6902
|
|
|
6903
|
+
> `react-native-edge-to-edge` is optional since v3.1.0 — Unistyles enables edge-to-edge automatically. See [dependencies](/v3/other/dependencies) for details.
|
|
6904
|
+
|
|
6369
6905
|
Tip
|
|
6370
6906
|
|
|
6371
6907
|
For best results and to ensure compatibility, we recommend using the `react-native-nitro-modules` version specified in the Unistyles [compatibility table](https://github.com/jpudysz/react-native-unistyles?tab=readme-ov-file#installation).
|
|
@@ -6378,6 +6914,8 @@ yarn expo prebuild --clean
|
|
|
6378
6914
|
|
|
6379
6915
|
### Configure Babel Plugins
|
|
6380
6916
|
|
|
6917
|
+
[Section titled “Configure Babel Plugins”](#configure-babel-plugins)
|
|
6918
|
+
|
|
6381
6919
|
Both `Unistyles` and `Reanimated` require a Babel plugin to work. Since a `babel.config.js` file isn’t created by default, we can generate it by running:
|
|
6382
6920
|
|
|
6383
6921
|
```bash
|
|
@@ -6386,17 +6924,17 @@ npx expo customize babel.config.js
|
|
|
6386
6924
|
|
|
6387
6925
|
Now, add the `unistyles` and `reanimated` plugins to your `babel.config.js`:
|
|
6388
6926
|
|
|
6389
|
-
```
|
|
6927
|
+
```diff
|
|
6390
6928
|
module.exports = function (api) {
|
|
6391
6929
|
api.cache(true);
|
|
6392
6930
|
return {
|
|
6393
6931
|
presets: ['babel-preset-expo'],
|
|
6394
|
-
plugins: [
|
|
6395
|
-
['react-native-unistyles/plugin', {
|
|
6396
|
-
root: 'app'
|
|
6397
|
-
}],
|
|
6398
|
-
['react-native-
|
|
6399
|
-
]
|
|
6932
|
+
+ plugins: [
|
|
6933
|
+
+ ['react-native-unistyles/plugin', {
|
|
6934
|
+
+ root: 'app'
|
|
6935
|
+
+ }],
|
|
6936
|
+
+ ['react-native-reanimated/plugin']
|
|
6937
|
+
+ ]
|
|
6400
6938
|
};
|
|
6401
6939
|
};
|
|
6402
6940
|
```
|
|
@@ -6407,14 +6945,16 @@ To learn more about the Unistyles Babel plugin and its configuration options, ch
|
|
|
6407
6945
|
|
|
6408
6946
|
### Modify app entry point
|
|
6409
6947
|
|
|
6948
|
+
[Section titled “Modify app entry point”](#modify-app-entry-point)
|
|
6949
|
+
|
|
6410
6950
|
To use features like themes and breakpoints, Unistyles must be configured before your application code runs. This ensures that all stylesheets are created with the correct theme and device context.
|
|
6411
6951
|
|
|
6412
6952
|
First, update your `package.json`:
|
|
6413
6953
|
|
|
6414
|
-
```
|
|
6954
|
+
```diff
|
|
6415
6955
|
{
|
|
6416
|
-
"main": "expo-router/entry"
|
|
6417
|
-
"main": "index.ts"
|
|
6956
|
+
-"main": "expo-router/entry"
|
|
6957
|
+
+"main": "index.ts"
|
|
6418
6958
|
}
|
|
6419
6959
|
```
|
|
6420
6960
|
|
|
@@ -6429,6 +6969,8 @@ We’ll create the `unistyles.ts` file in the next step.
|
|
|
6429
6969
|
|
|
6430
6970
|
### Configure Unistyles
|
|
6431
6971
|
|
|
6972
|
+
[Section titled “Configure Unistyles”](#configure-unistyles)
|
|
6973
|
+
|
|
6432
6974
|
This is where the magic happens! Create a `unistyles.ts` file in your project’s root. Here, we’ll define our `themes`, `breakpoints`, and register them with Unistyles.
|
|
6433
6975
|
|
|
6434
6976
|
```ts
|
|
@@ -6540,6 +7082,8 @@ Time to build the modal screens for theme and accent selection. We’ll create i
|
|
|
6540
7082
|
|
|
6541
7083
|
### Create SettingOptionRadio Component
|
|
6542
7084
|
|
|
7085
|
+
[Section titled “Create SettingOptionRadio Component”](#create-settingoptionradio-component)
|
|
7086
|
+
|
|
6543
7087
|
Let’s start with a radio button component for selecting theme modes. This component brings together concepts we’ve covered in previous steps.
|
|
6544
7088
|
|
|
6545
7089
|
Create `components/SettingOptionRadio.tsx`:
|
|
@@ -6629,14 +7173,16 @@ This component combines everything we’ve learned: `useVariants` for the radio
|
|
|
6629
7173
|
|
|
6630
7174
|
### Basic Theme Settings Screen
|
|
6631
7175
|
|
|
7176
|
+
[Section titled “Basic Theme Settings Screen”](#basic-theme-settings-screen)
|
|
7177
|
+
|
|
6632
7178
|
Let’s add this component to the theme settings screen:
|
|
6633
7179
|
|
|
6634
7180
|
app/settings/settings-theme.tsx
|
|
6635
7181
|
|
|
6636
|
-
```
|
|
6637
|
-
import { ThemedText } from '@/components/ThemedText'
|
|
6638
|
-
import { SettingOptionRadio } from '@/components/SettingOptionRadio'
|
|
6639
|
-
import React from 'react'
|
|
7182
|
+
```diff
|
|
7183
|
+
-import { ThemedText } from '@/components/ThemedText'
|
|
7184
|
+
+import { SettingOptionRadio } from '@/components/SettingOptionRadio'
|
|
7185
|
+
+import React from 'react'
|
|
6640
7186
|
import { ScrollView } from 'react-native'
|
|
6641
7187
|
import { StyleSheet } from 'react-native-unistyles'
|
|
6642
7188
|
|
|
@@ -6644,9 +7190,9 @@ import { StyleSheet } from 'react-native-unistyles'
|
|
|
6644
7190
|
export default function SettingsThemeScreen() {
|
|
6645
7191
|
return (
|
|
6646
7192
|
<ScrollView contentContainerStyle={styles.container}>
|
|
6647
|
-
|
|
6648
|
-
Change theme
|
|
6649
|
-
|
|
7193
|
+
-<ThemedText type="title">
|
|
7194
|
+
- Change theme
|
|
7195
|
+
-</ThemedText>
|
|
6650
7196
|
<SettingOptionRadio
|
|
6651
7197
|
label="System"
|
|
6652
7198
|
isSelected={false}
|
|
@@ -6674,6 +7220,8 @@ const styles = StyleSheet.create(theme => ({
|
|
|
6674
7220
|
|
|
6675
7221
|
### Create ThemeColor Component
|
|
6676
7222
|
|
|
7223
|
+
[Section titled “Create ThemeColor Component”](#create-themecolor-component)
|
|
7224
|
+
|
|
6677
7225
|
Now let’s create a component called `ThemeColor` that will preview different themes.
|
|
6678
7226
|
|
|
6679
7227
|
components/ThemeColor.tsx
|
|
@@ -6734,16 +7282,18 @@ Before proceeding further, notice that we used `keyof UnistylesTheme` (as label
|
|
|
6734
7282
|
|
|
6735
7283
|
### Enhanced Theme Settings Screen
|
|
6736
7284
|
|
|
7285
|
+
[Section titled “Enhanced Theme Settings Screen”](#enhanced-theme-settings-screen)
|
|
7286
|
+
|
|
6737
7287
|
Let’s update the theme settings screen to include theme previews:
|
|
6738
7288
|
|
|
6739
7289
|
app/settings/settings-theme.tsx
|
|
6740
7290
|
|
|
6741
|
-
```
|
|
7291
|
+
```diff
|
|
6742
7292
|
import { SettingOptionRadio } from '@/components/SettingOptionRadio'
|
|
6743
|
-
import { ThemeColor } from '@/components/ThemeColor'
|
|
7293
|
+
+import { ThemeColor } from '@/components/ThemeColor'
|
|
6744
7294
|
import React from 'react'
|
|
6745
|
-
import { ScrollView, View } from 'react-native'
|
|
6746
|
-
import { ScrollView } from 'react-native'
|
|
7295
|
+
+import { ScrollView, View } from 'react-native'
|
|
7296
|
+
-import { ScrollView } from 'react-native'
|
|
6747
7297
|
import { StyleSheet } from 'react-native-unistyles'
|
|
6748
7298
|
|
|
6749
7299
|
|
|
@@ -6760,16 +7310,16 @@ export default function SettingsThemeScreen() {
|
|
|
6760
7310
|
isSelected={false}
|
|
6761
7311
|
onPress={() => {}}
|
|
6762
7312
|
/>
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
label="light"
|
|
6766
|
-
onPress={() => {}}
|
|
6767
|
-
/>
|
|
6768
|
-
|
|
6769
|
-
label="dark"
|
|
6770
|
-
onPress={() => {}}
|
|
6771
|
-
/>
|
|
6772
|
-
|
|
7313
|
+
+<View style={styles.row}>
|
|
7314
|
+
+<ThemeColor
|
|
7315
|
+
+label="light"
|
|
7316
|
+
+onPress={() => {}}
|
|
7317
|
+
+ />
|
|
7318
|
+
+<ThemeColor
|
|
7319
|
+
+label="dark"
|
|
7320
|
+
+onPress={() => {}}
|
|
7321
|
+
+ />
|
|
7322
|
+
+</View>
|
|
6773
7323
|
</ScrollView>
|
|
6774
7324
|
)
|
|
6775
7325
|
}
|
|
@@ -6786,7 +7336,7 @@ const styles = StyleSheet.create(theme => ({
|
|
|
6786
7336
|
justifyContent: 'center',
|
|
6787
7337
|
flexDirection: 'row',
|
|
6788
7338
|
gap: theme.gap(2)
|
|
6789
|
-
}
|
|
7339
|
+
+}
|
|
6790
7340
|
}))
|
|
6791
7341
|
```
|
|
6792
7342
|
|
|
@@ -6800,64 +7350,66 @@ It seems that we need to also update `react-navigation` Header to use Unistyles
|
|
|
6800
7350
|
|
|
6801
7351
|
### Complete Theme Settings Implementation
|
|
6802
7352
|
|
|
7353
|
+
[Section titled “Complete Theme Settings Implementation”](#complete-theme-settings-implementation)
|
|
7354
|
+
|
|
6803
7355
|
Now let’s add the full functionality using `UnistylesRuntime` and `useUnistyles`:
|
|
6804
7356
|
|
|
6805
7357
|
app/settings/settings-theme.tsx
|
|
6806
7358
|
|
|
6807
|
-
```
|
|
7359
|
+
```diff
|
|
6808
7360
|
import { SettingOptionRadio } from '@/components/SettingOptionRadio'
|
|
6809
7361
|
import { ThemeColor } from '@/components/ThemeColor'
|
|
6810
7362
|
import React from 'react'
|
|
6811
7363
|
import { ScrollView, View } from 'react-native'
|
|
6812
|
-
import { StyleSheet, UnistylesRuntime, useUnistyles } from 'react-native-unistyles'
|
|
6813
|
-
import { StyleSheet } from 'react-native-unistyles'
|
|
7364
|
+
+import { StyleSheet, UnistylesRuntime, useUnistyles } from 'react-native-unistyles'
|
|
7365
|
+
-import { StyleSheet } from 'react-native-unistyles'
|
|
6814
7366
|
|
|
6815
7367
|
|
|
6816
7368
|
export default function SettingsThemeScreen() {
|
|
6817
|
-
const { rt } = useUnistyles()
|
|
7369
|
+
+const { rt } = useUnistyles()
|
|
6818
7370
|
|
|
6819
7371
|
|
|
6820
7372
|
return (
|
|
6821
7373
|
<ScrollView contentContainerStyle={styles.container}>
|
|
6822
7374
|
<SettingOptionRadio
|
|
6823
7375
|
label="System"
|
|
6824
|
-
isSelected={rt.hasAdaptiveThemes}
|
|
6825
|
-
onPress={() => {
|
|
6826
|
-
if (rt.hasAdaptiveThemes) {
|
|
6827
|
-
return
|
|
6828
|
-
}
|
|
7376
|
+
+isSelected={rt.hasAdaptiveThemes}
|
|
7377
|
+
+onPress={() => {
|
|
7378
|
+
+if (rt.hasAdaptiveThemes) {
|
|
7379
|
+
+return
|
|
7380
|
+
+ }
|
|
6829
7381
|
|
|
6830
7382
|
|
|
6831
|
-
UnistylesRuntime.setAdaptiveThemes(true)
|
|
6832
|
-
}}
|
|
6833
|
-
isSelected={false}
|
|
6834
|
-
onPress={() => {}}
|
|
7383
|
+
+UnistylesRuntime.setAdaptiveThemes(true)
|
|
7384
|
+
+ }}
|
|
7385
|
+
-isSelected={false}
|
|
7386
|
+
-onPress={() => {}}
|
|
6835
7387
|
/>
|
|
6836
7388
|
<SettingOptionRadio
|
|
6837
7389
|
label="User"
|
|
6838
|
-
isSelected={!rt.hasAdaptiveThemes}
|
|
6839
|
-
onPress={() => {
|
|
6840
|
-
if (rt.hasAdaptiveThemes) {
|
|
6841
|
-
UnistylesRuntime.setAdaptiveThemes(false)
|
|
6842
|
-
}
|
|
6843
|
-
}}
|
|
6844
|
-
isSelected={false}
|
|
6845
|
-
onPress={() => {}}
|
|
7390
|
+
+isSelected={!rt.hasAdaptiveThemes}
|
|
7391
|
+
+onPress={() => {
|
|
7392
|
+
+if (rt.hasAdaptiveThemes) {
|
|
7393
|
+
+UnistylesRuntime.setAdaptiveThemes(false)
|
|
7394
|
+
+ }
|
|
7395
|
+
+ }}
|
|
7396
|
+
-isSelected={false}
|
|
7397
|
+
-onPress={() => {}}
|
|
6846
7398
|
/>
|
|
6847
|
-
{!rt.hasAdaptiveThemes && (
|
|
7399
|
+
+{!rt.hasAdaptiveThemes && (
|
|
6848
7400
|
<View style={styles.row}>
|
|
6849
7401
|
<ThemeColor
|
|
6850
7402
|
label="light"
|
|
6851
|
-
onPress={() => UnistylesRuntime.setTheme('light')}
|
|
6852
|
-
onPress={() => {}}
|
|
7403
|
+
+onPress={() => UnistylesRuntime.setTheme('light')}
|
|
7404
|
+
-onPress={() => {}}
|
|
6853
7405
|
/>
|
|
6854
7406
|
<ThemeColor
|
|
6855
7407
|
label="dark"
|
|
6856
|
-
onPress={() => UnistylesRuntime.setTheme('dark')}
|
|
6857
|
-
onPress={() => {}}
|
|
7408
|
+
+onPress={() => UnistylesRuntime.setTheme('dark')}
|
|
7409
|
+
-onPress={() => {}}
|
|
6858
7410
|
/>
|
|
6859
7411
|
</View>
|
|
6860
|
-
)}
|
|
7412
|
+
+ )}
|
|
6861
7413
|
</ScrollView>
|
|
6862
7414
|
)
|
|
6863
7415
|
}
|
|
@@ -6875,11 +7427,13 @@ Try playing with different settings to see how the app adapts to your choices.
|
|
|
6875
7427
|
|
|
6876
7428
|
### Update navigation header colors
|
|
6877
7429
|
|
|
7430
|
+
[Section titled “Update navigation header colors”](#update-navigation-header-colors)
|
|
7431
|
+
|
|
6878
7432
|
As you probably noticed, navigation header colors are not updated when theme changes. Let’s fix that by updating `app/(tabs)/settings/_layout.tsx` file:
|
|
6879
7433
|
|
|
6880
7434
|
app/(tabs)/settings/\_layout.tsx
|
|
6881
7435
|
|
|
6882
|
-
```
|
|
7436
|
+
```diff
|
|
6883
7437
|
import { Stack } from 'expo-router'
|
|
6884
7438
|
import React from 'react'
|
|
6885
7439
|
import { useUnistyles } from 'react-native-unistyles'
|
|
@@ -6892,12 +7446,12 @@ export default function SettingsLayout() {
|
|
|
6892
7446
|
return (
|
|
6893
7447
|
<Stack
|
|
6894
7448
|
screenOptions={{
|
|
6895
|
-
headerTitleStyle: {
|
|
6896
|
-
color: theme.colors.typography
|
|
6897
|
-
},
|
|
6898
|
-
headerStyle: {
|
|
6899
|
-
backgroundColor: theme.colors.background
|
|
6900
|
-
},
|
|
7449
|
+
+ headerTitleStyle: {
|
|
7450
|
+
+ color: theme.colors.typography
|
|
7451
|
+
+ },
|
|
7452
|
+
+ headerStyle: {
|
|
7453
|
+
+ backgroundColor: theme.colors.background
|
|
7454
|
+
+ },
|
|
6901
7455
|
contentStyle: {
|
|
6902
7456
|
backgroundColor: theme.colors.background
|
|
6903
7457
|
}
|
|
@@ -6935,6 +7489,8 @@ That’s all for theme settings screen!
|
|
|
6935
7489
|
|
|
6936
7490
|
### Create Button Component
|
|
6937
7491
|
|
|
7492
|
+
[Section titled “Create Button Component”](#create-button-component)
|
|
7493
|
+
|
|
6938
7494
|
Now let’s learn something new. We will create an animated button component for the accent settings:
|
|
6939
7495
|
|
|
6940
7496
|
components/Button.tsx
|
|
@@ -7013,11 +7569,11 @@ You should be familiar with all the patterns used here: variants, `UnistylesVari
|
|
|
7013
7569
|
|
|
7014
7570
|
components/Button.tsx
|
|
7015
7571
|
|
|
7016
|
-
```
|
|
7572
|
+
```diff
|
|
7017
7573
|
import { Pressable } from 'react-native'
|
|
7018
7574
|
import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated'
|
|
7019
7575
|
import { StyleSheet, UnistylesVariants } from 'react-native-unistyles'
|
|
7020
|
-
import { useAnimatedVariantColor } from 'react-native-unistyles/reanimated'
|
|
7576
|
+
+import { useAnimatedVariantColor } from 'react-native-unistyles/reanimated'
|
|
7021
7577
|
import { ThemedText } from './ThemedText'
|
|
7022
7578
|
|
|
7023
7579
|
|
|
@@ -7037,18 +7593,18 @@ export const Button: React.FunctionComponent<ButtonProps> = ({
|
|
|
7037
7593
|
})
|
|
7038
7594
|
|
|
7039
7595
|
|
|
7040
|
-
const color = useAnimatedVariantColor(style.buttonColor, 'backgroundColor')
|
|
7041
|
-
const animatedStyle = useAnimatedStyle(() => ({
|
|
7596
|
+
+const color = useAnimatedVariantColor(style.buttonColor, 'backgroundColor')
|
|
7597
|
+
+const animatedStyle = useAnimatedStyle(() => ({
|
|
7042
7598
|
backgroundColor: withTiming(color.value, {
|
|
7043
7599
|
duration: 500
|
|
7044
|
-
})
|
|
7045
|
-
}))
|
|
7600
|
+
+})
|
|
7601
|
+
+}))
|
|
7046
7602
|
|
|
7047
7603
|
|
|
7048
7604
|
return (
|
|
7049
7605
|
<Pressable onPress={onPress}>
|
|
7050
|
-
|
|
7051
|
-
|
|
7606
|
+
-<Animated.View style={style.button}>
|
|
7607
|
+
+<Animated.View style={[animatedStyle, style.button]}>
|
|
7052
7608
|
<ThemedText bold>
|
|
7053
7609
|
{label}
|
|
7054
7610
|
</ThemedText>
|
|
@@ -7075,6 +7631,8 @@ For a comprehensive explanation, please refer to our dedicated guide on [Merging
|
|
|
7075
7631
|
|
|
7076
7632
|
### Build the Accent Settings Modal
|
|
7077
7633
|
|
|
7634
|
+
[Section titled “Build the Accent Settings Modal”](#build-the-accent-settings-modal)
|
|
7635
|
+
|
|
7078
7636
|
Let’s create the final modal screen for accent selection and animate the accent selection:
|
|
7079
7637
|
|
|
7080
7638
|
app/settings/settings-accent.tsx
|
|
@@ -7203,27 +7761,29 @@ Our application will feature three primary screens, with the Settings screen als
|
|
|
7203
7761
|
|
|
7204
7762
|
### PlaylistScreen
|
|
7205
7763
|
|
|
7764
|
+
[Section titled “PlaylistScreen”](#playlistscreen)
|
|
7765
|
+
|
|
7206
7766
|
First, let’s repurpose the existing `app/(tabs)/index.tsx` file to become our `PlaylistScreen`. This involves changing the component name from `HomeScreen` to `PlaylistScreen` and wrapping the content in a `ScrollView`:
|
|
7207
7767
|
|
|
7208
7768
|
app/(tabs)/index.tsx
|
|
7209
7769
|
|
|
7210
|
-
```
|
|
7211
|
-
import { ScrollView } from 'react-native';
|
|
7770
|
+
```diff
|
|
7771
|
+
+import { ScrollView } from 'react-native';
|
|
7212
7772
|
import { ThemedText } from '@/components/ThemedText'
|
|
7213
7773
|
import { ThemedView } from '@/components/ThemedView'
|
|
7214
7774
|
import { StyleSheet } from 'react-native-unistyles'
|
|
7215
7775
|
|
|
7216
7776
|
|
|
7217
|
-
export default function PlaylistScreen() {
|
|
7218
|
-
export default function HomeScreen() {
|
|
7777
|
+
+export default function PlaylistScreen() {
|
|
7778
|
+
-export default function HomeScreen() {
|
|
7219
7779
|
return (
|
|
7220
|
-
|
|
7221
|
-
|
|
7780
|
+
+<ScrollView contentContainerStyle={styles.container}>
|
|
7781
|
+
-<ThemedView style={styles.container}>
|
|
7222
7782
|
<ThemedText type="title">
|
|
7223
7783
|
Home Screen
|
|
7224
7784
|
</ThemedText>
|
|
7225
|
-
|
|
7226
|
-
|
|
7785
|
+
-</ThemedView>
|
|
7786
|
+
+</ScrollView>
|
|
7227
7787
|
);
|
|
7228
7788
|
}
|
|
7229
7789
|
|
|
@@ -7241,7 +7801,7 @@ Next, let’s update the title and adjust the container styles. We’ll remove t
|
|
|
7241
7801
|
|
|
7242
7802
|
app/(tabs)/index.tsx
|
|
7243
7803
|
|
|
7244
|
-
```
|
|
7804
|
+
```diff
|
|
7245
7805
|
import { ThemedText } from '@/components/ThemedText'
|
|
7246
7806
|
import { ScrollView } from 'react-native'
|
|
7247
7807
|
import { StyleSheet } from 'react-native-unistyles'
|
|
@@ -7251,8 +7811,8 @@ export default function PlaylistScreen() {
|
|
|
7251
7811
|
return (
|
|
7252
7812
|
<ScrollView contentContainerStyle={styles.container}>
|
|
7253
7813
|
<ThemedText type="title">
|
|
7254
|
-
Home Screen
|
|
7255
|
-
Playlist
|
|
7814
|
+
- Home Screen
|
|
7815
|
+
+ Playlist
|
|
7256
7816
|
</ThemedText>
|
|
7257
7817
|
</ScrollView>
|
|
7258
7818
|
);
|
|
@@ -7286,8 +7846,8 @@ Let’s use `rt.insets.top` to add a top margin to our container, pushing the co
|
|
|
7286
7846
|
|
|
7287
7847
|
app/(tabs)/index.tsx
|
|
7288
7848
|
|
|
7289
|
-
```
|
|
7290
|
-
const styles = StyleSheet.create((theme, rt) => ({
|
|
7849
|
+
```diff
|
|
7850
|
+
+const styles = StyleSheet.create((theme, rt) => ({
|
|
7291
7851
|
const styles = StyleSheet.create(theme => ({
|
|
7292
7852
|
container: {
|
|
7293
7853
|
flex: 1,
|
|
@@ -7300,8 +7860,8 @@ To give our content some breathing room, let’s also add horizontal padding usi
|
|
|
7300
7860
|
|
|
7301
7861
|
app/(tabs)/index.tsx
|
|
7302
7862
|
|
|
7303
|
-
```
|
|
7304
|
-
const styles = StyleSheet.create((theme, rt) => ({
|
|
7863
|
+
```diff
|
|
7864
|
+
+const styles = StyleSheet.create((theme, rt) => ({
|
|
7305
7865
|
container: {
|
|
7306
7866
|
flex: 1,
|
|
7307
7867
|
marginTop: rt.insets.top,
|
|
@@ -7312,6 +7872,8 @@ app/(tabs)/index.tsx
|
|
|
7312
7872
|
|
|
7313
7873
|
### PlayerScreen
|
|
7314
7874
|
|
|
7875
|
+
[Section titled “PlayerScreen”](#playerscreen)
|
|
7876
|
+
|
|
7315
7877
|
For the `PlayerScreen`, we’ll start by creating a new file structure and then use a modified version of our `PlaylistScreen` code.
|
|
7316
7878
|
|
|
7317
7879
|
Expo Router uses a file-based routing system. To create a dynamic route for our player, create the following folder and file:
|
|
@@ -7368,6 +7930,8 @@ Don’t worry about the TabBar icons and routing just yet - we’ll fix that soo
|
|
|
7368
7930
|
|
|
7369
7931
|
### SettingsScreen
|
|
7370
7932
|
|
|
7933
|
+
[Section titled “SettingsScreen”](#settingsscreen)
|
|
7934
|
+
|
|
7371
7935
|
The `SettingsScreen` is our final main screen. It will serve as a hub for navigating to the modal screens where users can change the app’s theme and accent color.
|
|
7372
7936
|
|
|
7373
7937
|
First, set up the required files and folders:
|
|
@@ -7399,6 +7963,8 @@ This is a standard Expo Router stack layout. Let’s add the code for each of th
|
|
|
7399
7963
|
|
|
7400
7964
|
#### 1. Configure the Stack Navigator (\_layout.tsx)
|
|
7401
7965
|
|
|
7966
|
+
[Section titled “1. Configure the Stack Navigator (\_layout.tsx)”](#1-configure-the-stack-navigator-_layouttsx)
|
|
7967
|
+
|
|
7402
7968
|
This file configures the stack navigator for the settings section, defining the main screen and the two modal screens.
|
|
7403
7969
|
|
|
7404
7970
|
app/settings/\_layout.tsx
|
|
@@ -7451,6 +8017,8 @@ export default function SettingsLayout() {
|
|
|
7451
8017
|
|
|
7452
8018
|
#### 2. Create the Main Settings Screen (index.tsx)
|
|
7453
8019
|
|
|
8020
|
+
[Section titled “2. Create the Main Settings Screen (index.tsx)”](#2-create-the-main-settings-screen-indextsx)
|
|
8021
|
+
|
|
7454
8022
|
This is the main `SettingsScreen`. The code is almost identical to our other screens for now.
|
|
7455
8023
|
|
|
7456
8024
|
app/settings/index.tsx
|
|
@@ -7483,6 +8051,8 @@ const styles = StyleSheet.create((theme, rt) => ({
|
|
|
7483
8051
|
|
|
7484
8052
|
#### 3. Create the Modal Screens
|
|
7485
8053
|
|
|
8054
|
+
[Section titled “3. Create the Modal Screens”](#3-create-the-modal-screens)
|
|
8055
|
+
|
|
7486
8056
|
The modal screens for changing the theme and accent color are also simple placeholders. Notice the component names and titles are updated for each.
|
|
7487
8057
|
|
|
7488
8058
|
app/settings/settings-theme.tsx
|
|
@@ -7545,11 +8115,13 @@ You should now be able to navigate to all three screens, each with a different t
|
|
|
7545
8115
|
|
|
7546
8116
|
### TabBar
|
|
7547
8117
|
|
|
8118
|
+
[Section titled “TabBar”](#tabbar)
|
|
8119
|
+
|
|
7548
8120
|
Currently, `TabBar` doesn’t reflect our new screen structure. Let’s update `app/(tabs)/_layout.tsx` to correctly register our routes and assign new icons.
|
|
7549
8121
|
|
|
7550
8122
|
app/(tabs)/\_layout.tsx
|
|
7551
8123
|
|
|
7552
|
-
```
|
|
8124
|
+
```diff
|
|
7553
8125
|
import { IconSymbol } from '@/components/ui/IconSymbol'
|
|
7554
8126
|
import { Tabs } from 'expo-router'
|
|
7555
8127
|
import React from 'react'
|
|
@@ -7576,27 +8148,27 @@ export default function TabLayout() {
|
|
|
7576
8148
|
<Tabs.Screen
|
|
7577
8149
|
name="index"
|
|
7578
8150
|
options={{
|
|
7579
|
-
title: 'Home',
|
|
7580
|
-
title: 'Playlist',
|
|
7581
|
-
tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />,
|
|
7582
|
-
tabBarIcon: ({ color }) => <IconSymbol size={24} name="music.house" color={color} />,
|
|
8151
|
+
- title: 'Home',
|
|
8152
|
+
+ title: 'Playlist',
|
|
8153
|
+
-tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />,
|
|
8154
|
+
+tabBarIcon: ({ color }) => <IconSymbol size={24} name="music.house" color={color} />,
|
|
7583
8155
|
}}
|
|
7584
8156
|
/>
|
|
8157
|
+
+<Tabs.Screen
|
|
8158
|
+
+name="player/[songId]"
|
|
8159
|
+
+options={{
|
|
8160
|
+
+ title: 'Player',
|
|
8161
|
+
+tabBarIcon: ({ color }) => <IconSymbol size={24} name="play.circle" color={color} />,
|
|
8162
|
+
+ }}
|
|
8163
|
+
+ />
|
|
7585
8164
|
<Tabs.Screen
|
|
7586
|
-
name="
|
|
8165
|
+
-name="explore"
|
|
8166
|
+
+name="settings"
|
|
7587
8167
|
options={{
|
|
7588
|
-
title: '
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
<Tabs.Screen
|
|
7593
|
-
name="explore"
|
|
7594
|
-
name="settings"
|
|
7595
|
-
options={{
|
|
7596
|
-
title: 'Explore',
|
|
7597
|
-
title: 'Settings',
|
|
7598
|
-
tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />,
|
|
7599
|
-
tabBarIcon: ({ color }) => <IconSymbol size={24} name="gear.circle" color={color} />,
|
|
8168
|
+
- title: 'Explore',
|
|
8169
|
+
+ title: 'Settings',
|
|
8170
|
+
-tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />,
|
|
8171
|
+
+tabBarIcon: ({ color }) => <IconSymbol size={24} name="gear.circle" color={color} />,
|
|
7600
8172
|
}}
|
|
7601
8173
|
/>
|
|
7602
8174
|
</Tabs>
|
|
@@ -7626,12 +8198,16 @@ By the end of this part, you’ll have a functional music app that displays a li
|
|
|
7626
8198
|
|
|
7627
8199
|
### Setting Up Types and Mock Data
|
|
7628
8200
|
|
|
8201
|
+
[Section titled “Setting Up Types and Mock Data”](#setting-up-types-and-mock-data)
|
|
8202
|
+
|
|
7629
8203
|
Before we can build our screens, we need to establish the data structure for our songs. This step is crucial for maintaining type safety throughout our application and ensuring that our components receive the expected data format.
|
|
7630
8204
|
|
|
7631
8205
|
Let’s start by creating the fundamental types that will power our music app.
|
|
7632
8206
|
|
|
7633
8207
|
#### Create Song Types
|
|
7634
8208
|
|
|
8209
|
+
[Section titled “Create Song Types”](#create-song-types)
|
|
8210
|
+
|
|
7635
8211
|
First, we’ll define what a song looks like in our application. Each song needs essential information like title, genre, cover image, and duration.
|
|
7636
8212
|
|
|
7637
8213
|
Create `types/song.ts`:
|
|
@@ -7663,6 +8239,8 @@ export * from './song'
|
|
|
7663
8239
|
|
|
7664
8240
|
#### Create Mock Playlist Data
|
|
7665
8241
|
|
|
8242
|
+
[Section titled “Create Mock Playlist Data”](#create-mock-playlist-data)
|
|
8243
|
+
|
|
7666
8244
|
For this tutorial, we’ll use a curated list of 20 fictional songs with diverse genres to showcase our app’s capabilities. In a real application, this data would come from an API or music service.
|
|
7667
8245
|
|
|
7668
8246
|
Create `mocks/playlist.ts`:
|
|
@@ -7831,6 +8409,8 @@ This gives us clean access to our mock data throughout the application with simp
|
|
|
7831
8409
|
|
|
7832
8410
|
### Building the Playlist Screen
|
|
7833
8411
|
|
|
8412
|
+
[Section titled “Building the Playlist Screen”](#building-the-playlist-screen)
|
|
8413
|
+
|
|
7834
8414
|
Now that we have our data structure in place, let’s transform the placeholder home screen into a proper playlist that displays our collection of songs. The playlist will serve as the main entry point where users can browse and select tracks.
|
|
7835
8415
|
|
|
7836
8416
|
Currently, our home screen at `app/(tabs)/index.tsx` just displays a simple title. We need to replace this with a scrollable list of songs that users can interact with.
|
|
@@ -7839,63 +8419,63 @@ Let’s update the playlist screen to display our songs:
|
|
|
7839
8419
|
|
|
7840
8420
|
app/(tabs)/index.tsx
|
|
7841
8421
|
|
|
7842
|
-
```
|
|
7843
|
-
import { SongTile } from '@/components/SongTile'
|
|
8422
|
+
```diff
|
|
8423
|
+
+import { SongTile } from '@/components/SongTile'
|
|
7844
8424
|
import { ThemedText } from '@/components/ThemedText'
|
|
7845
|
-
import { playlist } from '@/mocks'
|
|
7846
|
-
import { router } from 'expo-router'
|
|
7847
|
-
import { ScrollView, View } from 'react-native'
|
|
7848
|
-
import { ScrollView } from 'react-native'
|
|
8425
|
+
+import { playlist } from '@/mocks'
|
|
8426
|
+
+import { router } from 'expo-router'
|
|
8427
|
+
+import { ScrollView, View } from 'react-native'
|
|
8428
|
+
-import { ScrollView } from 'react-native'
|
|
7849
8429
|
import { StyleSheet } from 'react-native-unistyles'
|
|
7850
8430
|
|
|
7851
8431
|
|
|
7852
8432
|
export default function PlaylistScreen() {
|
|
7853
8433
|
return (
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
Playlist
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
{playlist.map(song => (
|
|
7862
|
-
|
|
7863
|
-
song={song}
|
|
7864
|
-
onPress={() => router.push(`/(tabs)/player/${song.id}`)}
|
|
7865
|
-
key={song.id}
|
|
7866
|
-
/>
|
|
7867
|
-
))}
|
|
7868
|
-
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
Playlist
|
|
7873
|
-
|
|
7874
|
-
|
|
8434
|
+
+<View style={styles.container}>
|
|
8435
|
+
+<ScrollView contentContainerStyle={styles.contentContainer}>
|
|
8436
|
+
+<View style={styles.header}>
|
|
8437
|
+
+<ThemedText type="title">
|
|
8438
|
+
+ Playlist
|
|
8439
|
+
+</ThemedText>
|
|
8440
|
+
+</View>
|
|
8441
|
+
+{playlist.map(song => (
|
|
8442
|
+
+<SongTile
|
|
8443
|
+
+song={song}
|
|
8444
|
+
+onPress={() => router.push(`/(tabs)/player/${song.id}`)}
|
|
8445
|
+
+key={song.id}
|
|
8446
|
+
+ />
|
|
8447
|
+
+ ))}
|
|
8448
|
+
+</ScrollView>
|
|
8449
|
+
+</View>
|
|
8450
|
+
-<ScrollView contentContainerStyle={styles.container}>
|
|
8451
|
+
-<ThemedText type="title">
|
|
8452
|
+
- Playlist
|
|
8453
|
+
-</ThemedText>
|
|
8454
|
+
-</ScrollView>
|
|
7875
8455
|
);
|
|
7876
8456
|
}
|
|
7877
8457
|
|
|
7878
8458
|
|
|
7879
|
-
const styles = StyleSheet.create((theme, rt) => ({
|
|
8459
|
+
+const styles = StyleSheet.create((theme, rt) => ({
|
|
7880
8460
|
container: {
|
|
7881
8461
|
marginTop: rt.insets.top + theme.gap(3),
|
|
7882
8462
|
backgroundColor: theme.colors.background
|
|
7883
|
-
},
|
|
8463
|
+
+},
|
|
7884
8464
|
contentContainer: {
|
|
7885
8465
|
gap: theme.gap(3),
|
|
7886
8466
|
paddingHorizontal: theme.gap(2)
|
|
7887
|
-
},
|
|
8467
|
+
+},
|
|
7888
8468
|
header: {
|
|
7889
8469
|
paddingBottom: theme.gap(2)
|
|
7890
|
-
}
|
|
7891
|
-
}));
|
|
7892
|
-
const styles = StyleSheet.create((theme, rt) => ({
|
|
8470
|
+
+}
|
|
8471
|
+
+}));
|
|
8472
|
+
-const styles = StyleSheet.create((theme, rt) => ({
|
|
7893
8473
|
container: {
|
|
7894
8474
|
flex: 1,
|
|
7895
8475
|
marginTop: rt.insets.top,
|
|
7896
8476
|
paddingHorizontal: theme.gap(2)
|
|
7897
|
-
},
|
|
7898
|
-
}));
|
|
8477
|
+
-},
|
|
8478
|
+
-}));
|
|
7899
8479
|
```
|
|
7900
8480
|
|
|
7901
8481
|
Here’s what we’ve changed and why:
|
|
@@ -7912,6 +8492,8 @@ The key difference is that we’re now structuring our app to handle a list of d
|
|
|
7912
8492
|
|
|
7913
8493
|
### Creating the SongTile Component
|
|
7914
8494
|
|
|
8495
|
+
[Section titled “Creating the SongTile Component”](#creating-the-songtile-component)
|
|
8496
|
+
|
|
7915
8497
|
The `SongTile` component will be responsible for displaying individual song information in an attractive, tappable format. This component needs to show the song’s cover art, title, genre, and duration in a clean layout.
|
|
7916
8498
|
|
|
7917
8499
|
Before our playlist screen can work, we need to create the `SongTile` component that will display each song:
|
|
@@ -7979,101 +8561,103 @@ We now have a fully functional playlist screen that displays our 20 mock songs i
|
|
|
7979
8561
|
|
|
7980
8562
|
### Building the Player Screen
|
|
7981
8563
|
|
|
8564
|
+
[Section titled “Building the Player Screen”](#building-the-player-screen)
|
|
8565
|
+
|
|
7982
8566
|
The player screen needs to handle dynamic routing, where the song ID comes from the URL parameter. This screen will display detailed information about the selected song and provide playback controls.
|
|
7983
8567
|
|
|
7984
8568
|
Let’s update our player screen at `app/(tabs)/player/[songId].tsx` to handle the dynamic song data:
|
|
7985
8569
|
|
|
7986
8570
|
app/(tabs)/player/\[songId].tsx
|
|
7987
8571
|
|
|
7988
|
-
```
|
|
7989
|
-
import { Button } from '@/components/Button'
|
|
7990
|
-
import { PlayerControls } from '@/components/PlayerControls'
|
|
8572
|
+
```diff
|
|
8573
|
+
+import { Button } from '@/components/Button'
|
|
8574
|
+
+import { PlayerControls } from '@/components/PlayerControls'
|
|
7991
8575
|
import { ThemedText } from '@/components/ThemedText'
|
|
7992
|
-
import { ThemedView } from '@/components/ThemedView'
|
|
7993
|
-
import { playlist } from '@/mocks'
|
|
7994
|
-
import { router, useLocalSearchParams } from 'expo-router'
|
|
7995
|
-
import { Image, ScrollView } from 'react-native'
|
|
7996
|
-
import { ScrollView } from 'react-native'
|
|
8576
|
+
+import { ThemedView } from '@/components/ThemedView'
|
|
8577
|
+
+import { playlist } from '@/mocks'
|
|
8578
|
+
+import { router, useLocalSearchParams } from 'expo-router'
|
|
8579
|
+
+import { Image, ScrollView } from 'react-native'
|
|
8580
|
+
-import { ScrollView } from 'react-native'
|
|
7997
8581
|
import { StyleSheet } from 'react-native-unistyles'
|
|
7998
8582
|
|
|
7999
8583
|
|
|
8000
8584
|
export default function PlayerScreen() {
|
|
8001
|
-
const { songId } = useLocalSearchParams()
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
const song = playlist.find(song => song.id === Number(songId))
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
if (!songId || !song) {
|
|
8008
|
-
return (
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
Looking for inspiration?
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
Pick a song from the playlist
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
label="Pick a song"
|
|
8018
|
-
accent="banana"
|
|
8019
|
-
onPress={() => router.replace('/')}
|
|
8020
|
-
/>
|
|
8021
|
-
|
|
8022
|
-
)
|
|
8023
|
-
}
|
|
8585
|
+
+const { songId } = useLocalSearchParams()
|
|
8586
|
+
|
|
8587
|
+
|
|
8588
|
+
+const song = playlist.find(song => song.id === Number(songId))
|
|
8589
|
+
|
|
8590
|
+
|
|
8591
|
+
+if (!songId || !song) {
|
|
8592
|
+
+return (
|
|
8593
|
+
+<ThemedView style={[styles.centerContainer, styles.container]}>
|
|
8594
|
+
+<ThemedText type="title">
|
|
8595
|
+
+ Looking for inspiration?
|
|
8596
|
+
+</ThemedText>
|
|
8597
|
+
+<ThemedText>
|
|
8598
|
+
+ Pick a song from the playlist
|
|
8599
|
+
+</ThemedText>
|
|
8600
|
+
+<Button
|
|
8601
|
+
+label="Pick a song"
|
|
8602
|
+
+accent="banana"
|
|
8603
|
+
+onPress={() => router.replace('/')}
|
|
8604
|
+
+ />
|
|
8605
|
+
+</ThemedView>
|
|
8606
|
+
+ )
|
|
8607
|
+
+ }
|
|
8024
8608
|
|
|
8025
8609
|
|
|
8026
8610
|
return (
|
|
8027
|
-
|
|
8028
|
-
|
|
8029
|
-
source={{ uri: song.imageUrl }}
|
|
8030
|
-
style={styles.image}
|
|
8031
|
-
/>
|
|
8032
|
-
|
|
8033
|
-
{song.title}
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
{song.genre}
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
{song.duration}
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
Player
|
|
8046
|
-
|
|
8047
|
-
|
|
8611
|
+
+<ScrollView contentContainerStyle={styles.container}>
|
|
8612
|
+
+<Image
|
|
8613
|
+
+source={{ uri: song.imageUrl }}
|
|
8614
|
+
+style={styles.image}
|
|
8615
|
+
+ />
|
|
8616
|
+
+<ThemedText type="title">
|
|
8617
|
+
+{song.title}
|
|
8618
|
+
+</ThemedText>
|
|
8619
|
+
+<ThemedText dimmed type="subtitle">
|
|
8620
|
+
+{song.genre}
|
|
8621
|
+
+</ThemedText>
|
|
8622
|
+
+<ThemedText>
|
|
8623
|
+
+{song.duration}
|
|
8624
|
+
+</ThemedText>
|
|
8625
|
+
+<PlayerControls />
|
|
8626
|
+
+</ScrollView>
|
|
8627
|
+
-<ScrollView contentContainerStyle={styles.container}>
|
|
8628
|
+
-<ThemedText type="title">
|
|
8629
|
+
- Player
|
|
8630
|
+
-</ThemedText>
|
|
8631
|
+
-</ScrollView>
|
|
8048
8632
|
);
|
|
8049
8633
|
}
|
|
8050
8634
|
|
|
8051
8635
|
|
|
8052
|
-
const styles = StyleSheet.create((theme, rt) => ({
|
|
8636
|
+
+const styles = StyleSheet.create((theme, rt) => ({
|
|
8053
8637
|
centerContainer: {
|
|
8054
8638
|
flex: 1,
|
|
8055
8639
|
justifyContent: 'center',
|
|
8056
8640
|
alignItems: 'center'
|
|
8057
|
-
},
|
|
8641
|
+
+},
|
|
8058
8642
|
container: {
|
|
8059
8643
|
flex: 1,
|
|
8060
8644
|
gap: theme.gap(2),
|
|
8061
8645
|
alignItems: 'center',
|
|
8062
8646
|
marginTop: rt.insets.top + theme.gap(3),
|
|
8063
|
-
},
|
|
8647
|
+
+},
|
|
8064
8648
|
image: {
|
|
8065
8649
|
width: 200,
|
|
8066
8650
|
height: 200,
|
|
8067
8651
|
borderRadius: theme.gap(2)
|
|
8068
|
-
}
|
|
8069
|
-
}));
|
|
8070
|
-
const styles = StyleSheet.create((theme, rt) => ({
|
|
8652
|
+
+}
|
|
8653
|
+
+}));
|
|
8654
|
+
-const styles = StyleSheet.create((theme, rt) => ({
|
|
8071
8655
|
container: {
|
|
8072
8656
|
flex: 1,
|
|
8073
8657
|
marginTop: rt.insets.top,
|
|
8074
8658
|
paddingHorizontal: theme.gap(2)
|
|
8075
|
-
},
|
|
8076
|
-
}));
|
|
8659
|
+
-},
|
|
8660
|
+
-}));
|
|
8077
8661
|
```
|
|
8078
8662
|
|
|
8079
8663
|
We use `useLocalSearchParams()` to extract the `songId` from the URL. This allows users to navigate directly to any song or bookmark specific tracks.
|
|
@@ -8084,6 +8668,8 @@ Our screen still needs one more component to be complete - the `PlayerControls`.
|
|
|
8084
8668
|
|
|
8085
8669
|
### Creating the PlayerControls Component
|
|
8086
8670
|
|
|
8671
|
+
[Section titled “Creating the PlayerControls Component”](#creating-the-playercontrols-component)
|
|
8672
|
+
|
|
8087
8673
|
The player controls provide the interface for music playback. While they won’t actually play music in this tutorial, they give users the familiar media controls they expect in a music app.
|
|
8088
8674
|
|
|
8089
8675
|
Let’s create the final component for our player interface:
|
|
@@ -8160,6 +8746,8 @@ Our settings screen will feature interactive tiles that users can tap to modify
|
|
|
8160
8746
|
|
|
8161
8747
|
### Create the SettingTile Component
|
|
8162
8748
|
|
|
8749
|
+
[Section titled “Create the SettingTile Component”](#create-the-settingtile-component)
|
|
8750
|
+
|
|
8163
8751
|
Let’s start by creating a reusable `SettingTile` component. This component will demonstrate one of Unistyles’ coolest features: zero-config integration with `Pressable` and `PressableStateCallbackType`.
|
|
8164
8752
|
|
|
8165
8753
|
Create a new file `components/SettingTile.tsx`:
|
|
@@ -8223,13 +8811,15 @@ When you press the tile, the opacity changes from `1` to `0.75`, giving users im
|
|
|
8223
8811
|
|
|
8224
8812
|
### Enhance ThemedText with Variants
|
|
8225
8813
|
|
|
8814
|
+
[Section titled “Enhance ThemedText with Variants”](#enhance-themedtext-with-variants)
|
|
8815
|
+
|
|
8226
8816
|
You might have noticed we’re using `bold` and `dimmed` props on `ThemedText` that don’t exist yet. Let’s add them using Unistyles variants.
|
|
8227
8817
|
|
|
8228
8818
|
Update your `ThemedText` component:
|
|
8229
8819
|
|
|
8230
8820
|
components/ThemedText.tsx
|
|
8231
8821
|
|
|
8232
|
-
```
|
|
8822
|
+
```diff
|
|
8233
8823
|
import { Text, type TextProps } from 'react-native'
|
|
8234
8824
|
import { StyleSheet, type UnistylesVariants } from 'react-native-unistyles'
|
|
8235
8825
|
|
|
@@ -8240,14 +8830,14 @@ export type ThemedTextProps = TextProps & UnistylesVariants<typeof styles>
|
|
|
8240
8830
|
export function ThemedText({
|
|
8241
8831
|
style,
|
|
8242
8832
|
type,
|
|
8243
|
-
bold,
|
|
8244
|
-
dimmed,
|
|
8833
|
+
+bold,
|
|
8834
|
+
+dimmed,
|
|
8245
8835
|
...rest
|
|
8246
8836
|
}: ThemedTextProps) {
|
|
8247
8837
|
styles.useVariants({
|
|
8248
8838
|
type,
|
|
8249
|
-
bold,
|
|
8250
|
-
dimmed
|
|
8839
|
+
+ bold,
|
|
8840
|
+
+ dimmed
|
|
8251
8841
|
})
|
|
8252
8842
|
|
|
8253
8843
|
|
|
@@ -8292,13 +8882,13 @@ const styles = StyleSheet.create(theme => ({
|
|
|
8292
8882
|
bold: {
|
|
8293
8883
|
true: {
|
|
8294
8884
|
fontWeight: 'bold',
|
|
8295
|
-
}
|
|
8296
|
-
},
|
|
8885
|
+
+}
|
|
8886
|
+
+},
|
|
8297
8887
|
dimmed: {
|
|
8298
8888
|
true: {
|
|
8299
8889
|
color: theme.colors.tint
|
|
8300
|
-
}
|
|
8301
|
-
}
|
|
8890
|
+
+}
|
|
8891
|
+
+}
|
|
8302
8892
|
}
|
|
8303
8893
|
}
|
|
8304
8894
|
}));
|
|
@@ -8310,73 +8900,77 @@ This pattern makes your components more readable and eliminates the need for mul
|
|
|
8310
8900
|
|
|
8311
8901
|
### Build the Settings Interface
|
|
8312
8902
|
|
|
8903
|
+
[Section titled “Build the Settings Interface”](#build-the-settings-interface)
|
|
8904
|
+
|
|
8313
8905
|
Now let’s implement the actual settings screen with our new `SettingTile` component.
|
|
8314
8906
|
|
|
8315
8907
|
Update your `app/settings/index.tsx`:
|
|
8316
8908
|
|
|
8317
8909
|
app/settings/index.tsx
|
|
8318
8910
|
|
|
8319
|
-
```
|
|
8320
|
-
import { SettingTile } from '@/components/SettingTile'
|
|
8911
|
+
```diff
|
|
8912
|
+
+import { SettingTile } from '@/components/SettingTile'
|
|
8321
8913
|
import { ThemedText } from '@/components/ThemedText'
|
|
8322
|
-
import { ScrollView, View } from 'react-native'
|
|
8323
|
-
import { StyleSheet, UnistylesRuntime } from 'react-native-unistyles'
|
|
8324
|
-
import { ScrollView } from 'react-native'
|
|
8325
|
-
import { StyleSheet } from 'react-native-unistyles'
|
|
8914
|
+
+import { ScrollView, View } from 'react-native'
|
|
8915
|
+
+import { StyleSheet, UnistylesRuntime } from 'react-native-unistyles'
|
|
8916
|
+
-import { ScrollView } from 'react-native'
|
|
8917
|
+
-import { StyleSheet } from 'react-native-unistyles'
|
|
8326
8918
|
|
|
8327
8919
|
|
|
8328
8920
|
export default function SettingsScreen() {
|
|
8329
|
-
const systemTheme = UnistylesRuntime.hasAdaptiveThemes
|
|
8921
|
+
+const systemTheme = UnistylesRuntime.hasAdaptiveThemes
|
|
8330
8922
|
|
|
8331
8923
|
|
|
8332
8924
|
return (
|
|
8333
|
-
|
|
8334
|
-
|
|
8925
|
+
-<ScrollView contentContainerStyle={styles.container}>
|
|
8926
|
+
+<ScrollView contentContainerStyle={styles.scrollView}>
|
|
8335
8927
|
<ThemedText type="title">
|
|
8336
|
-
Settings
|
|
8337
|
-
Appearance
|
|
8928
|
+
- Settings
|
|
8929
|
+
+ Appearance
|
|
8338
8930
|
</ThemedText>
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
settingName="Theme"
|
|
8342
|
-
selectedValue="Light"
|
|
8343
|
-
description={systemTheme ? 'System' : 'User'}
|
|
8344
|
-
onPress={() => {}}
|
|
8345
|
-
/>
|
|
8346
|
-
|
|
8347
|
-
settingName="App accent"
|
|
8348
|
-
selectedValue="Default"
|
|
8349
|
-
description="Primary app color"
|
|
8350
|
-
onPress={() => {}}
|
|
8351
|
-
/>
|
|
8352
|
-
|
|
8931
|
+
+<View style={styles.settingsContainer}>
|
|
8932
|
+
+<SettingTile
|
|
8933
|
+
+settingName="Theme"
|
|
8934
|
+
+selectedValue="Light"
|
|
8935
|
+
+description={systemTheme ? 'System' : 'User'}
|
|
8936
|
+
+onPress={() => {}}
|
|
8937
|
+
+ />
|
|
8938
|
+
+<SettingTile
|
|
8939
|
+
+settingName="App accent"
|
|
8940
|
+
+selectedValue="Default"
|
|
8941
|
+
+description="Primary app color"
|
|
8942
|
+
+onPress={() => {}}
|
|
8943
|
+
+ />
|
|
8944
|
+
+</View>
|
|
8353
8945
|
</ScrollView>
|
|
8354
8946
|
);
|
|
8355
8947
|
}
|
|
8356
8948
|
|
|
8357
8949
|
|
|
8358
|
-
const styles = StyleSheet.create((theme, rt) => ({
|
|
8359
|
-
scrollView: {
|
|
8360
|
-
marginTop: rt.insets.top + theme.gap(3),
|
|
8361
|
-
backgroundColor: theme.colors.background,
|
|
8362
|
-
paddingHorizontal: theme.gap(2)
|
|
8363
|
-
},
|
|
8364
|
-
settingsContainer: {
|
|
8365
|
-
marginTop: theme.gap(4),
|
|
8366
|
-
gap: theme.gap(4)
|
|
8367
|
-
},
|
|
8368
|
-
}));
|
|
8369
|
-
const styles = StyleSheet.create((theme, rt) => ({
|
|
8370
|
-
container: {
|
|
8371
|
-
flex: 1,
|
|
8372
|
-
marginTop: rt.insets.top,
|
|
8373
|
-
paddingHorizontal: theme.gap(2)
|
|
8374
|
-
},
|
|
8375
|
-
}));
|
|
8950
|
+
+ const styles = StyleSheet.create((theme, rt) => ({
|
|
8951
|
+
+ scrollView: {
|
|
8952
|
+
+ marginTop: rt.insets.top + theme.gap(3),
|
|
8953
|
+
+ backgroundColor: theme.colors.background,
|
|
8954
|
+
+ paddingHorizontal: theme.gap(2)
|
|
8955
|
+
+ },
|
|
8956
|
+
+ settingsContainer: {
|
|
8957
|
+
+ marginTop: theme.gap(4),
|
|
8958
|
+
+ gap: theme.gap(4)
|
|
8959
|
+
+ },
|
|
8960
|
+
+}));
|
|
8961
|
+
- const styles = StyleSheet.create((theme, rt) => ({
|
|
8962
|
+
- container: {
|
|
8963
|
+
- flex: 1,
|
|
8964
|
+
- marginTop: rt.insets.top,
|
|
8965
|
+
- paddingHorizontal: theme.gap(2)
|
|
8966
|
+
- },
|
|
8967
|
+
-}));
|
|
8376
8968
|
```
|
|
8377
8969
|
|
|
8378
8970
|
### UnistylesRuntime vs rt Object
|
|
8379
8971
|
|
|
8972
|
+
[Section titled “UnistylesRuntime vs rt Object”](#unistylesruntime-vs-rt-object)
|
|
8973
|
+
|
|
8380
8974
|
Here’s where things get interesting. Notice we’re using `UnistylesRuntime.hasAdaptiveThemes` instead of accessing it through `rt`.
|
|
8381
8975
|
|
|
8382
8976
|
**What’s the difference?**
|
|
@@ -8404,6 +8998,8 @@ Learn more about `UnistylesRuntime` in the [dedicated guide](/v3/references/unis
|
|
|
8404
8998
|
|
|
8405
8999
|
### ScrollView Background Issue
|
|
8406
9000
|
|
|
9001
|
+
[Section titled “ScrollView Background Issue”](#scrollview-background-issue)
|
|
9002
|
+
|
|
8407
9003
|
Try switching between light and dark themes in your app. You’ll notice something odd - the `ScrollView` background color doesn’t update! This is because `contentContainerStyle` is not a regular style prop that Unistyles can automatically track.
|
|
8408
9004
|
|
|
8409
9005
|
For such cases we created `withUnistyles` higher-order component (HOC) that allows you to wrap any component and automatically re-render it, depending on it’s dependencies.
|
|
@@ -8416,15 +9012,15 @@ In order to update background color of `ScrollView`, we need to wrap it with `wi
|
|
|
8416
9012
|
|
|
8417
9013
|
app/settings/index.tsx
|
|
8418
9014
|
|
|
8419
|
-
```
|
|
9015
|
+
```diff
|
|
8420
9016
|
import { SettingTile } from '@/components/SettingTile'
|
|
8421
9017
|
import { ThemedText } from '@/components/ThemedText'
|
|
8422
9018
|
import { ScrollView, View } from 'react-native'
|
|
8423
|
-
import { StyleSheet, UnistylesRuntime } from 'react-native-unistyles'
|
|
8424
|
-
import { StyleSheet, UnistylesRuntime, withUnistyles } from 'react-native-unistyles'
|
|
9019
|
+
-import { StyleSheet, UnistylesRuntime } from 'react-native-unistyles'
|
|
9020
|
+
+import { StyleSheet, UnistylesRuntime, withUnistyles } from 'react-native-unistyles'
|
|
8425
9021
|
|
|
8426
9022
|
|
|
8427
|
-
const StyledScrollView = withUnistyles(ScrollView)
|
|
9023
|
+
+const StyledScrollView = withUnistyles(ScrollView)
|
|
8428
9024
|
|
|
8429
9025
|
|
|
8430
9026
|
export default function SettingsScreen() {
|
|
@@ -8432,8 +9028,8 @@ export default function SettingsScreen() {
|
|
|
8432
9028
|
|
|
8433
9029
|
|
|
8434
9030
|
return (
|
|
8435
|
-
|
|
8436
|
-
|
|
9031
|
+
-<ScrollView contentContainerStyle={styles.scrollView}>
|
|
9032
|
+
+<StyledScrollView contentContainerStyle={styles.scrollView}>
|
|
8437
9033
|
<ThemedText type="title">
|
|
8438
9034
|
Appearance
|
|
8439
9035
|
</ThemedText>
|
|
@@ -8451,8 +9047,8 @@ export default function SettingsScreen() {
|
|
|
8451
9047
|
onPress={() => {}}
|
|
8452
9048
|
/>
|
|
8453
9049
|
</View>
|
|
8454
|
-
|
|
8455
|
-
|
|
9050
|
+
-</ScrollView>
|
|
9051
|
+
+</StyledScrollView>
|
|
8456
9052
|
);
|
|
8457
9053
|
}
|
|
8458
9054
|
|
|
@@ -8479,16 +9075,18 @@ Remember these key points about `withUnistyles`:
|
|
|
8479
9075
|
|
|
8480
9076
|
### Add Modal Navigation
|
|
8481
9077
|
|
|
9078
|
+
[Section titled “Add Modal Navigation”](#add-modal-navigation)
|
|
9079
|
+
|
|
8482
9080
|
Finally, let’s wire up the `onPress` callbacks to navigate to our modal screens:
|
|
8483
9081
|
|
|
8484
9082
|
app/settings/index.tsx
|
|
8485
9083
|
|
|
8486
|
-
```
|
|
9084
|
+
```diff
|
|
8487
9085
|
import { SettingTile } from '@/components/SettingTile'
|
|
8488
9086
|
import { ThemedText } from '@/components/ThemedText'
|
|
8489
9087
|
import { ScrollView, View } from 'react-native'
|
|
8490
9088
|
import { StyleSheet, UnistylesRuntime } from 'react-native-unistyles'
|
|
8491
|
-
import { router } from 'expo-router'
|
|
9089
|
+
+import { router } from 'expo-router'
|
|
8492
9090
|
|
|
8493
9091
|
|
|
8494
9092
|
const StyledScrollView = withUnistyles(ScrollView)
|
|
@@ -8508,15 +9106,15 @@ export default function SettingsScreen() {
|
|
|
8508
9106
|
settingName="Theme"
|
|
8509
9107
|
selectedValue="Light"
|
|
8510
9108
|
description={systemTheme ? "System" : 'User'}
|
|
8511
|
-
onPress={() => {}}
|
|
8512
|
-
onPress={() => router.push('/(tabs)/settings/settings-theme')}
|
|
9109
|
+
-onPress={() => {}}
|
|
9110
|
+
+onPress={() => router.push('/(tabs)/settings/settings-theme')}
|
|
8513
9111
|
/>
|
|
8514
9112
|
<SettingTile
|
|
8515
9113
|
settingName="App accent"
|
|
8516
9114
|
selectedValue="Default"
|
|
8517
9115
|
description="Primary app color"
|
|
8518
|
-
onPress={() => {}}
|
|
8519
|
-
onPress={() => router.push('/(tabs)/settings/settings-accent')}
|
|
9116
|
+
-onPress={() => {}}
|
|
9117
|
+
+onPress={() => router.push('/(tabs)/settings/settings-accent')}
|
|
8520
9118
|
/>
|
|
8521
9119
|
</View>
|
|
8522
9120
|
</StyledScrollView>
|