I am currently trying to use a <SectionList> inside a classic <View>, all my datas are formatted, the list displays correctly and my item's actions are working.
The issue is that when I am on the top of my SectionList, the area available to trigger the scroll is actually really small ( roughly 100 pixels from the top of the list ). However, once i scroll down a bit from that area, the whole list becomes scrollable and works as intended until I scroll back to the top.
My parent View has flex: 1 as well as my SectionList
Environment
Working environment : MacOS Sierra 10.13.3
Expo version : 23.0
React Native version : 0.50
React : 16.0
Using an IPhone 8 simulation
There's no issue on Android
Steps to Reproduce
Classic creation of a SectionList inside of a View
Expected Behavior
The scroll must be triggered from everywhere in the SectionList
Actual Behavior
When the SectionList is at the top, the scroll only triggers inside a small area ( around 100px from the top of the list )
The code of my SectionList :
<View style={{ flex: 1 }}>
<SectionList
style={styles.openSectionList} // flex: 1
scrollEnabled
stickySectionHeadersEnabled
sections={this.sections}
keyExtractor={item => item["#id"]}
removeClippedSubviews
renderSectionHeader={({ section }) => (
<TouchableHighlight
onPress={() => this.onSectionHeaderPressRef(section.index)}
activeOpacity={0.65}
underlayColor="rgba(0, 0, 0, 0.2)"
style={styles.sectionHeader}
>
<View style={styles.categoryContentContainer}>
<View style={styles.firstPartContent}>
<Text style={styles.categoryHeaderText}>
{section.title === "Autres"
? "Mes produits"
: section.title}{" "}
</Text>
{section.nbItems - section.nbItemsSelected === 0 ? (
<CheckBox
label=""
checked
checkboxStyle={styles.checkbox}
checkboxContainer={styles.checkboxContainer}
/>
) : (
<Text
style={[
styles.categoryHeaderText,
{ color: Colors.contrastColor },
]}
>
({section.nbItems - section.nbItemsSelected})
</Text>
)}
</View>
<Image
source={require("../../../assets/common/chevron.png")}
style={
section.index === this.state.currentCategoryOpen
? styles.categoryChevronOpen
: styles.categoryChevronClosed
}
/>
</View>
</TouchableHighlight>
)}
renderItem={({ item }) =>
this.state.currentCategoryOpen === item.categoryIndex ? (
<ShoppingListProduct
key={item["#id"]}
ingredient={item}
updateIngredient={this.updateIngredientListRef}
onLongPress={this.itemLongPressedRef}
/>
) : null}
/>
</View>
A GIF of the actual behavior ( I'm trying to scroll everytime the cursor is moving ) where we can see that the scroll only triggers when I am above a certain height.
GIF
Any help would be appreciated as I don't know if that's a bug and/or me implementing the component wrong.
Thank you by advance.
someone asked me the solution for this via email so I might as well add it here, from what i remember it was a position/imbrication problem with the components.
I can't remember exactly but I ended up with this code ( my page content changes so that's why it is set as a variable )
// render method
component = ( <SectionList
style={styles.openSectionList} // flex: 1, height: "100%"
scrollEnabled
stickySectionHeadersEnabled
sections={this.sections}
bounces={false}
keyExtractor={item =>
item["#id"] === undefined ? item.userIngredient : item["#id"]
}
getItemLayout={this.getItemLayout}
renderSectionHeader={this.renderSectionListHeaderRef}
renderItem={this.renderSectionListItemRef}
/> )
return (
<View style={{ flex: 1 }}>
{component}
</View>
)
So yeah watch out where your SectionList is defined and how many parents it has, I think it required only one
Hope this helps.
I was able to fix mine by adding this prop to the section list
stickySectionHeadersEnabled={false}
I added a marginBottom to the SectionList of equal amount of space consumed by the View on top to equalise the area.
Related
i got an problem with the refreshing on pull function. The FlatList renders fine, but pull to refresh is not working. This is my current sourcecode:
return (
<View style={GlobalStyles.flex1}>
<FlatList
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl
refreshing={isRefreshing}
onRefresh={() => {
console.log("onRefresh loadVocable");
loadVocables();
}}
/>
}
data={vocables}
keyExtractor={vocable => vocable.id}
onEndReached={() => {
if (!isRefreshing && !endReached) {
loadVocables();
}
}}
renderItem={vocable => (
<TouchableOpacity
onPress={() => {
props.navigation.navigate({ routeName: "editVocable", params: { vocable: vocable.item } });
}}
onLongPress={() => {
handleLongPress(vocable.item.id);
}}>
<Card style={styles.cardStyle}>
<View style={styles.part1}>
<Text style={styles.vocableText}>{vocable.item.wordENG}</Text>
<Text style={styles.vocableText}>{vocable.item.wordDE}</Text>
</View>
<View style={styles.part2}>
<Ionicons name={vocable.item.known ? "md-checkmark-circle" : "md-close-circle"} size={23} color={vocable.item.known ? Colors.success : Colors.danger} />
</View>
</Card>
</TouchableOpacity>
)}
/>
</View>
);
In the official docs is an example that says contentContainerStyle needs to be flex: 1 to know the height, that makes sence to me, so when i set contentContainerStyle with flex 1, refresh on pull works fine, but then i can't scroll anymore in the Flatlist and everthing get very tight, so the style also change then. Does anyone know why this happen?
The first picture is with "contentContainerStyle={{flex: 1}}" and the last one is without contentContainerStyle.
The answer was so easy, I compared a new project (there worked my code) to the one where the problem was and after 5 days I found the little error:
My import was wrong!
I imported FlatList like this:
import { FlatList } from "react-native-gesture-handler";
But it needs to get imported from React-Native so like this:
import { FlatList } from "react-native";
Thanks to #The1993, without the hint to compare the projects, maybe I would stuck forever on this error :D In the future I will compare working files to find any error!
contentContainerStyle is used to style inner content e.g items alignments, padding, etc
style is used to align its height and relations
You can replace style={{flex: 1}} instead of contentContainerStyle or wrap the parent element with flex: 1
I want to make sure that the selected item is always in the middle of the flatlist. The middle item is selected. The style of the chosen one is different than the others.
<FlatList data={items}
style={styles.listStyle}
ref={(ref) => {
this.flatListRef = ref;
}}
snapToAlignment={'center'}
horizontal
onPress={this.onPressButton}
showsHorizontalScrollIndicator={false}
renderItem={({item, index}) => (
<TouchableWithoutFeedback style={{justifyContent: 'center'}}
onPress={this.onPressButton}>
<View style={{justifyContent: 'center'}}>
<View style={styles.containerView}>
<View
style={[styles.circlesBack, (this.state.selectedId === index) ? styles.circles : styles.circlesBack]}>
{this.state.selectedId === index ?
<FontAwesome size={24} name={item.icon} color="white"/> :
<FontAwesome size={24} name={item.icon} color="#BEBEBE"/>}
</View>
{this.state.selectedId === index ? <Text
style={[styles.itemText, (this.state.selectedId === index) ? styles.itemText : styles.itemTextBack]}>{item.title}</Text> :
<Text> </Text>}
</View>
</View>
</TouchableWithoutFeedback>
)}
keyExtractor={item => item.id}/>
this is what it looks like now
it should look like this
How to do it?
Thank you
You should use like this:
this.flatListRef.scrollToIndex({animated: true, index: 30, viewPosition:0.5})
Valid params keys are:
'animated' (boolean) - Whether the list should do an animation while scrolling. Defaults to true.
'viewPosition' (number) - A value of 0 places the item specified by index at the top, 1 at the bottom, and 0.5 centered in the middle.
Here is someone's code for example, you can instead the line
this.flatListRef.scrollToIndex({animated: true, index: 30,viewPosition:0.5});
of
this.flatListRef.scrollToIndex({animated: true, index: randomIndex});
then you can see it works(at right choose android or ios to run will be better)
docs
But the several begins item seems can't go to middle because of there are no data before. Or you can use react-native-infinite-looping-scroll to let other data at bottom connect back to first. Maybe can achieve this problem.
This may seem like a basic question, but I can't seem to figure it out or properly word a search on this.
I have a View with two other Views in it, one of which sometimes is hidden. So the component looks something like this:
function MyComponent (props) {
return (
<View style={{ flex: 1 }}>
{
props.showButtonView
? (
<View style={{ flex: ??? }}>
<Button title='do something' onPress={() => console.warn('doSomethign)} />
</View>
)
: null
}
<View style={{ flex: ?? }}>
<Stuff/>
</View>
</View>
)
}
Now, what I am trying to do is have the Stuff component cover the entire screen whenever the Button is not present. However, if the props.showButtonView is true, and we do want to see the view with the Button I only want need to see the button on the top, and then the rest is
whatever is in the Stuff component. How do I get this done? Is it based on the flex numbers?
Also, you may be wondering why I need to separate these two into separate Views in the first place, the reason for that is because there are other unrelated things in the Stuff component that cover the button and don't allow me to click it. Anyway, long story short, the separation of the two by View is mandatory for this case.
flex should be 1 for both the places.
Flex:1 Indicates that it will takes the whole space available after if. So, it doesn't really matters when a button is placed there or not. Whenprops.showButtonView is true then the button is at the top and rest your stuff is placed after that.
Otherwise,
When props.showButtonView is false then button render code will not be executed and then stuff view will be all over your
Try below code when props.showButtonView === true
function MyComponent (props) {
return (
<View style={{ flex: 1 }}>
{
props.showButtonView
? (
<View style={{ width: '100%' }}>
<Button title='do something' onPress={() => console.warn('doSomethign)} />
</View>
)
: null
}
<View style={{ flex: 1 }}>
<Stuff/>
</View>
</View>
)
}
I am trying to make my content start 100 px from the top in React Native. I have tried with
const OFFSET = 100
const ScrollViewTest = (props) => (
<ScrollView
contentInset={{ top: OFFSET }}
contentOffset={{ y: OFFSET }}
>
<Text>Test</Text>
</ScrollView>
)
But when I render the screen, it starts from 0 px, but if I scroll a little, it will scroll to 100px from the top and stay there.
So it seems React Native doen't trigger the contentOffset and contentInset properties on initialization.
How can I fix this? I have also tried setting automaticallyAdjustContentInsets={false} with no changes.
Also, it seems these properties are for iOS only. Are there any similar properties for Android?
You should use the contentContainerStyle1 property with a marginTop on your ScrollView.
By using this property it will apply the container wrapping your children (which I believe is what you want in this case) and with the additional benefit of working on both iOS and Android.
I.e.
const OFFSET = 100
const ScrollViewTest = (props) => (
<ScrollView
contentContainerStyle={{ marginTop: OFFSET }}
>
<Text>Test</Text>
</ScrollView>
)
componentDidMount(){
if(Platform.OS==='ios'){
this.refs._scrollView.scrollToOffset({animated:false,offset:-OFFSET});
}
}
render(){
return(
<ScrollView
ref="_scrollView"
contentInset={{top: OFFSET}}
...>
...
</ScrollView>
)
}
When I try to call scrollTo from componentDidMount method it does not scroll. I have to use workaround with setTimeout to make it work:
componentDidMount() {
setTimeout(() => this.refs._scrollView.scrollTo({ x: 100, y: 0 }) , 0);
}
As I wrote in the comments to Peter Theill's solution, the proposed solutions here do not work in 100% of the cases, e.g. for one of the following scenarios:
You have a transparent Header where you want the content to scroll underneath (e.g. a blurred header).
You could solve this with Peter Theill's solution and replacing contentContainerStyle with style={{paddingTop: headerHeight}}.
But this won't help you if you have also the following typical scenario:
You want to use RefreshControl. It will be displayed behind the header and there is no way to position it differently on iOS. Therefore, you need to use contentOffset & contentInset as Jamgreen proposed. However, I had kind of similar issues with initial rendering of offset. I solved it in my case with the prop automaticallyAdjustContentInsets={false}, so I would recommend to try this!
Here is the complete solution (for android & iOS) in my scenario:
<ScrollView
style={{paddingTop: Platform.select({android: headerHeight, ios: 0})}}
scrollIndicatorInsets={{ right: 1 }}
ref={scrollViewRef}
contentInset={{ top: headerHeight}}
contentOffset={{ x: 0, y: Platform.select({android: 0, ios: -headerHeight})}}
automaticallyAdjustContentInsets={false}
refreshControl={<RefreshControl
refreshing={refresh}
onRefresh={() => {
setRefresh(true);
setTimeout(() => setRefresh(false), 10000);
}}
colors={[inputPlaceholderGray]}
tintColor={inputPlaceholderGray}
progressViewOffset={headerHeight}
/>}
>
{CONTENT}
</ScrollView>
Hope it still helps people although the issue is already old.
padding-like effect:
const OFFSET = 100
const ScrollViewTest = (props) => (
<ScrollView>
<View style={{ height: OFFSET }} />
<Text>Test</Text>
</ScrollView>
)
header-like effect:
const OFFSET = 100
const ScrollViewTest = (props) => (
<View style={{ paddingTop: OFFSET }}>
<ScrollView>
<Text>Test</Text>
</ScrollView>
</View >
)
I have simple app, with 3 tabs. Tabs are defined by TabBarIOS in index.ios. I’m not using Navigator nor NavigatorIOS. In each TabBarItem , I just put the component name in tags. Like this :
return(
<TabBarIOS
selectedTab={this.state.selectedTab}
//unselectedTintColor="yellow"
//tintColor="white"
//barTintColor="darkslateblue"
>
<TabBarIOS.Item
icon={{uri: 'courses.png', scale: 5}}
title="Courses"
badge={undefined}
selected={this.state.selectedTab === 'courses'}
onPress={() => {
this.setState({
selectedTab: 'courses',
});
}}>
<Courses />
</TabBarIOS.Item>
<TabBarIOS.Item
icon={{uri: 'register.png', scale: 5}}
title="Register"
badge={undefined}
selected={this.state.selectedTab === 'register'}
onPress={() => {
this.setState({
selectedTab: 'register',
});
}}>
<NavigatorIOS
//style={styles.nav}
initialRoute={{
title : 'Register',
component: Register
}}
/>
</TabBarIOS.Item>
<TabBarIOS.Item
icon={{uri: 'profile.png', scale: 5}}
title="Profile"
badge={undefined}
selected={this.state.selectedTab === 'profile'}
onPress={() => {
this.setState({
selectedTab: 'profile',
});
}}>
<Profile />
</TabBarIOS.Item>
</TabBarIOS>
);
If you see in code, in first and third tab, i'm showing content of the item by puting component name in tags like
<Courses />
But for second item, i tried using navigatorios to show component, but it shows only a blank page with title. it doesn't show the content of component. i see lots of examples working like that, but it didn't work for me. maybe because i'm not using navigator or navigaatorios for index.ios , because in most examples i see, they put navigatorios for index and set initialroute. i tried it, but didn't work for me.
Everything works fine till here. In Courses page, I have a list view with items under it (actually items with header, which are collapsible) , when user clicks on each item , I need to redirect user to a page to show that course’s details. (I should pass some arguments also).
render(){
return (
<View style={{
flex: 1
}}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
style={styles.listView}
/>
</View>
);
}
renderRow(data) {
var header = (
<View>
<View style={styles.rowContainer}>
<View style={styles.textContainer}>
<Text style={styles.title}>{data.nid}</Text>
<Text style={styles.description} numberOfLines={0}>{data.title}</Text>
</View>
</View>
<View style={styles.separator}></View>
</View>
);
///////////
var cid = [];
var content = [];
for(let x=0; x < Object.keys(data.course).length; x++){
cid[x] = data.course[x].course_id;
content.push(
<TouchableHighlight
underlayColor='#e3e0d7'
key={x}
onPress={()=> {
this.rowPress(x); ///// here i need to redirect user to another page to show course's details
}}
style={styles.child}
>
<Text style={styles.child}>
{data.course[x].title}
</Text>
</TouchableHighlight>
);
}
var clist = (
<View style={styles.rowContainer}>
{content}
</View>
);
////////////
return (
<Accordion
header={header}
content={clist}
easing="easeOutCubic"
/>
);
}
I want to redirect user to a page and show details, and i want to have a header title and a back button (like Navigator or NavigatorIOS).
I've found a sample app that is doing exactly what i'm looking for, but it's too complicated for me, that i didn't understand how it is working,
react native tabbar-navigator (iOS)
I've looked into many examples and tutorials. i tried to implement their code into mine but it didn't work.
Also find some other tutorials using redux, but it's so complicated and hard for me. i'm looking for something more simpler.
Navigation using Redux
Looking forward to hearing from you,
Any help is highly appreciated. Thanks in Advance!
I found out that when i have few scenes and i want to navigate between them , best practice is to use Navigator or NavigatorIOS.
I continue my app and did few more scenes and components ( with few levels going deep ) and i found out that Navigator or NavigatorIOS is not working properly for that purpose. Then i switched to NavigationExperimental and that was awesome. Then i had some problems in state management, i found out about redux. it was not easy at first, but when i start it, you will love it.
So for newbies like me, it's better to start with redux at first. that would make life much easier for you :)