<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Untitled Publication]]></title><description><![CDATA[Untitled Publication]]></description><link>https://devfabi.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1664998606348/VwCRdRVfcw.png</url><title>Untitled Publication</title><link>https://devfabi.com</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 13 Oct 2026 01:39:56 GMT</lastBuildDate><atom:link href="https://devfabi.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[React Native - Dynamic Flatlist]]></title><description><![CDATA[Introduction
Hi, I'm Fabian and this is my first blog post. I recently started learning about React Native. The other day, I was looking for a way to dynamically adjust the columns of a React Native Flatlist based on the screen size and orientation (...]]></description><link>https://devfabi.com/react-native-dynamic-flatlist</link><guid isPermaLink="true">https://devfabi.com/react-native-dynamic-flatlist</guid><category><![CDATA[React]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[React Native]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[flatlist]]></category><dc:creator><![CDATA[Fabian Grzesiak]]></dc:creator><pubDate>Wed, 05 Oct 2022 21:05:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1665001424219/vgCZsSczx.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-introduction">Introduction</h3>
<p>Hi, I'm Fabian and this is my first blog post. I recently started learning about <a target="_blank" href="https://reactnative.dev/">React Native</a>. The other day, I was looking for a way to dynamically adjust the columns of a <a target="_blank" href="https://reactnative.dev/docs/flatlist">React Native Flatlist</a> based on the screen size and orientation (horizontal or vertical) of the device. By the end of this article, you'll be able to create such a <code>Flatlist</code>: </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664909975114/5BINw7fbr.gif" alt="output-onlinegiftools (3).gif" /></p>
<h3 id="heading-basics">Basics</h3>
<p>First, let's start with a simple dataset for the <code>data</code> property of our <code>Flatlist</code>. For this example, let's take a <code>data</code> array that contains objects that each have just a <code>key</code> with an increasing number.</p>
<p>Now, we can already represent this data with a few lines of code. In addition to the <code>data</code> property of the <code>Flatlist</code>, we also need the <code>renderItem</code> prop, which in this case simply makes use of the keys of our array that we just created.</p>
<pre><code><span class="hljs-keyword">import</span> { FlatList } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-native'</span>;

<span class="hljs-keyword">const</span> data = [
  { <span class="hljs-attr">key</span>: <span class="hljs-string">'1'</span> }, { <span class="hljs-attr">key</span>: <span class="hljs-string">'2'</span> }, { <span class="hljs-attr">key</span>: <span class="hljs-string">'3'</span> }, { <span class="hljs-attr">key</span>: <span class="hljs-string">'4'</span> }, { <span class="hljs-attr">key</span>: <span class="hljs-string">'5'</span> }, 
  { <span class="hljs-attr">key</span>: <span class="hljs-string">'6'</span> }, { <span class="hljs-attr">key</span>: <span class="hljs-string">'7'</span> }, { <span class="hljs-attr">key</span>: <span class="hljs-string">'8'</span> }, { <span class="hljs-attr">key</span>: <span class="hljs-string">'9'</span> }
];

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">FlatList</span>
        <span class="hljs-attr">data</span>=<span class="hljs-string">{data}</span>
        <span class="hljs-attr">renderItem</span>=<span class="hljs-string">{({item})</span> =&gt;</span> { return item.key}}
      /&gt;</span>
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre><p>This list is still a long way from our desired result, but it already represents our desired data. If we now want to display this list in columns, we can use the <code>numColumns</code> property. For this, we define a constant with a value of <code>3</code> (we will change that in a moment).</p>
<p>At the same time, let's define a <code>style</code> that will center the objects, give them a background, and set them to a fixed height and width to form rectangles. In order for us to be able to see these, we also set a <code>margin</code> to 1. Furthermore, let's define a <code>flex</code> of 1 on a <code>view</code> enclosing the text to ensure automatic adjustment of the width of the rectangles.</p>
<pre><code><span class="hljs-keyword">const</span> numColumns = <span class="hljs-number">3</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">FlatList</span>
        <span class="hljs-attr">data</span>=<span class="hljs-string">{data}</span>
        <span class="hljs-attr">numColumns</span>=<span class="hljs-string">{numColumns}</span>
        <span class="hljs-attr">renderItem</span>=<span class="hljs-string">{({item})</span> =&gt;</span> {
          return (
          <span class="hljs-tag">&lt;<span class="hljs-name">View</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{styles.item}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{styles.itemText}</span>&gt;</span>{item.key}<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">View</span>&gt;</span>)
        }}
      /&gt;</span>
    );
}


<span class="hljs-keyword">const</span> styles = StyleSheet.create({
  <span class="hljs-attr">item</span>: {
    <span class="hljs-attr">backgroundColor</span>: <span class="hljs-string">'#A1A1A1'</span>,
    <span class="hljs-attr">alignItems</span>: <span class="hljs-string">'center'</span>,
    <span class="hljs-attr">justifyContent</span>: <span class="hljs-string">'center'</span>,
    <span class="hljs-attr">flex</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">margin</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">height</span>: <span class="hljs-number">120</span>,
    <span class="hljs-attr">width</span>: <span class="hljs-number">90</span>
  },
  <span class="hljs-attr">itemText</span>: {
    <span class="hljs-attr">color</span>: <span class="hljs-string">'#fff'</span>,
  },
});
</code></pre><h3 id="heading-empty-items">Empty items</h3>
<p>Since we currently have nine elements, which we represent in three columns, we have three rows with three elements each. This makes everything look evenly distributed. However, if we now remove an element, we see that this is no longer the case.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664913785367/Ubruoer7u.png" alt="image.png" class="image--center mx-auto" /></p>
<p>Due to the added <code>flex</code> of 1, <code>React Native</code> adjusts the <code>width</code> of each object of each row to fill the entire width of the screen (except the <code>margin</code>). If this is your desired result, you can skip to the next header. If not, let's define a new function called <code>formatData</code>. </p>
<p>This function takes two parameters, <code>data</code> and <code>numColumns</code>. Given these parameters, we can calculate how many empty objects we need to add to our existing <code>data</code> array to fill the last row. If, as in this case, we have two items in the last row, we want to add an empty object to that row to reflect the column count of <code>3</code>.</p>
<pre><code><span class="hljs-keyword">const</span> formatData = <span class="hljs-function">(<span class="hljs-params">data, numColumns</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> amountFullRows = <span class="hljs-built_in">Math</span>.floor(data.length / numColumns);
  <span class="hljs-keyword">let</span> amountItemsLastRow = data.length - amountFullRows * numColumns;

  <span class="hljs-keyword">while</span> (amountItemsLastRow !== numColumns &amp;&amp; amountItemsLastRow !== <span class="hljs-number">0</span>) {
    data.push({<span class="hljs-attr">key</span>: <span class="hljs-string">`empty-<span class="hljs-subst">${amountItemsLastRow}</span>`</span>, <span class="hljs-attr">empty</span>: <span class="hljs-literal">true</span>});
    amountItemsLastRow++;
  }
  <span class="hljs-keyword">return</span> data;
};
</code></pre><p>This method first calculates the number of full rows and subtracts that multiplied by the number of columns from our total number of objects in the <code>data</code> array to calculate the number of objects left over.</p>
<p>Practically speaking, it looks at how many elements have already been <em>served </em>and still have to be <em>served</em>. This number can be either <code>0</code> if all have already been resolved, or less than the number of columns. Therefore, in the last step, new objects are added to the <code>data</code> array until the number of elements in the last row corresponds to the required number of columns (<code>amountItemsLastRow === numColumns</code>).</p>
<p>It is important to note that the added objects must also receive a unique <code>key</code>. The boolean <code>empty</code> is used in the <code>renderItem</code> prop to display an empty object as such. To keep things simple, we're also relocating the <code>renderItem</code> as a standalone method.</p>
<pre><code><span class="hljs-keyword">const</span> renderItem = <span class="hljs-function">(<span class="hljs-params">{ item, index }</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (item.empty) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">View</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{[styles.item,</span> <span class="hljs-attr">styles.itemTransparent</span>]} /&gt;</span></span>;
    }
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">View</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{styles.item}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{styles.itemText}</span>&gt;</span>{item.key}<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">View</span>&gt;</span></span>
    );
  };

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">FlatList</span>
        <span class="hljs-attr">...</span>
        <span class="hljs-attr">renderItem</span>=<span class="hljs-string">{renderItem}</span>
      /&gt;</span></span>
    );
}
</code></pre><p>As you may have noticed, there is now an additional <code>style</code> that is applied in the <code>renderItem</code> function if the object has the boolean empty, and it is <code>true</code> to make the background transparent.</p>
<h3 id="heading-dynamic-numcolumns">Dynamic numColumns</h3>
<p>Now let's get to the part we've been waiting for: dynamically adjusting the number of columns. In order to achieve this, we need to keep in mind what can happen and how we want our app to react. The user can rotate their phone so that the view can switch from vertical to horizontal mode or vice versa. Another example is Samsung's foldables, which can change their view by opening or closing the device.</p>
<p>Since this also changes the <code>width</code> and <code>height</code> of the <code>window</code> each time, we track the <code>width</code> using the <a target="_blank" href="https://reactnative.dev/docs/usewindowdimensions">useWindowsDimesions</a> hook for this application. This allows the <code>width</code> to be destructured at the beginning of our component.</p>
<pre><code><span class="hljs-keyword">const</span> {width} = useWindowDimensions();
</code></pre><p>By using the <a target="_blank" href="https://reactjs.org/">React</a> <a target="_blank" href="https://reactjs.org/docs/hooks-reference.html#usestate">useState</a> we can now replace the <code>numColumns</code> constant accordingly and set the initial state to a function that we will implement later in order to be able to calculate the number of columns depending on the <code>width</code> that has just been destructured.</p>
<pre><code><span class="hljs-keyword">const</span> [numColumns, setNumColumns] = useState(calcNumColumns(width));
</code></pre><p>The <a target="_blank" href="https://reactjs.org/docs/hooks-reference.html#useeffect">useEffect</a> hook helps us detect a change in the <code>width</code> variable through <code>useWindowDimensions</code>, and then replace the <code>numColumns</code> with a recalculation using the updated width.</p>
<pre><code>useEffect(<span class="hljs-function">() =&gt;</span> {
    setNumColumns(calcNumColumns(width));
  }, [width]);
</code></pre><p>The only thing left now is to write our function for calculating the columns. First, we have to think about which calculation we want to use to determine the number. We calculate the not rounded count <code>cols</code> by dividing the <code>width</code> passed in by the <code>width</code> of one of our objects .</p>
<pre><code><span class="hljs-keyword">const</span> calcNumColumns = <span class="hljs-function">(<span class="hljs-params">width</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> cols = width / styles.item.width;
};
</code></pre><p>We also specify a constant <code>colsFloor</code>, which either takes on the value of a fixed minimum number of <code>numColumns</code>, or, if our just calculated <code>cols</code> is larger when rounded down, should equal this value.</p>
<pre><code><span class="hljs-keyword">const</span> minCols = <span class="hljs-number">2</span>;

<span class="hljs-keyword">const</span> calcNumColumns = <span class="hljs-function">(<span class="hljs-params">width</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> cols = width / styles.item.width;
  <span class="hljs-keyword">const</span> colsFloor = <span class="hljs-built_in">Math</span>.floor(cols) &gt; minCols ? <span class="hljs-built_in">Math</span>.floor(cols) : minCols;
};
</code></pre><p>From this, we now have to subtract the total <code>margin</code> to calculate how many columns fit in one row. If we temporarily increase the <code>margin</code> to 15, for example, we see that the spacing at the edges of the screen is half the spacing between objects. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664919665093/VYntkQQlA.png" alt="Untitled.png" /></p>
<p>This is because the <code>margin</code> applies to each object and is therefore duplicated between the objects themselves. We take this into account in our calculation by multiplying the rounded number of columns by the generated distance twice. This allows us to derive the total <code>margin</code> and subtract that from <code>cols</code> to know if our <code>colsFloor</code> fit into <code>cols</code> along with that margin.</p>
<pre><code><span class="hljs-keyword">const</span> minCols = <span class="hljs-number">2</span>;

<span class="hljs-keyword">const</span> calcNumColumns = <span class="hljs-function">(<span class="hljs-params">width</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> cols = width / styles.item.width;
  <span class="hljs-keyword">const</span> colsFloor = <span class="hljs-built_in">Math</span>.floor(cols) &gt; minCols ? <span class="hljs-built_in">Math</span>.floor(cols) : minCols;
  <span class="hljs-keyword">const</span> colsMinusMargin = cols - <span class="hljs-number">2</span> * colsFloor * styles.item.margin;
  <span class="hljs-keyword">if</span> (colsMinusMargin &lt; colsFloor &amp;&amp; colsFloor &gt; minCols) <span class="hljs-keyword">return</span> colsFloor - <span class="hljs-number">1</span>; 
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">return</span> colsFloor;
};
</code></pre><p>If the resulting difference (<code>colsMinusMargin</code>) is smaller than the rounded one (<code>colsFloor</code>), this means that including our defined distance between the objects a maximum of <code>colsFloor - 1</code> elements will fit in one row, otherwise <code>colsFloor</code>. The rest of the if ensures that we don't display fewer than our defined <code>minCols</code> columns. </p>
<h3 id="heading-final-fix">Final fix</h3>
<p>Now if we run this code like this and rotate our screen, we get an error message saying:</p>
<blockquote>
<p>Changing numColumns on the fly is not supported. Change the key prop on FlatList when changing the number of columns to force a fresh render of the component.</p>
</blockquote>
<p>This can be fixed by setting the Flatlist's <code>key</code> prop to <code>numColumns</code>.</p>
<h3 id="heading-everything-put-together">Everything put together</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://snack.expo.io/@blackcore/flalist-dynamic-numcolumns-depending-on-screensize-and-orientation">https://snack.expo.io/@blackcore/flalist-dynamic-numcolumns-depending-on-screensize-and-orientation</a></div>
<p>Here you can finally try out for yourself how the flat list changes as a result of your own adjustments. If you want to rotate the screen or change the screen size, just open the app on your own mobile device or the <a target="_blank" href="https://snack.expo.dev/@blackcore/flalist-dynamic-numcolumns-depending-on-screensize-and-orientation">Snack webplayer</a>. After opening the link you can use the web player by clicking on the small icon to the right of the blue web button.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>With that we have come to the end. Thanks for reading my first blog article. If you have any suggestions for improvement or questions, please leave a comment. Have fun with the Flatlist and see you soon! </p>
]]></content:encoded></item></channel></rss>