13. 7. 2020
3 min read
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.
Jozef Petro
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.
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 minutesFlutter
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 FPSFor
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 { @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.
You might
also like