You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native FlatList指定View点击增减高度及滚动位置保持

问题分析与解决方案

你的问题核心在于全局状态控制所有Item的高度和展开状态,加上FlatList缺少唯一key导致滚动位置丢失:

  • 全局的initialHeight和isViewShown会让所有Item同步变化,点击任意Item都会触发全列表高度改变
  • 未设置keyExtractor,FlatList重新渲染时无法正确识别组件,进而重置滚动位置

解决步骤

  1. 用独立状态记录单个Item的展开状态:放弃全局状态,改用集合存储展开的Item唯一ID,实现单个Item的独立控制
  2. 给FlatList添加唯一key:让FlatList能准确识别每个Item,避免组件复用错误,保留滚动位置
  3. 动态计算单个Item高度:每个Item根据自身展开状态计算高度,不再依赖全局统一值
  4. 点击事件仅切换当前Item状态:点击时传递当前Item的ID,只修改该Item的展开状态

修改后的完整代码

import { useState, useCallback } from 'react';

export default function YourComponent() {
  // 用Set存储展开的Item ID,方便添加/删除操作
  const [expandedIds, setExpandedIds] = useState(new Set());

  // 切换指定Item的展开状态
  const toggleItemExpand = useCallback((itemId) => {
    setExpandedIds(prev => {
      const newSet = new Set(prev);
      newSet.has(itemId) ? newSet.delete(itemId) : newSet.add(itemId);
      return newSet;
    });
  }, []);

  // 根据展开状态计算单个Item的高度
  const getItemHeight = (isExpanded) => {
    if (windowWidth > windowHeight) {
      return isExpanded ? verticalScale(170) : verticalScale(140);
    } else {
      return isExpanded ? verticalScale(280) : verticalScale(150);
    }
  };

  return (
    <View>
      <FlatList
        data={data}
        // 必须设置keyExtractor,使用Item的唯一标识
        keyExtractor={(item) => item.key}
        style={{ backgroundColor: '#fff' }}
        contentContainerStyle={{ paddingBottom: '5%' }}
        // 可选:若滚动位置仍有跳动,添加该属性强制保持可见区域位置
        maintainVisibleContentPosition={{ minIndexForVisible: 0 }}
        renderItem={({ item }) => {
          const isExpanded = expandedIds.has(item.key);
          const itemHeight = getItemHeight(isExpanded);

          return (
            <View style={{ flexDirection: 'column', flex: 1, width: '100%' }}>
              <TouchableOpacity onPress={() => toggleItemExpand(item.key)}>
                <View style={{
                  paddingLeft: 5,
                  flexDirection: 'row',
                  flex: 1,
                  height: itemHeight,
                  backgroundColor: item.Status === 'Open' ? '#B7E6E6' : '#EFA5A5',
                  marginTop: 10,
                  borderRadius: 5,
                  alignContent: 'center',
                  marginHorizontal: 10,
                }}>
                  <View style={{
                    backgroundColor: '#fff',
                    width: 50,
                    height: 50,
                    borderRadius: 27,
                    alignSelf: 'center',
                    justifyContent: 'center',
                    alignItems: 'center'
                  }}>
                    <Text style={{ color: item.Status === 'Open' ? '#B7E6E6' : '#EFA5A5' }}>{item.Status}</Text>
                  </View>

                  <View style={{
                    flex: 1,
                    borderTopRightRadius: 20,
                    borderBottomRightRadius: 20,
                    padding: windowWidth > windowHeight ? moderateScale(10) : moderateScale(15),
                    justifyContent: 'center'
                  }}>
                    <View style={{
                      flexDirection: 'row',
                      justifyContent: 'space-between',
                      marginVertical: windowWidth > windowHeight ? verticalScale(5) : verticalScale(5),
                      marginTop: 10
                    }}>
                      <Text style={{ color: 'black', fontSize: windowWidth > windowHeight ? moderateScale(14) : moderateScale(12), fontFamily: 'DMSans-Bold' }}>Customer ID: {item.CusId}</Text>
                      <Text style={{ color: 'black', fontSize: windowWidth > windowHeight ? moderateScale(14) : moderateScale(12), fontFamily: 'DMSans-Bold' }}>Customer PO: {item.CusPo}</Text>
                    </View>

                    <View style={{
                      flexDirection: 'row',
                      justifyContent: 'space-between',
                      marginVertical: windowWidth > windowHeight ? verticalScale(5) : verticalScale(5)
                    }}>
                      <Text style={{ color: 'black', fontSize: windowWidth > windowHeight ? moderateScale(13) : moderateScale(12), fontFamily: 'DMSans-Regular' }}>Date From: {item.Datefrm}</Text>
                      <Text style={{ color: 'black', fontSize: windowWidth > windowHeight ? moderateScale(13) : moderateScale(12), fontFamily: 'DMSans-Regular' }}>Date To: {item.Dateto}</Text>
                    </View>

                    <View style={{
                      flexDirection: 'row',
                      justifyContent: 'space-between',
                      marginVertical: windowWidth > windowHeight ? verticalScale(5) : verticalScale(5)
                    }}>
                      <Text style={{ color: 'black', fontSize: windowWidth > windowHeight ? moderateScale(13) : moderateScale(12), fontFamily: 'DMSans-Regular' }}>Tracking No: {item.TrNo}</Text>
                      <Text style={{ color: 'black', fontSize: windowWidth > windowHeight ? moderateScale(13) : moderateScale(12), fontFamily: 'DMSans-Regular' }}>Mark For: {item.Markfr}</Text>
                    </View>

                    {isExpanded && (
                      <View>
                        <View style={{
                          flexDirection: 'row',
                          justifyContent: 'space-between',
                          marginVertical: windowWidth > windowHeight ? verticalScale(5) : verticalScale(5)
                        }}>
                          <Text style={{ color: 'black', fontSize: windowWidth > windowHeight ? moderateScale(13) : moderateScale(12), fontFamily: 'DMSans-Regular' }}>Item ID: {item.ItmId}</Text>
                          <Text style={{ color: 'black', fontSize: windowWidth > windowHeight ? moderateScale(13) : moderateScale(12), fontFamily: 'DMSans-Regular' }}>Warehouse: {item.Wearh}</Text>
                        </View>

                        <View style={{
                          flexDirection: 'row',
                          justifyContent: 'space-between',
                          marginVertical: windowWidth > windowHeight ? verticalScale(5) : verticalScale(5)
                        }}>
                          <Text style={{ color: 'black', fontSize: windowWidth > windowHeight ? moderateScale(13) : moderateScale(12), fontFamily: 'DMSans-Regular' }}>Unit Price: {item.UPri}</Text>
                          <Text style={{ color: 'black', fontSize: windowWidth > windowHeight ? moderateScale(13) : moderateScale(12), fontFamily: 'DMSans-Regular' }}>Ext Price: {item.EPri}</Text>
                        </View>

                        <View style={{
                          flexDirection: 'row',
                          justifyContent: 'space-between',
                          marginVertical: windowWidth > windowHeight ? verticalScale(5) : verticalScale(5)
                        }}>
                          <Text style={{ color: 'black', fontSize: windowWidth > windowHeight ? moderateScale(13) : moderateScale(12), fontFamily: 'DMSans-Regular' }}>Order No: {item.OrdNum}</Text>
                          <Text style={{ color: 'black', fontSize: windowWidth > windowHeight ? moderateScale(13) : moderateScale(12), fontFamily: 'DMSans-Regular' }}>Status: {item.Status}</Text>
                        </View>
                      </View>
                    )}
                  </View>
                </View>
              </TouchableOpacity>
            </View>
          );
        }}
      />
    </View>
  );
}

关键修改说明

  • 独立状态管理:通过expandedIds集合控制单个Item的展开状态,不会影响其他Item
  • 唯一key设置:keyExtractor让FlatList能准确识别组件,避免滚动位置丢失
  • 动态高度计算:每个Item根据自身展开状态计算高度,实现局部高度变化
  • 精准事件触发:点击事件仅修改当前Item的状态,减少不必要的全列表渲染

内容的提问来源于stack exchange,提问作者Junaid Anwar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 08:20:22