Skip to content

Commit 3a588b1

Browse files
t0maborokligarski
andauthored
refactor(Tabs): Refactor APIs for icons (#3214)
## Description This PR refactors the way icon resources are passed into components by merging platform-specific props into a single unified icon configuration structure. Previously, icons were defined using 3–4 separate props depending on the platform, which led to inconsistency and potential conflicts. Now, the icon is defined using a single object: ```js { android?: PlatformIconAndroid; ios?: PlatformIconIOS; shared?: PlatformIconShared; } ``` Platform-specific types (`PlatformIconAndroid`, `PlatformIconIOS`, `PlatformIconShared`) are used to define supported icon formats and sources per platform. Note: This PR introduces a new approach to handling SVGs on Android. In #3216, I've started migrating to the Fresco image loading library, which unfortunately does not support SVGs directly. As a result, SVG support will now be provided exclusively through the `drawableResourceAndroid` type. ⚠️ This is a breaking change that may affect developers currently using SVGs via remote URLs or external icon props. Please make sure to update your code accordingly to avoid runtime issues. ## Changes - Updated types for `icon` prop. - Added logic for resolving props internally on Android and adapted the current logic for iOS. - Updated examples to use new APIs. ## Test code and steps to reproduce Verified that the updated `TestBottomTabs` example is showing icons as expected. <img width="590" height="1301" alt="Screenshot 2025-09-15 at 19 30 32" src="https://github.com/user-attachments/assets/b1a1eb8b-babd-4a55-a2fb-2964ade978b0" /> ## Checklist - [x] Included code example that can be used to test this change - [x] Ensured that CI passes --------- Co-authored-by: kligarski <[email protected]>
1 parent 6fc0183 commit 3a588b1

File tree

12 files changed

+225
-145
lines changed

12 files changed

+225
-145
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="512dp"
3+
android:height="512dp"
4+
android:viewportWidth="512"
5+
android:viewportHeight="512">
6+
<path
7+
android:fillColor="#FF000000"
8+
android:pathData="M490.13,185.47L338.97,34.3c-45.85,-45.74 -120.08,-45.74 -165.93,0L21.87,185.47C7.82,199.45 -0.05,218.46 0,238.27v221.4C0.05,488.57 23.48,511.98 52.37,512h407.25c28.9,-0.02 52.33,-23.43 52.37,-52.33V238.27C512.06,218.46 504.18,199.45 490.13,185.47zM448,448H341.33v-67.88c0,-44.98 -36.47,-81.45 -81.45,-81.45c0,0 0,0 0,0h-7.76c-44.98,0 -81.45,36.47 -81.45,81.45l0,0V448H64V238.27c0.01,-2.83 1.13,-5.54 3.12,-7.55L218.28,79.55c20.83,-20.83 54.59,-20.83 75.43,-0.01c0,0 0.01,0.01 0.01,0.01L444.89,230.72c1.99,2.01 3.11,4.72 3.12,7.55V448z"/>
9+
</vector>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="512dp"
3+
android:height="512dp"
4+
android:viewportWidth="512"
5+
android:viewportHeight="512">
6+
<path
7+
android:fillColor="#FF000000"
8+
android:pathData="M490.13,185.47L338.97,34.3c-45.85,-45.74 -120.08,-45.74 -165.93,0L21.87,185.47C7.82,199.45 -0.05,218.46 0,238.27v221.4C0.05,488.57 23.48,511.98 52.37,512h407.25c28.9,-0.02 52.33,-23.43 52.37,-52.33V238.27C512.06,218.46 504.18,199.45 490.13,185.47zM448,448H341.33v-67.88c0,-44.98 -36.47,-81.45 -81.45,-81.45c0,0 0,0 0,0h-7.76c-44.98,0 -81.45,36.47 -81.45,81.45l0,0V448H64V238.27c0.01,-2.83 1.13,-5.54 3.12,-7.55L218.28,79.55c20.83,-20.83 54.59,-20.83 75.43,-0.01c0,0 0.01,0.01 0.01,0.01L444.89,230.72c1.99,2.01 3.11,4.72 3.12,7.55V448z"/>
9+
</vector>

android/src/main/java/com/swmansion/rnscreens/gamma/tabs/TabScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class TabScreen(
5858
}
5959

6060
// Icon
61-
var iconResourceName: String? by Delegates.observable(null) { _, oldValue, newValue ->
61+
var drawableIconResourceName: String? by Delegates.observable(null) { _, oldValue, newValue ->
6262
if (newValue != oldValue) {
6363
icon = getSystemDrawableResource(reactContext, newValue)
6464
}

android/src/main/java/com/swmansion/rnscreens/gamma/tabs/TabScreenViewManager.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,12 @@ class TabScreenViewManager :
167167
view.tabBarItemBadgeTextColor = value
168168
}
169169

170-
@ReactProp(name = "iconResourceName")
171-
override fun setIconResourceName(
170+
@ReactProp(name = "drawableIconResourceName")
171+
override fun setDrawableIconResourceName(
172172
view: TabScreen,
173173
value: String?,
174174
) {
175-
view.iconResourceName = value
175+
view.drawableIconResourceName = value
176176
}
177177

178178
override fun setOrientation(
@@ -185,8 +185,8 @@ class TabScreenViewManager :
185185
value: String?,
186186
) = Unit
187187

188-
@ReactProp(name = "iconResource")
189-
override fun setIconResource(
188+
@ReactProp(name = "imageIconResource")
189+
override fun setImageIconResource(
190190
view: TabScreen,
191191
value: ReadableMap?,
192192
) {

android/src/paper/java/com/facebook/react/viewmanagers/RNSBottomTabsScreenManagerDelegate.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ public void setProperty(T view, String propName, @Nullable Object value) {
4141
case "orientation":
4242
mViewManager.setOrientation(view, (String) value);
4343
break;
44-
case "iconResourceName":
45-
mViewManager.setIconResourceName(view, value == null ? null : (String) value);
44+
case "drawableIconResourceName":
45+
mViewManager.setDrawableIconResourceName(view, value == null ? null : (String) value);
4646
break;
47-
case "iconResource":
48-
mViewManager.setIconResource(view, (ReadableMap) value);
47+
case "imageIconResource":
48+
mViewManager.setImageIconResource(view, (ReadableMap) value);
4949
break;
5050
case "tabBarItemBadgeTextColor":
5151
mViewManager.setTabBarItemBadgeTextColor(view, ColorPropConverter.getColor(value, view.getContext()));

android/src/paper/java/com/facebook/react/viewmanagers/RNSBottomTabsScreenManagerInterface.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ public interface RNSBottomTabsScreenManagerInterface<T extends View> {
2121
void setTitle(T view, @Nullable String value);
2222
void setBadgeValue(T view, @Nullable String value);
2323
void setOrientation(T view, @Nullable String value);
24-
void setIconResourceName(T view, @Nullable String value);
25-
void setIconResource(T view, @Nullable ReadableMap value);
24+
void setDrawableIconResourceName(T view, @Nullable String value);
25+
void setImageIconResource(T view, @Nullable ReadableMap value);
2626
void setTabBarItemBadgeTextColor(T view, @Nullable Integer value);
2727
void setTabBarItemBadgeBackgroundColor(T view, @Nullable Integer value);
2828
void setStandardAppearance(T view, Dynamic value);

apps/src/tests/Test3115.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,15 @@ const TAB_CONFIGS: TabConfiguration[] = [
5050
title: 'Tab 1',
5151
freezeContents: false,
5252
icon: {
53-
sfSymbolName: 'sun.max',
53+
ios: {
54+
type: 'sfSymbol',
55+
name: 'sun.max'
56+
},
57+
android: {
58+
type: 'drawableResource',
59+
name: 'sunny',
60+
}
5461
},
55-
iconResourceName: 'sunny',
5662
},
5763
component: makeTab('Tab 1'),
5864
},
@@ -61,9 +67,15 @@ const TAB_CONFIGS: TabConfiguration[] = [
6167
tabKey: 'Tab2',
6268
title: 'Tab 2',
6369
icon: {
64-
sfSymbolName: 'snow',
70+
ios: {
71+
type: 'sfSymbol',
72+
name: 'snow'
73+
},
74+
android: {
75+
type: 'drawableResource',
76+
name: 'mode_cool',
77+
}
6578
},
66-
iconResourceName: 'mode_cool',
6779
},
6880
component: makeTab('Tab 2'),
6981
},

apps/src/tests/TestBottomTabs/index.tsx

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,19 @@ const TAB_CONFIGS: TabConfiguration[] = [
3535
title: 'Tab1',
3636
isFocused: true,
3737
icon: {
38-
sfSymbolName: 'house',
38+
ios: {
39+
type: 'sfSymbol',
40+
name: 'house.fill',
41+
},
42+
android: {
43+
type: 'imageSource',
44+
imageSource: require('../../../assets/variableIcons/icon_fill.png'),
45+
}
3946
},
4047
selectedIcon: {
41-
sfSymbolName: 'house.fill',
48+
type: 'sfSymbol',
49+
name: 'house.fill',
4250
},
43-
// iconResourceName: 'sym_call_incoming', // Android specific
44-
iconResource: require('../../../assets/variableIcons/icon_fill.png'),
4551
},
4652
component: Tab1,
4753
},
@@ -96,13 +102,19 @@ const TAB_CONFIGS: TabConfiguration[] = [
96102
},
97103
tabBarItemBadgeBackgroundColor: Colors.GreenDark100,
98104
icon: {
99-
templateSource: require('../../../assets/variableIcons/icon.png'),
105+
ios: {
106+
type: 'templateSource',
107+
templateSource: require('../../../assets/variableIcons/icon.png'),
108+
},
109+
android: {
110+
type: 'drawableResource',
111+
name: 'sym_call_missed',
112+
}
100113
},
101114
selectedIcon: {
115+
type: 'templateSource',
102116
templateSource: require('../../../assets/variableIcons/icon_fill.png'),
103117
},
104-
iconResourceName: 'sym_call_missed', // Android specific
105-
iconResource: require('../../../assets/variableIcons/icon.png'),
106118
title: 'Tab2',
107119
orientation: 'landscape',
108120
},
@@ -134,13 +146,15 @@ const TAB_CONFIGS: TabConfiguration[] = [
134146
tabBarBlurEffect: 'none',
135147
},
136148
icon: {
137-
imageSource: require('../../../assets/variableIcons/icon.png'),
149+
shared: {
150+
type: 'imageSource',
151+
imageSource: require('../../../assets/variableIcons/icon.png'),
152+
}
138153
},
139154
selectedIcon: {
155+
type: 'imageSource',
140156
imageSource: require('../../../assets/variableIcons/icon_fill.png'),
141157
},
142-
// iconResourceName: 'sym_action_email', // Android specific
143-
iconResource: require('../../../assets/variableIcons/icon_fill.png'),
144158
title: 'Tab3',
145159
// systemItem: 'search', // iOS specific
146160
// systemItem: 'contacts', // iOS specific
@@ -153,13 +167,19 @@ const TAB_CONFIGS: TabConfiguration[] = [
153167
tabScreenProps: {
154168
tabKey: 'Tab4',
155169
icon: {
156-
sfSymbolName: 'rectangle.stack',
170+
ios: {
171+
type: 'sfSymbol',
172+
name: 'rectangle.stack',
173+
},
174+
android: {
175+
type: 'drawableResource',
176+
name: 'custom_home_icon'
177+
}
157178
},
158179
selectedIcon: {
159-
sfSymbolName: 'rectangle.stack.fill',
180+
type: 'sfSymbol',
181+
name: 'rectangle.stack.fill',
160182
},
161-
// iconResourceName: 'sym_action_chat', // Android specific
162-
iconResource: require('../../../assets/svg/cart.svg'),
163183
title: 'Tab4',
164184
systemItem: 'search', // iOS specific
165185
badgeValue: '123',

0 commit comments

Comments
 (0)