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.