Flutter and React Native performance overview

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 rudimentary UI and 300 items in length. I used both ReactNative and Flutter for comparison and then analyzed the performance.

The app's layout side by side.

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:

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.

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.

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.

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.

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 {
  
  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 {
  
  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 with implementing more advanced concepts where the tooling and utils for everything can help significantly. Advanced routing, animations, real native feeling, better performance - these factors contribute to better product experience and more options. I think it's just a trade-off, which makes sense to me.

written by

Jozef Petro

co-founder & CEO at sudolabs.io

Let’s build something that users love!

Contact us
hello@sudolabs.io

DUETT Business Residence
Námestie osloboditeľov 3/A
040 01 Košice, Slovakia

©2020 Sudolabs