Both React Native and Flutter position themselves as frameworks to create natively compiled apps, and there is much truth in it. But their approach to building UI is fundamentally different.
To build interfaces, React Native uses system UI components, while Flutter has its own widgets for it. Where React Native provides a “wrapper” for native, platform-specific UI components of Android and iOS, Flutter essentially creates the elements itself, using the third-party 2D graphics library Skia for drawing them from scratch each time.
The advantages of Flutter’s approach are in the details: developers have two sets of widgets for Android and iOS—named “Material” and “Cupertino” respectively—and can create custom widgets as well as customize the existing ones.
It also means that Flutter avoids one of the main limitations of React Native (and of many other native-component GUI builders for that matter). Namely, the problem of using platform-specific native GUI components of iOS or Android when there are no direct analogs to these components in other platforms.
Where React Native has to either work around such elements or abandon them completely, Flutter just creates the 100% same thing for iOS and Android with no hassle. While this potentially can lead to some inconsistencies in the overall style (like iOS-looking elements in Android apps), it still means less UI testing is necessary, since this is where Flutter’s got you covered.
Flutter was created by Google, so, naturally, Android components are very well-supported and prioritized. However, iOS widgets can be somewhat lacking, especially when it comes down to legacy design, staying behind iOS updates, guideline compliance, and visual consistency.
New iterations of iOS and Android roll out every year, which means UI changes. Flutter’s GUI building will inevitably have to play catch-up or risk making the apps created with it look dated. Striving for a native look as much as possible will come at a cost in terms of more complex code and additional development time, while the results might still not be 100% on the money in the end.
Still, Flutter shows great capabilities in quick prototyping and showcasing, and like React Native, it has the hot reload functionality. Allowing to quickly evaluate the impact of code changes visually, hot reload dramatically speeds up UI testing, which would otherwise take much longer.
In short: if having 100% native UI elements is crucial for your cross-platform development project, React Native is the right choice. If a lot of flexibility at the expense of some ‘nativeness’ is preferable, Flutter is the better option.