Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React native multi-column FlatList insert banner

I'm using multi-column FlatList in my React Native application to display items like below (left image). I'm trying to integrate AdMob banner into the application like many other apps did, and insert the ads banner in the middle of the list, like below (right image).

As far as I can tell, FlatList doesn't support this type of layout out-of-the-box. I'm wondering what would be a good practice to implement this feature and doesn't impact app performance.

(Side note, the list supports pull-to-refresh and infinite loading when approaching end of the list.).

Thank you in advance for any suggestions.

enter image description here

like image 902
Allan Jiang Avatar asked Jun 15 '19 03:06

Allan Jiang


1 Answers

In such a case, I always recommend to drop the numColumns property and replace it by a custom render function, which handles the columns by its own.

Let's say we have the following data structure:

const DATA = 
[{ id: 1, title: "Item One"}, { id: 2, title: "Item Two"}, { id: 3, title: "Item Three"}, 
{ id: 4, title: "Item Four"}, { id: 5, title: "Item Five"}, { id: 6, title: "Item Six"}, 
{ id: 7, title: "Item Seven"}, { id:8, title: "Item Eight"}, { id: 9, title: "Item Nine"}, 
{ id: 10, title: "Item Ten"}, { id: 11, title: "Item eleven"}, 
{ id: 12, title: "Item Twelve"}, { id: 13, title: "Item Thirteen"}];

As I said we don't use the numColumns property instead we are restructuring our data so we can render our list how we want. In this case we want to have 3 columns and after six items we want to show an ad banner.

Data Modification:

  modifyData(data) { 
    const  numColumns = 3;
    const addBannerAfterIndex = 6;
    const arr = [];
    var tmp = [];
    data.forEach((val, index) => {
      if (index % numColumns == 0 && index != 0){
        arr.push(tmp);
        tmp = [];
      }
      if (index % addBannerAfterIndex == 0 && index != 0){
        arr.push([{type: 'banner'}]);
        tmp = [];
      }
      tmp.push(val);
    });
    arr.push(tmp);
    return arr; 
  }

Now we can render our transformed data:

Main render function:

render() {
    const newData = this.modifyData(DATA); // here we can modify the data, this is probably not the spot where you want to trigger the modification 
    return (
      <View style={styles.container}>
        <FlatList 
        data={newData}
        renderItem={({item, index})=> this.renderItem(item, index)}
        /> 
      </View>
    );
}

RenderItem Function:

I removed some inline styling to make it more clearer.

renderItem(item, index) {
    // if we have a banner item we can render it here 
    if (item[0].type == "banner"){
      return (
         <View key={index} style={{width: WIDTH-20, flexDirection: 'row'}}>
        <Text style={{textAlign: 'center', color: 'white'}}> YOUR AD BANNER COMPONENT CAN BE PLACED HERE HERE </Text>
      </View>
      )
    }

    //otherwise we map over our items and render them side by side 
     const columns = item.map((val, idx) => {
      return (
        <View style={{width: WIDTH/3-20, justifyContent: 'center', backgroundColor: 'gray', height: 60, marginLeft: 10, marginRight: 10}} key={idx}>
          <Text style={{textAlign: 'center'}}> {val.title} </Text>
        </View>
      )
    });
    return (
      <View key={index} style={{width: WIDTH, flexDirection: 'row', marginBottom: 10}}>
      {columns}
      </View>
    )
  }

Output:

output

Working Example:

https://snack.expo.io/SkmTqWrJS

like image 88
Tim Avatar answered Sep 21 '22 06:09

Tim