Shrikant
Shrikant

Reputation: 334

Responsive React-native-web layout

I'm trying to create a dashboard layout from a react-native project. The idea is to keep most of the code similar for Android, iOS and Web only the layout or navigation style will change. But I find making this type of layout in web is easy but to make it responsive without re-rendering is difficult.

I have achieved this by manually calculating the windows heigh and width by following the code

Dimensions.get('window').width Dimensions.get('window').height

and by eventListener keep updating the state so that it re-renders the whole page again and again.

Dimensions.addEventListener("change", this.updateScreen);

Is there a way I can simply use some % value to fill up the screen. Right now if I use % it squeezed to a child View size. I even tried flex:1 with alignSelf:stretch, alignItem:stretch, width:'100%' etc but no luck.

For a while, let's talk about center row (image attached) it contain 3 columns. I want left and right block (Menu & Call to Action) to be 300px each. Now if I'm on 1000px width monitor my Content block should be (1000 - (300+300)) 400px. if monitor is 1200px then Content block will be (1200 - (300+300)) 600px.

enter image description here

Upvotes: 3

Views: 1233

Answers (2)

Mehmet Kaplan
Mehmet Kaplan

Reputation: 2342

I made this exactly for the same reasons:

https://www.npmjs.com/package/react-native-animated-layout

Demos:

Expo snack: https://snack.expo.dev/@mehmetkaplan/react-native-animated-layout

Web demo: https://mehmetkaplan.github.io/react-native-animated-layout/

It takes layouts as inputs and using the Width / Height ratio of the screen, decides which layout to use.

And within each layout, you define top, right, bottom, left of each screen using the coordinates between 0 and 1. (Because browser resizing changes the coordinates of each View and you want views to stay exact relative places.)

import React, { useState, useEffect } from 'react';
import { Text, View } from 'react-native';
import ReactNativeAnimatedLayout from 'react-native-animated-layout';


export default function App() {
    const [rerender, setRerender] = useState(0);
    const redView = <View style={{ backgroundColor: 'red', height: '100%', width: '100%', }}><Text>{"I am the red view"}</Text></View>;
    const greenView = <View style={{ backgroundColor: 'green', height: '100%', width: '100%', overflow: 'hidden'}}>
        <Text>{"I am the green view and my contents overflow. Because of the overflow prop, the over-flown content is hidden."}</Text><Text>{"0"}</Text><Text>{"1"}</Text><Text>{"2"}</Text><Text>{"3"}</Text><Text>{"4"}</Text><Text>{"5"}</Text><Text>{"6"}</Text><Text>{"7"}</Text><Text>{"8"}</Text><Text>{"9"}</Text><Text>{"10"}</Text><Text>{"11"}</Text><Text>{"12"}</Text><Text>{"13"}</Text><Text>{"14"}</Text><Text>{"15"}</Text><Text>{"16"}</Text><Text>{"17"}</Text><Text>{"18"}</Text><Text>{"19"}</Text><Text>{"20"}</Text><Text>{"21"}</Text><Text>{"22"}</Text><Text>{"23"}</Text><Text>{"24"}</Text><Text>{"25"}</Text><Text>{"26"}</Text><Text>{"27"}</Text><Text>{"28"}</Text><Text>{"29"}</Text>
        </View>;
    const blueView = <View style={{ backgroundColor: 'blue', height: '100%', width: '100%', }}><Text>{"I am the blue view"}</Text></View>;
    const layouts = [
        {
            validAfterWHRatio: 0,
            validBeforeWHRatio: 0.9,
            views: [
                { top: 0, bottom: 0.5, left: 0, right: 1, children: redView },
                { top: 0.75, bottom: 1, left: 0, right: 1, children: blueView },
                { top: 0.5, bottom: 0.75, left: 0, right: 1, children: greenView },
            ]
        },
        {
            validAfterWHRatio: 1 / 0.62,
            validBeforeWHRatio: 999, // infinity
            views: [
                { top: 0, bottom: 1, left: 0, right: 0.5, children: redView },
                { top: 0.5, bottom: 1, left: 0.5, right: 1, children: blueView },
                { top: 0, bottom: 0.5, left: 0.5, right: 1, children: greenView },
            ]
        },
        {
            defaultFlag: true,
            views: [
                { top: 0.16, bottom: 0.84, left: 0.16, right: 0.5, children: redView },
                { top: 0.50, bottom: 0.84, left: 0.5, right: 0.84, children: blueView },
                { top: 0.16, bottom: 0.50, left: 0.5, right: 0.84, children: greenView },
            ]
        },
    ];

    useEffect(() => {
        let nextRerender = rerender + 1;
        setRerender(nextRerender);
    }, []); // when we want to re-render

    return <ReactNativeAnimatedLayout
        layouts={layouts}
        rerender={rerender}
    />;
}

Note: This approach may be useful especially for PWAs. If you need to use scroll bars, they should be inside one of the views, probably "the" content view.

Upvotes: 0

cltsang
cltsang

Reputation: 1829

I hope this doesn't come too late.

  <View style={{ flex: 1 }}>
    <View style={{ height: 100, backgroundColor: 'red' }} />

    <View style={{ flex: 1, backgroundColor: 'gray', flexDirection: 'row' }}>
      <View style={{ width: 100, backgroundColor: 'green' }} />
      <View style={{ flex: 1, backgroundColor: 'blue' }} />
      <View style={{ width: 100, backgroundColor: 'green' }} />
    </View>

    <View style={{ height: 100, backgroundColor: 'red' }} />
  </View>

This is the result from the above code. result

You don't need to do percentage calculations at all; just structure it in 2 layers of flex layout.

For the components that should not stretch, state their width. For the rest, specify the flex value.

If you insist on using 1 layer to handle it all, then we shall discuss again.

Cheers.

Upvotes: 3

Related Questions