React Native and Flutter are two popular cross-platform mobile app development frameworks that allow developers to build apps for iOS and Android using a single codebase. The challenge for these cross-platform solutions is to deliver a user interface that looks and feels native, which is important for creating a seamless user experience. In this blog I will go into how React Native and Flutter have solved this, and what I believe gives the best result.
React Native
React Native creates platform-specific UI by relying on platform-specific UI components. When building a UI element in React Native, the UI component that is used is specific to the platform the app is running on (e.g. Text
on iOS, TextView
on Android). React Native will automatically use the UI-specific component for each platform, allowing the app to look and feel like a native app on both iOS and Android.
This is beneficial, because future native UI changes will automatically translate into React Native land. Also accessibility support is natively supported, of which I will show the following example.
Example
React Native provides the components Switch
, which converts to the native component UISwitch
for iOS and a Switch
for Android. Here’s an example of how you might use these components in React Native.
function App(): JSX.Element { const [switchValue, setSwitchValue] = useState(false); const handleSwitch = () => setSwitchValue(previousState => !previousState); const switchState = switchValue ? 'ON' : 'OFF'; const switchText = Platform.OS === 'ios' ? <code>iOS Switch (${switchState})
:Android Switch (${switchState})
; return ( <ScrollView contentInsetAdjustmentBehavior="automatic" style={styles.scrollViewStyle} contentContainerStyle={styles.scrollViewContent}> <View style={styles.container}> <View style={styles.switchContainer}> <Text>{switchText}</Text> <Switch onValueChange={handleSwitch} value={switchValue} /> </View> </View> </ScrollView> ); }
This code will give the following result on the simulator. On the simulator I enabled the accessibility setting to show On/Off labels. As you can see, React Native uses the native UI component, resulting in showing these labels via accessibility settings.
Flutter
Flutter takes a different approach to platform-specific UI. Instead of relying on platform-specific components, Flutter provides its own set of widgets that are designed to look and feel like native UI components on both iOS and Android. These widgets are implemented in the Flutter framework itself and are rendered directly by the Flutter engine, which allows apps built with Flutter to have a consistent look and feel across platforms.
Example
Flutter provides its own set of widgets that can be used to create platform-specific UI elements. Here’s an example of how you might use the PlatformSwitch
and to shows a switch that looks and feels like a native button on iOS and Android, respectively:
class _MyHomePageState extends State<MyHomePage> {
bool switchValue = false;
void handleSwitch(bool value) {
setState(() {
switchValue = value;
});
}
String getSwitchState() {
return switchValue ? 'ON' : 'OFF';
}
String getSwitchText() {
return Platform.isIOS
? 'iOS Switch (${getSwitchState()})'
: 'Android Switch (${getSwitchState()})';
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: const EdgeInsets.all(20),
color: Color(0xFF7AA4FF),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: double.infinity,
height: 50,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20), // Added horizontal padding of 20
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
getSwitchText(),
),
PlatformSwitch(
value: switchValue,
onChanged: handleSwitch,
),
],
),
),
),
]
),
),
);
}
}
This code will give the following result on the simulator. On the simulator I enabled the accessibility setting to show On/Off labels. As you can see, Flutter doesn’t use the native UI component, resulting in not showing these labels via accessibility settings.
Although this is a known bug (from 2016) and a PR is ready, I used this as an example to showcase the difference between these platforms. For React Native this won’t be an issue, because it will be available out of the box because it’s using the native UI component. Flutter is mimicking the UI component, which is the reason these issues exist.
The reason Flutter isn’t using the solution as React Native is because they believe it adds an extra abstraction between the UI and the app logic. I would assume this is one of the reasons Flutter is faster in comparison.
While this is an example about accessibility, the same applies when the native platform components change and need to be updated to stay in touch. At this moment, React Native get these changes automatically while Flutter needs another PR to comply to platform changes.
Concluding
While Flutter is a mature platform to build your cross-platform apps in, it’s good to know what the differences are between the other cross-platform solutions, like React Native.
To me, making use of native UI elements already gives you a head start in developing apps with a good user experience. It will give users a better understanding what to expect, and provides a familiar feeling. The learning curve to get used to your app is lower, so users will need less time to understand the functionality of the app. In the end, the chance are higher that users will use your app more often.
In conclusion, React Native relies on native UI components to achieve a platform-specific user interface, providing a seamless and familiar user experience. Flutter, on the other hand, uses its own set of widgets to mimic native UI components, offering consistency but potentially encountering limitations. The choice between the two frameworks depends on project requirements, but if you were to ask me, I would choose functionality over a little bit of speed, so React Native would be my preferred solution.