How to override style in NativeBase? - react-native

In every code example mentioned in NativeBase Docs, there's no usage of React StyleSheet.
I didn't find a way to override styles of NativeBase.
How can I include React StyleSheet into my app?

NativeBase uses a different approach to add styling to your component.
It uses a Utility first approach, wherein it leverages the ReactNative stylesheets API and adds a layer of Utility Props on top of it. The idea is to pass props to your component to change their styling, instead of defining a className and adding styles in a separate stylesheet.
For example, with React Native, you would write:
function Example() {
return (
<View style={styles.container}>
{/* content */}
</View>;
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#0891b2',
paddingVertical: 16,
paddingHorizontal: 12,
borderRadius: 5,
alignSelf: 'center',
width: 375,
maxWidth: '100%'
}
});
Now, with NativeBase, to achieve the same view, you would write it as follows:
function Example() {
return (
<NativeBaseProvider>
<Box bg="primary.600" py="4" px="3" rounded="md" width={375} maxWidth="100%">
{/* content */}
</Box>
</NativeBaseProvider>;
);
}
Here, NativeBase has already defined bg, py, px, etc. as props and mapped them to the styling properties of backgroundColor, paddingVertical , paddingHorizontal, etc. respectively.
There is a list of these mappings provided here, which you can simply refer to add any styling that you may need for your component.

For NativeBase usually if you want customize the appearance you need to do it at a theme level and not a component/widget level.
http://nativebase.io/docs/v0.5.2/customize
I believe the reason it this keeps the UI consistent.
Have a look at the source code for all the widgets that you are trying to override the style for:
https://github.com/GeekyAnts/NativeBase/blob/master/Components/Widgets/
There are certain cases where you can amend certain aspects e.g. for the button
this.props.style is used anywhere but this.props.textStyle
So you can override the textStyle when you define the component:
<Button textStyle={{...}}/>
https://github.com/GeekyAnts/NativeBase/blob/master/Components/Widgets/Button.js

Related

Can't overwrite external stylesheet property with inline style in React Native

I have a custom TextInput component that takes in two stylesheets and appends any inline styles afterwards and passes those to the TextInput style property:
class WSTextInput extends React.Component<TextInputProps> {
render() {
return (
<TextInput
style={[styles.defaultText, styles.defaultTextInput, this.props.style] as StyleProp<TextStyle>}
{...this.props}
/>
);
}
}
These are the stylesheets it uses:
defaultText: {
fontSize: 24,
fontFamily: 'cardenio-modern'
},
defaultTextInput: {
flex: 1,
alignSelf: 'stretch',
textAlign: 'center',
backgroundColor: '#fafafa',
borderBottomWidth: 1,
margin: 10
}
The code works fine so as long as this.props.style does not attempt to overwrite any of the existing properties in defaultText or defaultTextInput.
When I do try to do so, e.g. with this element (note that styles.defaultTextInput's borderBottomWidth and backgroundColor are overwritten):
<WSTextInput style={{ borderBottomWidth: 0, backgroundColor: 'transparent' }}
editable={false}
value={this.state.total.toString()}
/>
Then this is the result:
The top right element is a WSTextInput rendering correctly without inline overrides; the bottom right element is a WSTextInput with the above inline overrides rendering incorrectly.
When I open reactnative-tools and inspect the element tree, I see this for the top right element:
And this for the bottom right element:
So it seems that for the bottom right element, the inline style survives, but both stylesheets are lost.
I have tried different ways of coalescing the properties in WSTextInput.render, e.g. using Object.assign or the spread operator, but the result is the same. Any insights would be appreciated.
I'm still not exactly clear on what the desired outcome is because it's not great to not allow the font size and some others to not be overridden. But, I think this is just as simple as moving the props to the first arg so they are overridden by yours.
render() {
const {style, ...theRest} = this.props;
return (
<TextInput
{...this.props}
style={[styles.defaultText, styles.defaultTextInput] as StyleProp<TextStyle>}
/>
);
}
For more control try removing the style from the props and passing it where desired in the style props so font is overridden but other stuff isn't.
render() {
const {style, ...theRest} = this.props;
return (
<TextInput
style={[styles.defaultText, style, styles.defaultTextInput] as StyleProp<TextStyle>}
{...theRest}
/>
);
}
Finally, you can explicitly remove the ones you don't want overridden thru deconstruction and pass thru anything else.
const {style, ...theRest} = this.props;
const {backgroundColor, borderBottomWidth, ...theRestOfStyle} = style;
Then use it in your style:
<TextInput
style={[styles.defaultText, styles.defaultTextInput, theRestOfStyle] as StyleProp<TextStyle>}
{...theRest}
/>

How to show View within styled (using styled-components) View - React Native

I am learning React Native and styled components. I am working on a simple iOS app and facing some problems with styled-components.
What I'm trying to do
I am trying to show modal on click which looks like this
<Modal visible={this.state.isModalVisible} animationType={'fade'}>
<StyledView flex={1} padding={10} backgroundColor={'orange'}>
<View>
...more Views and Texts
</View>
</StyledView>
</Modal>
StyledView is a custom view that I have created using styled-components which looks like this
const ViewWrapper = styled.View`
flex: ${props => props.flex};
padding: ${props => props.padding};
backgroundColor: ${props => props.backgroundColor};
`;
const StyledView = ({ flex, padding, backgroundColor }) => (
<ViewWrapper
flex={flex}
padding={padding}
backgroundColor={backgroundColor}
/>
);
export default StyledView;
Problems I'm having
1) When I set padding={10}, I get an error Failed to parse declaration "padding: 10".
2) After Googling a bit, I found that I should be using padding={'10px'} which throws this error, 10px is of type NSString cannot be converted to YGValue. Did you forget the % or pt suffix?.
(padding={'10%'} works fine)
Then I simply tried setting flex and padding values in ViewWrapper and send only background color as prop.
3) But for some reason, Views and Texts nested within StyledView does not show up.
Please tell me why it's not working and help me understand what I'm missing here. Thanks.
You have a couple of issues.
Styled Components do not accept strings in them, so you cannot
sent the '10px' from the prop.
You are correct that padding
needs a px at the end. Something padding alone does not work, but
you can workaround it by adding paddingVertical and
paddingHorizontal with the same value which is the same as padding.
You are overcomplicating the implementation as you dont need to
pass the style props and you can define them all within your styled
component. Like this:
import styled from "styled-components/native"
const StyledView = styled.View`
flex: 1;
padding: 10px;
backgroundColor: orange
`;
And then you just use it like this:
<Modal visible={this.state.isModalVisible} animationType={'fade'}>
<StyledView>
<View>
...more Views and Texts
</View>
</StyledView>
</Modal>
No need for the ViewWrapper or more props. Also, and this is just personal. I only use StyledComponents for the ones that could change at run time or depend on a theme, like exts for fontFamily or fontSize. For the rest that have constant styles that never change, I just use normal styles objects since it is less verbose.
If this is a simplified version and you absolutely need the props you con move them all into one single theme object and pass it with a ThemeProvider and then just read it as ${props.theme.backgroundColor} or something.
Cheers

Cannot change width and height of custom component in React-native?

I have a minimal custom stateless component like this:
const ViewBox = (props) => (
<View style={ mainStyle : {backgroundColor: 'beige'} }>
{props.children}
</View>
)
export default ViewBox;
so I want to import and use it inside another component.
export default class Test extends React.Component {
render() {
return (
<View style={styles.containerView} >
<ViewBox style={styles.mainBox}>
<Text style={[styles.boxTitle, {color: '#8F468C'}]}>Lorem ipsum...</Text>
</ViewBox>
</View>
);
}
}
const styles = {
containerView: {
flex: 1,
marginTop: 50,
alignItems: 'center',
backgroundColor: 'brown',
},
mainBox: {
flex: 1,
width: 250, //No effect ! ! !
height: 250 //No effect ! ! !
},
boxTitle: {
backgroundColor: 'pink',
fontSize: 17,
marginTop: 20
}
};
Here we have at least 2 inexplicable facts:
1) and more important the width and height of the ViewBox (or every custom component you want to use here) is totally out of control! Assigning numeric size or Flex values has no effect and ViewBox keeps the minimum width/height needed to render the inner Text.
2) Removing the root View (so the ViewBox became the root) ViewBox continue ignoring any size, but now it fills all the space available.... WHY???
All mentioned behavoirs occurs using a custom View (ViewBox in this case), instead if replace it with a normal View all works as expected.
I guess to know enough flex and UI best practices for React-native, but such two cases are not well covered by docs. Hope somebody can surprise me!
This is actually how flexbox works:
Flex containers come with flexShrink: 1 by default meaning that they will shrink to their contents. Adding flexShrink: 0 changes that. You may need to use minWidth and minHeight with that instead.
The reason why it stretches is because there's nothing to tell it otherwise. Your container has the default alignItems: 'stretch' overwritten with alignItems: 'center' which shrinks its contents.
Have a look at the example code on Snack: https://snack.expo.io/B1VdUma1M
There's a really nice flexbox cheatsheet/playground that shows the behaviour at: https://yoksel.github.io/flex-cheatsheet/
Bear in mind React Native's implementation is slightly different.

How do i center the Title on React Natives ToolbarAndroid?

I am using the ToolbarAndroid where i need to center the Toolbars Title. I coudn´t figure out how this works. With the Styles properties it didn´t work.
This is my code:
<ToolbarAndroid
title={toolbarTitle(this.props.activeTab)}
titleColor='white'
style={styles.toolbar}
actions={toolbarActions(this.props.activeTab)}
onActionSelected={this._onActionSelected.bind(this)}>
</ToolbarAndroid>
Toolbar style:
toolbar: { height: 50, backgroundColor: 'grey' }
I can render the title properties and other properties depending on which Tab i am (using scrollable-tab-view) but changing the position where my title is placed didn´t work on my approach
You can add a custom view inside the toolbar
<ToolbarAndroid>
<View style={styles.title}>
<Text>Hi there</Text>
</View>
</ToolbarAndroid>
and center its text
var styles = StyleSheet.create({
title: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
marginRight: 28
}
});
A margin was needed to get alignment right.
In some cases I've seen that it was necessary to set an extra style property like backgroundColor for the alignment to work.
By design guidelines toolbar title is aligned to the left. There are some hacky ways to do this in java, but it is probably impossible in react native.
If you don't mind android design guidelines I advise you to use custom component instead of ToolbarAndroid.

React Native <ScrollView> persistent scrollbar

After perusing the React Native Documentation I couldn't seem to find out how to make a <ScrollView> have a persistent scrollbar that doesn't fade out. How would I achieve that?
iOS
The underlying iOS native component, UIScrollView (technically, RCTEnhancedScrollView), doesn't support keeping the scroll indicators visible. For this reason, the React Native wrapper around it won't either.
There is a hack to get this working with the native component (see this answer for one approach). To accomplish this in React Native, you'd need to implement this hack on the native side, and then either create your own Native Module or fork React Native and modify their ScrollView component.
That said, the iOS Scroll View interface guidelines discourage this, so you may want to leave the indicators' behavior alone.
Android
A few approaches:
set <item name="android:overScrollMode">always</item>,
set android:fadeScrollbars="false" in XML, or
set ScrollView.setScrollbarFadingEnabled(false) in Java (e.g. in your custom native bridge code)
This is similarly discouraged as nonstandard UI unless you have a strong reason for it.
Adding answer since none of the above worked for me.
Android now has the persistentScrollbar props.
iOS does not support this. So I created a JS solution that can be used as follows:
<SBScrollView persistentScrollbar={true}>...</SBScrollView>
Basically, this functional component will use persistentScrollbar when on Android, while add a bar when we are on iOS. It is not smooth for now, but it is functional.
// #flow
import React, {useState} from 'react';
import {Platform, View, ScrollView} from 'react-native';
type Props = {|
persistentScrollbar?: boolean,
children?: React$Node,
|} & View.propTypes;
export default function SBScrollView({
persistentScrollbar = false,
children,
...other
}: Props) {
const [nativeEvent, setNativeEvent] = useState();
if (Platform.OS === 'android' || !persistentScrollbar) {
// Abdroid supports the persistentScrollbar
return (
<ScrollView persistentScrollbar={persistentScrollbar} {...other}>
{children}
</ScrollView>
);
}
const top = nativeEvent
? nativeEvent.contentOffset.y +
(nativeEvent.contentOffset.y / nativeEvent.contentSize.height) *
nativeEvent.layoutMeasurement.height
: 0;
// iOS does not support persistentScrollbar, so
// lets simulate it with a view.
return (
<ScrollView
scrollEventThrottle={5}
showsVerticalScrollIndicator={false}
onScroll={event => setNativeEvent(event.nativeEvent)}
{...other}>
{children}
<View
style={{
position: 'absolute',
top,
right: 4,
height: 200,
width: 4,
borderRadius: 20,
backgroundColor: 'gray',
}}
/>
</ScrollView>
);
}
I hope this can help others.
I was looking for a solution but I didn't find nothing, then I created a solution, I hope can help you with it.
I created a view View with height and width and put it over my scrollview, after that I used the Props of scrollview like onMomentumScrollBegin, onMomentumScrollEnd, onContentSizeChange and onScroll
after that I make a condition with a boolean variable, if this variable is false, the View is visible, if is false the View is hide, How do I active this variable? with the Prop onMomentumScrollBegin that detect when you use the scrollView and the same way to set the variable in false with onMomentumScrollEnd that detects when the scroll ends.
The Prop onContentSizeChange allows me to get the height and width of my scrollview, this values I used to calculate where would be set the scrollbar/scrollIndicator
and finally with the Prop onScroll I get the position.
the example:
<ScrollView
onMomentumScrollBegin={() => {this.setvarScrollState()}}
onMomentumScrollEnd={() => {this.setvarScrollStateRev()}}
scrollEventThrottle={5}
onContentSizeChange={(w, h) => this.state.hScroll = h}
showsVerticalScrollIndicator={false}
onScroll={event => { this.state.wScroll = event.nativeEvent.contentOffset.y }}
style={{ marginVertical: 15, marginHorizontal:15, width: this.state.measurements3.width}}>
{
Mydata.map((value, index) => {
return <TouchableOpacity>
<Text>{ value.MyDataItem }</Text>
</TouchableOpacity>
})
}
the functions:
setvarScrollState() {
this.setState({VarScroll: true});
}
setvarScrollStateRev() {
this.setState({VarScroll: false});
}
and the variable
this.state = {VarScroll: false}
Then my condition is
!this.state.VarScroll ?
<View
style = {{
marginTop: 200*(this.state.wScroll / this.state.hScroll),
marginLeft:338.5,
height: 35,
width: 2,
backgroundColor: 'grey',
position:'absolute'
}}
/>
: null
Why 200? because is the maximum value that my marginTop can set
Check the picture
Final note:
the scrollView have to be inside a View with the another View (scrollbar)
something like this
<View>
{/*---- ScrollBar and conditions----*/}
<View>
<View>
<ScrollView>
</ScrollView>
</View>