3

Iv'e been working with react-native for a couple of weeks now and I have reached a point where I need to support both orientations. Allegedly flex-box should do its magic and automatically adapt the layout to the new screen size. However flex-box does not handle the changes as I would wish it were, I am not saying it does not do what is supposed to do I am saying I am just not happy with it.

To be more concrete, I found this example online which basically describes the problem I am trying to solve (only they do it with native views and auto-layout). I want to achieve layout changes on device rotation (or more generally on size changes) similar to those applied to that "mini Instagram" example I linked.

How can I do that with react-native and flex-box? Should I use different components for different screen sizes or is there a proper way to achieve that?

Daniel Lahyani
  • 925
  • 8
  • 15

2 Answers2

6

There's not much work to do to achieve this with react native. Here I have tried to demonstrate similar visual layout as you have asked.

enter image description here

We just need to listen for onLayout event of view that gets invoke whenever there is a change in orientation.

By comparing the height and width from Dimension.get('window') we can figure out the whether the device is in portrait or landscape mode. Here's the simple code for this

import React, { Component } from 'react';

import {
  StyleSheet,
  Text,
  View,
  Image,
  Dimensions
} from 'react-native';

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

export default class Com extends Component{
  constructor(){
    console.log('constructor');
    super();
    this.state = {
      layout:{
        height:height,
        width:width,

      }
    };

  }
  _onLayout = event => {

    console.log('------------------------------------------------' + JSON.stringify(event.nativeEvent.layout));

    this.setState({
      layout:{
        height:event.nativeEvent.layout.height,
        width:event.nativeEvent.layout.width,

      }
    });
  }


  render(){
    console.log(JSON.stringify(this.props));
    var imagel = this.props.list[0].src;
    var landscapeView =
                        <View style={{marginTop:20, flexDirection:'row'}}>
                          <Image source={require('./flower.jpg')} style={{height:this.state.layout.height-50, width:this.state.layout.width/2}}/>
                          <View>
                            <Text style={{backgroundColor:'gray', margin:5}}> header this is landscape view </Text>
                            <Text style={{backgroundColor:'gray',margin:5}}> footer this is portrait view  </Text>
                          </View>


                        </View>
    var portraitView = <View style={{marginTop:20}}>
                        <Text style={{backgroundColor:'gray', margin:5}}> header this is landscape view </Text>
                          <Image source={require('./flower.jpg')} style={{height:200, width:this.state.layout.width}}/>
                          <Text style={{backgroundColor:'gray',margin:5}}> footer this is portrait view  </Text>
                        </View>

    var cview =null;
    if (this.state.layout.height>this.state.layout.width) {
      cview = portraitView
    }else{
      cview = landscapeView;
    }
    return(
      <View style={{backgroundColor:'red', flex:1}}   onLayout={this._onLayout}>
      {cview}
      </View>
    );
  }
}

What's happening here is we are listening to event onLayout which are triggered when there is a orientation change. Then we have state variable which hold the actual height and width of the screen to reflect the orientation change. we are using this state height and width for view design.

Two things is happening here.

  1. onLayout get invoked upon orientation change.
  2. upon changing state value, view will render again.
Rajan Twanabashu
  • 4,586
  • 5
  • 43
  • 55
  • Thanks. I am familiar with the onLayout event. What you suggest is basically equivalent to having two different components - one for portrait and one for landscape. – Daniel Lahyani Jul 05 '16 at 13:33
  • yes , correct. it's because the layout is completely different in both orientation. – Rajan Twanabashu Jul 06 '16 at 06:05
  • How can I pass this value to my ```style.js``` file(another separate file) where I need the width and height of different orientations for styling? – Zip Feb 23 '17 at 02:04
1

I don't think RN sends any rotation events from native to JS currently, but you will get a layout callback, so you can update your layout metrics in response to that. e.g.:

getInitialState(): {
  return {isPortrait: true}; // You could use Dimensions to get this right initially?
},

render(): {
  // return a different layout depending on this.state.isPortrait
  return <View onLayout={this.onLayout} />
},

onLayout(e): {
  var isPortrait = e.nativeEvent.layout.height > e.nativeEvent.layout.width;
  if (isPortrait != this.state.isPortrait) {
    this.setState({isPortrait});
  }
}

Note: since JS view updates and layout all happen asynchronously, it will be hard to match the timing of the actual device rotation change.

Javache
  • 3,398
  • 3
  • 21
  • 25
  • I know how to recognize a size change (as you suggested providing the onLayout callback). The question is what's the proper way to handle it? If I need a layout change like the example I attached the rendering method should change vastly, so maybe I should have 2 render methods in the same component or maybe I should have 2 different components? – Daniel Lahyani Feb 04 '16 at 07:14
  • It depends on the complexity of your view tree really. In the example you linked it might be as simple as using a different Stylesheet when in portrait than in landscape, but in other cases 2 render methods might be the better approach. React itself doesn't really care about the difference between these approaches. – Javache Feb 05 '16 at 12:07