# Flutter and React Native performance overview

I've been playing with Flutter for a bit again, and this time I tried to come up with a simple example of how its performance compares to ReactNative. (Tested on IOS, iPhone SE2) I built a card list app with rudimentary UI and 300 items in length. I used both ReactNative and Flutter for comparison and then analyzed the performance.

- URL: https://sudolabs.com/insights/flutter-and-react-native-performance-overview
- Published: 2020-07-13
- Updated: 2024-01-16
- Author: Jozef Petro
- Categories: Tech
- Language: English

## and which one is better for your product?

I've been playing with Flutter for a bit again, and this time I tried to come up with a simple example of how its performance compares to ReactNative. (Tested on IOS, iPhone SE2) I built a card list app with a rudimentary UI and 300 items in length. I used both `ReactNative` and `Flutter` for comparison and then analyzed the performance.

The app's layout is side by side. ![layouts](https://a.storyblok.com/f/117609/1481x1202/5a52f449c6/layout.jpeg)

## Analyzing performance

The metric that I picked for the analysis is `FPS in time` (Frames per second).

**I tried to perform a similar set of actions accurately in both apps:**

- Waiting for the first picture
- Start scrolling as fast as I could
- Change scroll direction three times
- Performed in 30 seconds timeline

Time spent on the app can also be useful from a dev performance perspective. In my case, it's a bit biased because I have much more experience with ReactNative and Javascript, while on the other hand, this was the first list I built in Flutter.

**The time spent including project setup:**

- `ReactNative` around 45 minutes
- `Flutter` around 1.5 hour

**For the scrolling components, I used:**

- [FlatList](https://reactnative.dev/docs/flatlist) in case of ReactNative
- [ListView](https://api.flutter.dev/flutter/widgets/ListView-class.html) in case of Flutter

**When I started analyzing the performance, first, I can look at the app FPS on startup and when the app becomes actually usable:**

- For `ReactNative` -  2 seconds in and we're still on 1 FPS
- For `Flutter` - 2 seconds in and we're good to go at 39 FPS

Performance profiling result.
![perfomance tables](https://a.storyblok.com/f/117609/1481x1202/b375200d01/perftable.jpeg)

You can feel the difference between the two.

When I started scrolling, I pretty much didn't have any significant problems. **Flutter seemed smoother**, and it can also be seen when you compare the charts and how ReactNative's FPS is more unstable. It's worth noting that once I picked up the scroll speed on React Native, I started to see how items are mounted, which caused a bit of flickering (really tiny, but noticeable).

Here you can see how `FPS` is much more unstable in ReacNative. The Y axis shows range from `0 - 60 FPS`.
![performance charts](https://a.storyblok.com/f/117609/1481x1042/38da6daae0/perfchart.jpeg)

## From the implementation perspective

ReactNative wins the game for me, because it's just so friendly for developers experienced in Javascript (me).

Code for the `ReactNative` implementation.

```javascript
import React from 'react';
import {
  SafeAreaView,
  StyleSheet,
  View,
  Text,
  ImageBackground,
  FlatList,
  StatusBar,
} from 'react-native';

const LIST_DATA = new Array(300).fill('');
const CARD_RADIUS = 8;

const Card = () => (
  <View style={styles.cardWrapper}>
    <View style={styles.cardImageWrapper}>
      <ImageBackground
        source={{uri: 'https://picsum.photos/150'}}
        style={styles.cardImage}
      />
    </View>
    <View style={styles.cardTextWrapper}>
      <Text>
        Lorem Ipsum is simply dummy text of the printing and typesetting
        industry. Lorem Ipsum has been the industry's standard.
      </Text>
    </View>
  </View>
);

const App = () => {
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <FlatList
          data={LIST_DATA}
          renderItem={() => <Card />}
          keyExtractor={(_, index) => index.toString()}
        />
      </SafeAreaView>
    </>
  );
};

const styles = StyleSheet.create({
  cardWrapper: {
    height: 150,
    margin: 10,
    flexDirection: 'row',
    alignItems: 'center',
    borderRadius: CARD_RADIUS,
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 5,
    },
    shadowOpacity: 0.36,
    shadowRadius: 6.68,
    elevation: 11,
    backgroundColor: '#fff',
  },
  cardImageWrapper: {
    flex: 1,
    overflow: 'hidden',
    borderTopLeftRadius: CARD_RADIUS,
    borderBottomLeftRadius: CARD_RADIUS,
  },
  cardImage: {
    flex: 1,
    resizeMode: 'cover',
    justifyContent: 'center',
    height: 150,
  },
  cardTextWrapper: {
    flex: 1,
    paddingLeft: 10,
    flexDirection: 'row',
    justifyContent: 'flex-start',
  },
});

export default App;
```

When they write in Flutter docs that everything is a widget, they mean it. It has extensive API to learn. Even if it tries to mitigate HTML / CSS layouting approaches and the way of decorating elements, there's a class for everything, even for basic styling of borders and shadows. So you need to get at least an overview, and this takes time.

Code of the `Flutter` implementation for comparison.

```javascript
import 'package:flutter/material.dart';
import 'dart:ui' as ui;

void main() {
  runApp(
    MediaQuery(
      data: MediaQueryData.fromWindow(ui.window),
      child: Directionality(
        textDirection: TextDirection.ltr,
        child: SafeArea(
          child: CardList(),
        ),
      ),
    ),
  );
}

const double CARD_RADIUS = 8;

class Card extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.all(Radius.circular(CARD_RADIUS)),
        boxShadow: [
          BoxShadow(
            color: Colors.grey,
            blurRadius: 10,
            spreadRadius: 1,
          )
        ],
      ),
      margin: EdgeInsets.all(10),
      height: 150,
      child: Row(
        children: <Widget>[
          Expanded(
            child: ClipRRect(
              borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(CARD_RADIUS),
                  bottomLeft: Radius.circular(CARD_RADIUS)),
              child: Image(
                image: NetworkImage('https://picsum.photos/150'),
                height: 150,
                fit: BoxFit.cover,
              ),
            ),
          ),
          Expanded(
            child: Container(
              padding: EdgeInsets.only(left: 10),
              child: Text(
                "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard.",
                style: TextStyle(color: Colors.black),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class CardList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(color: Colors.white),
      child: ListView.separated(
          itemBuilder: (BuildContext context, int index) {
            return Card();
          },
          separatorBuilder: (BuildContext context, int index) =>
              const Divider(height: 3, color: Colors.transparent),
          itemCount: 300),
    );
  }
}
```

## Why am I trying it then?

I see massive potential in implementing more advanced concepts where the tooling and utils for everything can help significantly. Advanced routing, animations, real native feeling, and better performance - these factors contribute to a better product experience and more options. I think it's just a trade-off, which makes sense to me.
