Expo & React Native vs Flutter: Which Should You Choose?
A practical comparison of Expo/React Native and Flutter from a developer who has used both — including real experience building a barcode scanning inventory management system.
Introduction
I have built mobile apps with both Expo/React Native and Flutter. For my Inventory Management System, I needed a mobile companion app that could scan barcodes to track stock movements in real time. I built the first version with Expo/React Native, then evaluated Flutter as an alternative. Here is what I learned.
Getting Started
Expo / React Native
npx create-expo-app inventory-scanner
cd inventory-scanner
npx expo start
Scan the QR code with the Expo Go app and your app is live on your phone. No Xcode or Android Studio required for basic development. If you know React and JavaScript, you are productive within hours.
Flutter
flutter create inventory_scanner
cd inventory_scanner
flutter run
More setup upfront but the Flutter doctor tool guides you through it. Once configured, the developer experience is excellent.
Winner: Expo for getting started quickly.
Barcode Scanning — The Core Feature
Expo Camera
import { CameraView, useCameraPermissions } from "expo-camera"
export default function Scanner({ onScan }: { onScan: (data: string) => void }) {
const [permission, requestPermission] = useCameraPermissions()
if (!permission?.granted) {
return <Button title="Grant Camera Permission" onPress={requestPermission} />
}
return (
<CameraView
style={{ flex: 1 }}
onBarcodeScanned={({ data }) => onScan(data)}
barcodeScannerSettings={{ barcodeTypes: ["qr", "ean13", "ean8", "code128"] }}
/>
)
}
Flutter — mobile_scanner
import "package:mobile_scanner/mobile_scanner.dart";
class ScannerScreen extends StatelessWidget {
final void Function(String) onScan;
const ScannerScreen({ required this.onScan });
@override
Widget build(BuildContext context) {
return MobileScanner(
onDetect: (capture) {
final barcode = capture.barcodes.first;
if (barcode.rawValue != null) onScan(barcode.rawValue!);
},
);
}
}
Flutter's mobile_scanner was marginally faster in low-light warehouse conditions. Winner: Flutter (narrowly).
Shared TypeScript Types — The React Native Advantage
The biggest advantage of staying with React Native for my IMS was sharing types across the entire stack.
// shared/schemas.ts — used in Next.js dashboard AND React Native app
import { z } from "zod"
export const StockMovementSchema = z.object({
itemId: z.string().cuid(),
quantity: z.number().int().positive(),
action: z.enum(["in", "out", "adjust"]),
timestamp: z.string().datetime(),
scannedBy: z.string(),
})
export type StockMovement = z.infer<typeof StockMovementSchema>
One schema used in the API, the web dashboard, and the mobile scanner — zero duplication, full type safety everywhere. Flutter cannot do this.
UI and Performance
Flutter draws its own UI rather than using native components, giving it pixel-perfect consistency across iOS and Android. On low-end Android devices — the kind commonly used in Kenyan warehouses — Flutter is smoother at 60fps.
Winner: Flutter for UI performance on budget devices.
The Real Decision
Choose Expo/React Native if:
Choose Flutter if:
Conclusion
For my IMS barcode scanner, I stayed with Expo. The TypeScript code sharing between web and mobile saved more time than Flutter's performance advantage would have gained. If you need a mobile app built for your business — iOS, Android, or both — reach out.
Work With James
Need help with your project?
Whether it’s M-Pesa integration, a full web application, or a performance audit — reach out and let’s build something great.
Get In Touch →