---
title: 'Examining performance differences between Native, Flutter, and React Native
  mobile development: Take two.'
teaser: Flutter proves itself to be a more performant option than previously realized.
tags: android,react native,flutter
author: Alex Sullivan
published_on: 2018-07-16
---

A few weeks ago [I wrote a blog
post](https://thoughtbot.com/blog/examining-performance-differences-between-native-flutter-and-react-native-mobile-development)
comparing the performance of a simple timer app written as a native application
then rewritten in both React Native and Flutter. In that blog post, I came to
the conclusion that the React Native and Flutter implementations had _roughly_
similar performance metrics. However, a helpful engineer from the Flutter team
pointed out that the Flutter implementation was written particularly
inefficiently, so in this blog post we'll correct that issue and rerun our
tests!

First, as a recap, let's look at the Flutter code we wrote in the last blog post:

```dart
void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _startTime = new DateTime.now().millisecondsSinceEpoch;
  int _numMilliseconds = 0;
  int _numSeconds = 0;
  int _numMinutes = 0;

  @override
  void initState() {
    super.initState();
    Timer.periodic(new Duration(milliseconds: 10), (Timer timer) {
      int timeDifference = new DateTime.now().millisecondsSinceEpoch - _startTime;
      double seconds = timeDifference / 1000;
      double minutes = seconds / 60;
      double leftoverSeconds = seconds % 60;
      double leftoverMillis = timeDifference % 1000 / 10;
      setState(() {
        _numMilliseconds = leftoverMillis.floor();
        _numSeconds = leftoverSeconds.floor();
        _numMinutes = minutes.floor();
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        body: new Center(
          child: new Text(
            sprintf("%02d:%02d:%2d", [_numMinutes, _numSeconds, _numMilliseconds]),
          ),
        )
    );
  }
}
```

Let's walk through the code. We have a `StatelessWidget` called `MyApp` that
builds a `StatefulWidget` called `MyHomePage`. `MyHomePage` consists of a
`Scaffold`, which is a kind of container class used to show different material
design oriented widgets, and a centered `Text` widget. Every 10 milliseconds we
reset the state with updated timer information and redraw the widget tree.

Herein lies the problem - we're not _just_ redrawing the `Text` widget, which is
all that we actually care about - we're also redrawing the _entire scaffold_. So
every ten milliseconds we're doing a lot more work than necessary. We can easily
fix this by by moving the `Scaffold` and `Center` widgets up into our stateless
`MyApp` widget. Applying that adjustment results in the following:

```dart
void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: new Scaffold(
          body: new Center(
            child: new TimerWidget(),
          )),
    );
  }
}

class TimerWidget extends StatefulWidget {
  @override
  TimerWidgetState createState() {
    return new TimerWidgetState();
  }
}

class TimerWidgetState extends State<TimerWidget> {
  int _startTime = new DateTime.now().millisecondsSinceEpoch;
  int _numMilliseconds = 0;
  int _numSeconds = 0;
  int _numMinutes = 0;

  @override
  Widget build(BuildContext context) {
    return new Text(
      sprintf("%02d:%02d:%2d", [_numMinutes, _numSeconds, _numMilliseconds]),
    );
  }

  @override
  void initState() {
    super.initState();
    Timer.periodic(new Duration(milliseconds: 10), (Timer timer) {
      int timeDifference =
          new DateTime.now().millisecondsSinceEpoch - _startTime;
      double seconds = timeDifference / 1000;
      double minutes = seconds / 60;
      double leftoverSeconds = seconds % 60;
      double leftoverMillis = timeDifference % 1000 / 10;
      setState(() {
        _numMilliseconds = leftoverMillis.floor();
        _numSeconds = leftoverSeconds.floor();
        _numMinutes = minutes.floor();
      });
    });
  }
}
```

It's worth noting at this point that in the original article the React Native
implementation was actually built with this optimization already applied. As a
refresher, here's the React Native implementation:

```javascript
export default class App extends Component {

  render() {
    return (
      <View style={styles.container}>
        <Timer />
      </View>
    );
  }
}

class Timer extends Component {
  constructor(props) {
    super(props);
    this.state = {
      milliseconds: 0,
      seconds: 0,
      minutes: 0,
    }

    let startTime = global.nativePerformanceNow();
    setInterval(() => {
      let timeDifference = global.nativePerformanceNow() - startTime;
      let seconds = timeDifference / 1000;
      let minutes = seconds / 60;
      let leftoverSeconds = seconds % 60;
      let leftoverMillis = timeDifference % 1000 / 10;
      this.setState({
        milliseconds: leftoverMillis,
        seconds: leftoverSeconds,
        minutes: minutes,
      });
    }, 10);
  }

  render() {
    let { milliseconds, seconds, minutes } = this.state;
    let time = sprintf("%02d:%02d:%2d", minutes, seconds, milliseconds);
    return (
      <Text>{time}</Text>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  }
});
```

We've already separated out a `Timer` component, so this implementation won't
suffer from the same performance flaw that the Flutter implementation did.

Now that we're on a more level playing field, let's rerun our experiment.

#### Old Flutter results on the Pixel

![](https://images.thoughtbot.com/blog-vellum-image-uploads/lQ2x4GucSWODuouxK270_pixel-flutter-profile.png)

#### Updated Flutter results on the Pixel

![](https://images.thoughtbot.com/blog-vellum-image-uploads/6vLWdUQ6Sk2GoA3VQOi8_updated-flutter-pixel.png)

#### React Native results on the Pixel

![](https://images.thoughtbot.com/blog-vellum-image-uploads/wcCikaphRPy2lv8frxuS_pixel-react-native.png)

#### Native results on the Pixel

![](https://images.thoughtbot.com/blog-vellum-image-uploads/ZL2Ji5UAQF2rBSPVpuTA_pixel-native.png)

We knocked our CPU utilization on the Flutter implementation down about 3%, and
had a minor reduction in memory usage as well. Not bad for a tiny amount of
work! At this point, the Flutter implementations CPU utilization is comparable
to the native application and considerably lower than the React Native
implementation - the memory footprint, however, is still high.

Now let's take a look on the Nexus 5X

#### Old Flutter results on the Nexus 5X

![](https://images.thoughtbot.com/blog-vellum-image-uploads/AJNqUln8TOiJE1vZ0tnj_5X-flutter-profile.png)

#### Updated Flutter results on the Nexus 5X

![](https://images.thoughtbot.com/blog-vellum-image-uploads/DEotGlXaTeiutv8zAqx3_updated-flutter-nexus5x.png)

#### React Native results on the Nexus 5X

![](https://images.thoughtbot.com/blog-vellum-image-uploads/AZGU7tjrQafOmrSmInOA_5X-react-native.png)

#### Native results on the Nexus 5X

![](https://images.thoughtbot.com/blog-vellum-image-uploads/lFFEgOsfTSoj0vK5Se3T_5X-native.png)

Again, we shaved off about 3% CPU utilization with our performance enhancements
and brought the Flutter implementation closer in line with the native
implementation. Memory usage also went down quite a bit, from 31MB on average to
23MB, beating out React Native's 27MB memory usage. It's still a fair bit higher
than the 14MB the native app is utilizing though.

## Updated Conclusion

This is _still_ a tiny test, but with our updated data I'm becoming more
excited about the prospect of Flutter being a cross platform stack that's more
performant than React Native. I'm still not willing to make a judgement call one
way or the other about which framework is faster in the real world, but I'm
excited to watch Flutter continue to develop!

## Want to learn more about mobile development?

thoughtbot can help accelerate your product development with proven best practices and processes derived from our 20 years of software development experience. Interested? [Learn how thoughtbot can help](https://thoughtbot.com/services/react-native-development) grow your mobile application.
