State Management in Mobile Apps: Redux, Riverpod, and Alternatives

A practical comparison of state management approaches across React Native and Flutter, with code for each.

Why State Management Choice Matters

The wrong state management approach doesn’t usually break a small app — it breaks a large one, once state is shared across many screens and updates become hard to trace. Choosing deliberately early avoids a painful mid-project migration.

Redux Toolkit (React Native)

import { createSlice, configureStore } from '@reduxjs/toolkit';

const cartSlice = createSlice({
  name: 'cart',
  initialState: { items: [] },
  reducers: {
    addItem: (state, action) => {
      state.items.push(action.payload);
    },
    removeItem: (state, action) => {
      state.items = state.items.filter(item => item.id !== action.payload);
    },
  },
});

export const { addItem, removeItem } = cartSlice.actions;
export const store = configureStore({ reducer: { cart: cartSlice.reducer } });

Best for: large apps with complex, shared state and a team that values explicit, predictable state transitions with strong devtools support.

Zustand (React Native, Lightweight Alternative)

import { create } from 'zustand';

const useCartStore = create((set) => ({
  items: [],
  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
}));

function CartScreen() {
  const items = useCartStore((state) => state.items);
  return <FlatList data={items} renderItem={...} />;
}

Best for: teams wanting Redux-like predictability without the boilerplate.

Riverpod (Flutter)

final cartProvider = StateNotifierProvider<CartNotifier, List<Item>>(
  (ref) => CartNotifier(),
);

class CartNotifier extends StateNotifier<List<Item>> {
  CartNotifier() : super([]);

  void addItem(Item item) {
    state = [...state, item];
  }
}

class CartScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final items = ref.watch(cartProvider);
    return ListView(children: items.map((i) => Text(i.name)).toList());
  }
}

Best for: most Flutter apps — Riverpod is the current community-recommended default, replacing the older Provider package.

Choosing Local vs Global State

Not everything belongs in global state. Screen-local UI state (form input, toggle states) is usually better kept in local component state — pushing everything into a global store adds unnecessary complexity and re-render overhead.

A Simple Rule of Thumb

Scope Approach
Single screen Local state (useState / StatefulWidget)
Shared across a few screens Context / lightweight store (Zustand, simple Riverpod provider)
App-wide, complex, many actions Redux Toolkit / structured Riverpod notifiers

Conclusion

Start with the simplest tool that solves your actual problem, and only escalate to a heavier solution once you feel real pain from prop drilling or scattered state updates. Over-engineering state management upfront is a more common mistake than under-engineering it.