175

I'm trying to create a react native app that looks like an existing web app. I have a fixed footer at bottom of the window. Does anyone have an idea how this can be achieved with react native?

In the existing app it's simple:

.footer {
  position: fixed;
  bottom: 0;
}
Molbac
  • 81
  • 9
4ega
  • 1,992
  • 2
  • 12
  • 15

23 Answers23

244

Here's the actual code based on Colin's Ramsay answer:

<View style={{flex: 1}}>
  <ScrollView>main</ScrollView>
  <View><Text>footer</Text></View>
</View>
Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
Alexander Zaytsev
  • 2,744
  • 1
  • 16
  • 8
  • 3
    Yes I tried it but without flex didnt work :D Thanks for push to try it again :) And if you clicked on input i want to mention to use onContentSizeChange. So what I did I scrolled scrollview upper like this: onContentSizeChange={ (width, height) => this.refs.scrollView.scrollTo({ y: this.state.onInputSelectScrollViewPaddingSize} ) } – Ernestyno Mar 20 '18 at 13:08
  • 2
    it dont works. i cant see why it would work on any case – Paulo Roberto Rosa Jan 29 '19 at 12:49
224

Off the top of my head you could do this with a ScrollView. Your top-level container could be a flex container, inside that have a ScrollView at the top and your footer at the bottom. Then inside the ScrollView just put the rest of your app as normal.

locropulenton
  • 4,743
  • 3
  • 32
  • 57
Colin Ramsay
  • 16,086
  • 9
  • 52
  • 57
  • 1
    works great =) thx, just added ```height``` to footer View and it's looks good on 4s and 6 – 4ega Apr 05 '15 at 09:00
  • 1
    This works. But I couldn't understand why. Why does this work? – Aditi Mar 14 '19 at 10:01
  • 2
    @Aditi If I understood correctly, it's because the ScrollView is set to use all the height available on the screen. The second container would just take the space it needs - for example, if you set it to `height: 10`, it would take a height of 10. So there would be two components, the first would take all the space available, whereas the second would just take what it needs, and it would start where the first one ends - which is at the bottom of the screen. – Rodrigo Vieira Sep 20 '20 at 05:19
82

I'm using fixed footers for buttons in my app. The way I implement a fixed footer is like so:

<View style={{flex: 1}}>
<View><Text>my text</Text></View>
<View style={{position: 'absolute', left: 0, right: 0, bottom: 0}}><Text>My fixed footer</Text></View>
</View>

And if need the footer to move up when a keyboard appears for instance you can use:

const {  DeviceEventEmitter } = React

class MyClass {
  constructor() {
     this.state = {
       btnLocation: 0
     }
  }

  componentWillMount() {
     DeviceEventEmitter.addListener('keyboardWillShow', this.keyboardWillShow.bind(this))
     DeviceEventEmitter.addListener('keyboardWillHide', this.keyboardWillHide.bind(this))
  }

  keyboardWillShow(e) {
    this.setState({btnLocation: e.endCoordinates.height})
  }

  keyboardWillHide(e) {
    this.setState({btnLocation: 0})
  }
}

Then use {bottom: this.state.btnLocation} in your fixed footer class. I hope this helps!

dahveed707
  • 907
  • 6
  • 14
  • 2
    Anyone getting 'undefined is not an object (evaluating "DeviceEventEmitter.addListener")' when trying to do 'this.setState(...)' on the keyboard listeners? – John Sardinha Oct 22 '17 at 11:52
  • @JohnSardinha Try `import { Keyboard} from 'react-native'; Keyboard.addListener('keyboardWillShow', this.showHandler)` instead. – maxhungry Nov 28 '17 at 02:59
28

You get the Dimension first and then manipulate it through flex style

var Dimensions = require('Dimensions')
var {width, height} = Dimensions.get('window')

In render

<View style={{flex: 1}}>
    <View style={{width: width, height: height - 200}}>main</View>
    <View style={{width: width, height: 200}}>footer</View>
</View>

The other method is to use flex

<View style={{flex: 1}}>
    <View style={{flex: .8}}>main</View>
    <View style={{flex: .2}}>footer</View>
</View>
syarul
  • 2,161
  • 2
  • 20
  • 22
17

@Alexander Thanks for solution

Below is code exactly what you looking for

import React, {PropTypes,} from 'react';
import {View, Text, StyleSheet,TouchableHighlight,ScrollView,Image, Component, AppRegistry} from "react-native";

class mainview extends React.Component {
 constructor(props) {
      super(props);

  }

  render() {
    return(
      <View style={styles.mainviewStyle}>
        <ContainerView/>
          <View style={styles.footer}>
          <TouchableHighlight style={styles.bottomButtons}>
              <Text style={styles.footerText}>A</Text>
          </TouchableHighlight>
          <TouchableHighlight style={styles.bottomButtons}>
              <Text style={styles.footerText}>B</Text>
          </TouchableHighlight>
          </View>
      </View>
    );
  }
}

class ContainerView extends React.Component {
constructor(props) {
      super(props);
}

render() {
    return(
      <ScrollView style = {styles.scrollViewStyle}>
          <View>
            <Text style={styles.textStyle}> Example for ScrollView and Fixed Footer</Text>
          </View>
      </ScrollView>
    );
  }
}

var styles = StyleSheet.create({
  mainviewStyle: {
  flex: 1,
  flexDirection: 'column',
},
footer: {
  position: 'absolute',
  flex:0.1,
  left: 0,
  right: 0,
  bottom: -10,
  backgroundColor:'green',
  flexDirection:'row',
  height:80,
  alignItems:'center',
},
bottomButtons: {
  alignItems:'center',
  justifyContent: 'center',
  flex:1,
},
footerText: {
  color:'white',
  fontWeight:'bold',
  alignItems:'center',
  fontSize:18,
},
textStyle: {
  alignSelf: 'center',
  color: 'orange'
},
scrollViewStyle: {
  borderWidth: 2,
  borderColor: 'blue'
}
});

AppRegistry.registerComponent('TRYAPP', () => mainview) //Entry Point    and Root Component of The App

Below is the Screenshot

ScrollView With Fixed Footer

Ashok R
  • 19,892
  • 8
  • 68
  • 68
10

Simple stuff here:

Incase you don't need a ScrollView for this approach you can go with the below code to achieve Something like this :

Something like this

<View style={{flex: 1, backgroundColor:'grey'}}>
    <View style={{flex: 1, backgroundColor: 'red'}} />
    <View style={{height: 100, backgroundColor: 'green'}} />
</View>
LuFFy
  • 8,799
  • 10
  • 41
  • 59
Cherag Verma
  • 299
  • 2
  • 9
7

You might also want to take a look at NativeBase (http://nativebase.io). This a library of components for React Native that include some nice layout structure (http://nativebase.io/docs/v2.0.0/components#anatomy) including Headers and Footers.

It's a bit like Bootstrap for Mobile.

Simon Ordish
  • 137
  • 2
  • 4
7

The way I did this was to have a view (lets call it P) with flex 1, then inside that view have 2 more views (C1 and C2) with flex 0.9 and 0.1 respectively (you can change the flex heights to required values). Then, inside the C1 have a scrollview. This worked perfectly for me. Example below.

<View style={{flex: 1}}>
    <View style={{flex: 0.9}}>
        <ScrollView>
            <Text style={{marginBottom: 500}}>scrollable section</Text>
        </ScrollView>
    </View>
    <View style={{flex: 0.1}}>
        <Text>fixed footer</Text>
    </View>
</View>
Rajesh
  • 3,743
  • 1
  • 24
  • 31
  • Adding on top of this, the left, right and bottom styles of value 0 must be supplied in order for it to work. – IVI Sep 02 '17 at 11:25
7

When flex is a positive number, it makes the component flexible and it will be sized proportional to its flex value. So a component with flex set to 2 will take twice the space as a component with flex set to 1.

   <View style={{flex: 1}>
            
     <ScrollView style={{flex: 1}>
        //your scroll able content will be placed above your fixed footer content. 
        //when your content will grow bigger and bigger it will hide behind 
        //footer content. 
     </ScrollView>

     <View style={styles.footerContainer}>
        //your fixed footer content will sit fixed below your screen 
     </View>

</View>
iambinodstha
  • 937
  • 10
  • 11
6

Below is code to set footer and elements above.

import React, { Component } from 'react';
import { StyleSheet, View, Text, ScrollView } from 'react-native';
export default class App extends Component {
    render() {
      return (
      <View style={styles.containerMain}>
        <ScrollView>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
          <Text> Main Content Here</Text>
        </ScrollView>
        <View style={styles.bottomView}>
          <Text style={styles.textStyle}>Bottom View</Text>
        </View>
      </View>
    );
  }
}
const styles = StyleSheet.create({
  containerMain: {
    flex: 1,
    alignItems: 'center',
  },
  bottomView: {
    width: '100%',
    height: 50,
    backgroundColor: '#EE5407',
    justifyContent: 'center',
    alignItems: 'center',
    position: 'absolute',
    bottom: 0,
  },
  textStyle: {
    color: '#fff',
    fontSize: 18,
  },
});
ftoyoshima
  • 363
  • 5
  • 10
shruti garg
  • 332
  • 3
  • 12
5

One could achieve something similar in react native with position: absolute

let footerStyle = {
  position: 'absolute',
  bottom: 0,
}

There are a few things to keep in mind though.

  1. absolute positions the element relative to its parent.
  2. You might have to set the width and hight of the element manually.
  3. Width and hight will change when orientation changes. This has to be managed manually

A practical style definition would look something like this:

import { Dimensions } from 'react-native';

var screenWidth = Dimensions.get('window').width; //full screen width

let footerStyle = {
  position: 'absolute',
  bottom: 0,
  width: screenWidth,
  height: 60
}
Akash
  • 13,107
  • 1
  • 25
  • 19
5

Suggestion 1

=> Body with fixed footer

<View style={{ flex: 1, backgroundColor: 'gray' }}>

        <View style={{ flex: 9, backgroundColor: 'gray',alignItems: 'center', justifyContent: 'center',  }}>
          <Text style={{color:'white'}}>...Header or Body</Text>
        </View>


        <View style={{ flex: 1, backgroundColor: 'yellow', alignItems: 'center', justifyContent: 'center', }}>
          <Text>...Footer</Text>
        </View>

</View>

Demo Image

Edit 2

=> Body & Fixed footer with tabs

<View style={{ flex: 1, backgroundColor: 'gray' }}>

        <View style={{ flex: 9, backgroundColor: 'gray', alignItems: 'center', justifyContent: 'center', }}>
          <Text style={{ color: 'white' }}>...Header or Body</Text>
        </View>


        <View style={{ flex: 1, backgroundColor: 'yellow', alignItems: 'center', justifyContent: 'center', }}>
          <View style={{ flex: 1, flexDirection: 'row' }}>
            <TouchableOpacity style={{ flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: 'white' }}>
              <View>
                <Text>
                  ...Home
              </Text>
              </View>
            </TouchableOpacity>
            <TouchableOpacity style={{ flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: 'white' }}>
              <View>
                <Text>
                  ...Settings
              </Text>
              </View>
            </TouchableOpacity>
          </View>
        </View>
</View>

enter image description here

Notes

import {TouchableOpacity} from 'react-native'

Advantages

We can use this simple footer without react bottom navigation

Hardik Desai
  • 1,089
  • 12
  • 20
4

The best way is to use justifyContent property

<View style={{flexDirection:'column',justifyContent:'flex-end'}}>
     <View>
        <Text>fixed footer</Text>
    </View>
</View>

if you have multiple view elements on screen, then you can use

<View style={{flexDirection:'column',justifyContent:'space-between'}}>
     <View>
        <Text>view 1</Text>
    </View>
    <View>
        <Text>view 2</Text>
    </View>
    <View>
        <Text>fixed footer</Text>
    </View>
</View>
Manzoor Samad
  • 889
  • 1
  • 9
  • 16
4

I found using flex to be the simplest solution.

<View style={{flex:1, 
    justifyContent: 'space-around', 
    alignItems: 'center',
    flexDirection: 'row',}}>

  <View style={{flex:8}}>
    //Main Activity
  </View>
  <View style={{flex:1}}>
    //Footer
  </View>

 </View>
Colin
  • 1,112
  • 1
  • 16
  • 27
saiftyfirst
  • 111
  • 2
  • 7
4
import {Dimensions} from 'react-native'

const WIDTH = Dimensions.get('window').width;
const HEIGHT = Dimensions.get('window').height;

then on the write this styles

 position: 'absolute',
 top: HEIGHT-80,
 left: 0,
 right: 0,

worked like a charm

Harry Moreno
  • 10,231
  • 7
  • 64
  • 116
Joseph Owigo
  • 430
  • 3
  • 10
2

if you just use react native so you can use the following code

<View style={{flex:1}}>

{/* Your Main Content*/}
<View style={{flex:3}}>

<ScrollView>
   {/* Your List View ,etc */}
</ScrollView>

</View>

{/* Your Footer */}
<View style={{flex:1}}>
   {/*Elements*/}
</View>


 </View>

also, you can use https://docs.nativebase.io/ in your react native project and then do something like the following

<Container>

{/*Your Main Content*/}
<Content>

<ScrollView>
   {/* Your List View ,etc */}
</ScrollView>

</Content>

{/*Your Footer*/}
<Footer>
   {/*Elements*/}
</Footer>

</Container>

React_Native

NativeBase.io

Ryan Didevar
  • 512
  • 4
  • 14
1

For Android problems with this:

in app/src/AndroidManifest.xml change windowSoftInputMode to the following.

<activity
   android:windowSoftInputMode="stateAlwaysHidden|adjustPan">

I had absolutely no problems with this in ios using react-native and keyboardAwareScroll. I was about to implement a ton of code to figure this out until someone gave me this solution. Worked perfectly.

Lisa Faye Cook
  • 111
  • 1
  • 4
1

Set android:windowSoftInputMode="adjustPan" in your manifest file, and it will work as you expect.

1

I've used a combination of height: 100% and flex: 1.

<View style={{ height: "100%" }}>
      <View
        style={{
          display: "flex",
          flexDirection: "row",
          alignItems: "center",
          height: 50,
        }}
      >
        {R.map(
          tab => (
            <TouchableOpacity
              key={tab.id}
              onPress={() => setCurrentTab(tab)}
            >
              <Text>{tab.name}</Text>
            </TouchableOpacity>
          ),
          tabs
        )}
      </View>
      <View style={{ flex: 1 }}>
        <View style={{ height: "100%" }}>
          <View style={{ flex: 1 }}>
           <ScrollView
             style={{
              width: "100%",
             }}
           >
           ... ScrollView content
           </ScrollView>
          </View>
         <View
        style={{
          borderTopColor: "#dadada",
          borderTopWidth: 1,
          width: "100%",
          alignItems: "center",
          justifyContent: "center",
          height: 60,
          paddingBottom: 10,
        }}
      >
        <TouchableOpacity
          style={{
            padding: 8,
            borderRadius: 3,
          }}
        >
          <Text>
            Show Results
          </Text>
        </TouchableOpacity>
      </View>
      </View>
</View>
Alessander França
  • 2,697
  • 2
  • 29
  • 52
1

Easy way to fix footer at the bottom:

import { StyleSheet, Text, View } from 'react-native'
import React from 'react'

const Footer = () => {
    return (
        <View style={styles.footer}>
            <Text style={{ color: 'black', fontSize: 15 }}>All rights reserved, 2022</Text>
        </View>
    )
}

const styles = StyleSheet.create({
    footer: {
        position: 'absolute',
        bottom: 0,
        left: 0,
        right: 0,
        backgroundColor: '#F4CE14',
        padding: 10,
        justifyContent: 'center',
        alignItems: 'center',
    },
})

export default Footer;
  • Make sure to always explain why your code works, and/or why the original doesn't! [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer) – William Brochensque junior Mar 03 '23 at 18:02
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 03 '23 at 18:02
0

I think best and easy one would be as below, just place rest of ur view in a content and footer in a separate view.

`<Container>
   <Content>
     <View>
      Ur contents
    </View>
  </Content>
  <View>
  Footer
  </View>
</Container>`

or u can use footer from native-base

`<Container>
  <Content>
    <View>
Ur contents
    </View>
  </Content>
<Footer>
Footer
</Footer>
</Container>`
0

i created a package. it may meet your needs.

https://github.com/caoyongfeng0214/rn-overlaye

<View style={{paddingBottom:100}}>
     <View> ...... </View>
     <Overlay style={{left:0, right:0, bottom:0}}>
        <View><Text>Footer</Text></View>
     </Overlay>
</View>
0

Create a Style like this:

const styles = StyleSheet.create({
  header:{ backgroundColor: "#00BFFF", height: "20%" },
  footer:{ backgroundColor: "royalblue", height: "10%", flexDirection: "row", alignItems: "center" }
});

Then use the style in a tag :

            <View style={styles.footer}>
                <View style={{ flex: 1, alignItems: "center" }}>
                    <Pressable onPress={() => openCamera(true)}>
                        <View style={{ flexDirection: "column", alignItems: "center" }}>
                            <Icon name="camera" style={{ fontSize: 21, color: "white" }}/>
                            <Text style={{ color: "white" }}>Photo</Text>
                        </View>
                    </Pressable>
                </View>
</View>
Leandro Ariel
  • 727
  • 8
  • 5