← Articles

Migrating from React Native to Flutter

By Ann Tech · 28 April 2026

If you're coming from React Native, Flutter is more familiar than it looks — and more different than it seems. Here is a practical mapping between the two worlds.

Mental model shift

React NativeFlutter
JSX componentsWidget classes
StyleSheet.createTheme / TextStyle / BoxDecoration
useState / ReduxsetState / Riverpod / BLoC
React Navigationgo_router / Navigator
FlatListListView.builder
ExpoFlutterFire / pub.dev packages
Metro bundlerflutter build

Layout

React Native uses Flexbox everywhere. Flutter uses a tree of layout widgets:

// React Native
<View style={{ flexDirection: 'row', alignItems: 'center', padding: 16 }}>
  <Image source={{ uri: url }} style={{ width: 48, height: 48 }} />
  <Text style={{ marginLeft: 8 }}>{name}</Text>
</View>
// Flutter equivalent
Padding(
  padding: const EdgeInsets.all(16),
  child: Row(
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      Image.network(url, width: 48, height: 48),
      const SizedBox(width: 8),
      Text(name),
    ],
  ),
)

Flutter's layout widgets (Row, Column, Stack, Padding, Center) feel verbose at first but are predictable once learned.

State management

// React Native: useState hook
const [count, setCount] = useState(0);
<Button onPress={() => setCount(c => c + 1)} title="Add" />
// Flutter equivalent: StatefulWidget
class Counter extends StatefulWidget {
  @override
  State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
  int count = 0;
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () => setState(() => count++),
      child: Text('Count: $count'),
    );
  }
}

For global state, Riverpod is the closest to React's Context + hooks mental model:

// Riverpod feels familiar to React developers
@riverpod
class CartNotifier extends _$CartNotifier {
  @override
  Cart build() => Cart.empty();
  void add(Product p) => state = state.copyWith(items: [...state.items, p]);
}

// In widget (like useSelector):
final cart = ref.watch(cartNotifierProvider);
// To dispatch (like useDispatch):
ref.read(cartNotifierProvider.notifier).add(product);
// React Navigation
navigation.navigate('ProductDetail', { productId: '123' });
navigation.goBack();
// go_router
context.push('/products/123');
context.pop();
// Or:
context.go('/products/123'); // Replaces stack entry

Styling

React Native styles are JS objects. Flutter uses widget properties and themes:

// React Native
const styles = StyleSheet.create({
  card: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    shadowColor: '#000',
    shadowOpacity: 0.1,
    shadowRadius: 8,
    elevation: 4,
  },
});
// Flutter
Container(
  decoration: BoxDecoration(
    color: Colors.white,
    borderRadius: BorderRadius.circular(12),
    boxShadow: [
      BoxShadow(
        color: Colors.black.withOpacity(0.1),
        blurRadius: 8,
        offset: const Offset(0, 2),
      ),
    ],
  ),
  padding: const EdgeInsets.all(16),
  child: child,
)

What's genuinely better in Flutter

  • Hot reload is faster and more reliable
  • Consistent rendering — no native component differences between Android and iOS
  • Performance — AOT-compiled Dart is faster than JavaScriptCore
  • Type safety — Dart's sound null safety catches bugs the JS type system misses
  • Testing — widget tests are easier to write and faster than RN equivalents

What takes getting used to

  • Verbose widget tree syntax (no JSX)
  • No direct DOM/native manipulation
  • A new package ecosystem (pub.dev vs npm)
  • Dart instead of JavaScript/TypeScript

Common pitfalls

Expecting JSX-like syntax. Dart doesn't have JSX. Widget trees look verbose compared to JSX but are just regular Dart constructors — they're more typesafe and IDE-friendly.

Importing npm packages. React Native uses npm/yarn. Flutter uses pub.dev and pubspec.yaml. The package ecosystems are separate — RN packages don't work in Flutter.

Using StatefulWidget everywhere. In RN, state with useState is natural. In Flutter, reach for Riverpod for anything shared across screens and use StatefulWidget only for local UI state.

Sign in to like, dislike, or report.

Migrating from React Native to Flutter — ANN Tech