Skip to content

Commit fcbfde1

Browse files
committed
Decouple scrollIndicatorInsets from contentInset in KeyboardChatScrollView
Previously, scrollIndicatorInsets matched contentInset, which included blankSize. This caused the scroll indicator to end halfway down the screen when blankSize was large. Add a scrollIndicatorPadding prop to ScrollViewWithBottomPadding that computes indicator insets from keyboard + extraContentPadding only, excluding blankSize. User-supplied scrollIndicatorInsets.bottom/top are now additive adjustments (previously silently ignored). Update AILegendListChat example to pass a negative safe area bottom adjustment for apps that render into the unsafe area.
1 parent 5aff491 commit fcbfde1

3 files changed

Lines changed: 22 additions & 2 deletions

File tree

  • FabricExample/src/screens/Examples/AILegendListChat
  • src/components

FabricExample/src/screens/Examples/AILegendListChat/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ const AIChat = () => {
352352
keyExtractor={(_item, index) => `item-${index}`}
353353
maintainScrollAtEnd={Platform.OS === "web"}
354354
offset={insets.bottom}
355+
scrollIndicatorInsets={{ bottom: -insets.bottom }}
355356
renderItem={({ item }) => (
356357
<View>
357358
{item.sender === "user" ? (

src/components/KeyboardChatScrollView/index.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ const KeyboardChatScrollView = forwardRef<
8787
),
8888
);
8989

90+
// Scroll indicator inset = keyboard + ecp (excludes blankSize).
91+
// Apps that render into the unsafe area can supply a negative
92+
// scrollIndicatorInsets adjustment at the application layer.
93+
const indicatorPadding = useDerivedValue(
94+
() => padding.value + effectiveExtraContentPadding.value,
95+
);
96+
9097
const onLayout = useCallback(
9198
(e: LayoutChangeEvent) => {
9299
onLayoutInternal(e);
@@ -124,6 +131,7 @@ const KeyboardChatScrollView = forwardRef<
124131
ref={onRef}
125132
{...rest}
126133
bottomPadding={totalPadding}
134+
scrollIndicatorPadding={indicatorPadding}
127135
contentOffsetY={contentOffsetY}
128136
inverted={inverted}
129137
ScrollViewComponent={ScrollViewComponent}

src/components/ScrollViewWithBottomPadding/index.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ type ScrollViewWithBottomPaddingProps = {
3131
children?: React.ReactNode;
3232
inverted?: boolean;
3333
bottomPadding: SharedValue<number>;
34+
/** Padding for scroll indicator insets (excludes blankSize). Falls back to bottomPadding when not provided. */
35+
scrollIndicatorPadding?: SharedValue<number>;
3436
/** Absolute Y content offset (iOS only, for KeyboardChatScrollView). */
3537
contentOffsetY?: SharedValue<number>;
3638
} & ScrollViewProps;
@@ -43,6 +45,7 @@ const ScrollViewWithBottomPadding = forwardRef<
4345
{
4446
ScrollViewComponent,
4547
bottomPadding,
48+
scrollIndicatorPadding,
4649
contentInset,
4750
scrollIndicatorInsets,
4851
inverted,
@@ -60,6 +63,14 @@ const ScrollViewWithBottomPadding = forwardRef<
6063
const bottom = insetBottom + (contentInset?.bottom || 0);
6164
const top = insetTop + (contentInset?.top || 0);
6265

66+
const indicatorPad = scrollIndicatorPadding ?? bottomPadding;
67+
const indicatorTop =
68+
(inverted ? indicatorPad.value : 0) +
69+
(scrollIndicatorInsets?.top || 0);
70+
const indicatorBottom =
71+
(!inverted ? indicatorPad.value : 0) +
72+
(scrollIndicatorInsets?.bottom || 0);
73+
6374
const result: Record<string, unknown> = {
6475
// iOS prop
6576
contentInset: {
@@ -69,8 +80,8 @@ const ScrollViewWithBottomPadding = forwardRef<
6980
left: contentInset?.left,
7081
},
7182
scrollIndicatorInsets: {
72-
bottom: bottom,
73-
top: top,
83+
bottom: indicatorBottom,
84+
top: indicatorTop,
7485
right: scrollIndicatorInsets?.right,
7586
left: scrollIndicatorInsets?.left,
7687
},

0 commit comments

Comments
 (0)