Focus Crop Issue When Scrolling – Cards Not Fully Visible

Issue Summary

We are observing a focus cropping issue on the Home page while navigating through content carousels.
When scrolling vertically, certain cards—particularly those of type roller_poster—are not fully visible on screen when they gain focus.
This behavior does not occur for other card types like height below 300px, which render correctly within the visible viewport.


File: HomeScreen.tsx

<TVFocusGuideView trapFocusLeft style={styles.movieGridContainer}>
  {contentdata && contentdata.length > 0 ? (
    <ScrollView ref={scrollRef} style={{ flex: 1 }}>
      <MovieGrid
        ref={firstTileRef}
        data={contentdata}
        initialColumnsToRender={13}
        onTileFocus={onTileFocus}
        onTilePress={onTilePress}
        componentStyle={styles.movieGridComponent}
        destinations={destinations}
        triggerFetch={handleFetch}
        isFetching={isFetching}
        hasTVPreferredFocus={false}
      />
    </ScrollView>
  ) : (
    <View style={styles.RotatorItem}>
      <Animated.Image
        source={require('../assets/progress_loader.png')}
        style={[styles.imageStyle, { transform: [{ rotate: rotation }] }]}
      />
    </View>
  )}
</TVFocusGuideView>

File: MovieGrid.tsx

const calculateEstimatedItemSize = (item: any) => {
  const cardType = item?.data?.[0]?.cardType;
  switch (cardType) {
    case 'roller_poster':
      return 450;
    case 'sheet_poster':
    case 'sheet_poster_package':
      return 400;
    case 'network_poster':
    case 'network_poster_package':
      return 350;
    default:
      return 450;
  }
};

<View style={styles.scrollView}>
  <FlashList
    data={data}
    renderItem={({ item, index }) => renderCarousel(item, index)}
    keyExtractor={(item, index) => `${item.heading}-${index}`}
    estimatedItemSize={data.length > 0 ? calculateEstimatedItemSize(data[0]) : 400}
    estimatedListSize={{
      height: Dimensions.get('screen').height,
      width: Dimensions.get('screen').width,
    }}
  />
</View>


Observed Behavior

  • When navigating vertically, the focused card gets partially cropped (focus outline is not fully visible).

  • The issue occurs only for roller_poster cards, which have greater height (≈450px).

  • Other poster types render and focus correctly within the visible area.


Expected Behavior

The focused card should remain fully visible and centered within the scrollable view when navigating up or down, regardless of the card type or height.


Possible Causes (Initial Analysis)

  • The issue might be related to ScrollView configuration within HomeScreen.tsx, which may not be auto-adjusting for larger item heights.

  • Alternatively, incorrect estimatedItemSize or estimatedListSize values in FlashList could be causing layout miscalculations and improper scroll offset handling.


Request for Guidance

Could you please review the implementation and suggest:

  1. Whether any configuration or prop should be added to ensure the focused card remains fully visible during scroll operations?

  2. If adjustments are needed in FlashList’s estimatedItemSize or ScrollView behavior to accommodate taller poster types (like roller_poster)?

  3. Any best practices for handling focus visibility and scroll offset correction in TVFocusGuideView + ScrollView + FlashList integrations?

    Thank you .
    Vittal maradi .

Hi @vittalmaradi ,

Thank you for the detailed question about the focus cropping issue with roller_poster cards. Our team will look into this and get back to you with updates.

Best regards,
Aishwarya

Hi Team ,
just checking , is there any update ?

Regards
Vittal maradi

Hi @vittalmaradi

I was able to replicate part of your issue and have taken this up with the Internal team. They are currently working on it.

However I noticed something in MovieGrid.tsx that I feel, could be improved. In your code, you have added:

const calculateEstimatedItemSize = (item: any) => {
  const cardType = item?.data?.[0]?.cardType;
  switch (cardType) {
    case 'roller_poster':
      return 450;
    case 'sheet_poster':
    case 'sheet_poster_package':
      return 400;
    case 'network_poster':
    case 'network_poster_package':
      return 350;
    default:
      return 450;
  }
};

This only checks the first item (item?.data?.[0]?.cardType)

However, if we change it to the below snippet (that I used) , we can processes all items in the array (item.data.map())

const calculateEstimatedItemSize = (item: any) => {
      if (!item?.data?.length) {
        return 450;
      }

      return Math.max(
        ...item.data.map((dataItem: any) => {
          switch (dataItem?.cardType) {
            case 'roller_poster':
              return 450;
            case 'sheet_poster':
            case 'sheet_poster_package':
              return 400;
            case 'network_poster':
            case 'network_poster_package':
              return 350;
            default:
              return 450;
          }
        }),
      );
    };

Following my changes in calculateEstimatedItemSize , we are returning the maximum size among all items in the array. So it’s more robust for handling multiple items and ensures the container is sized for the largest content.

Can you please try this for now and let me know if you observe any changes?
In the meantime, I’ll get back to you with more updates.

Warm regards,
Ivy