A production-architected React Native wallet app with biometric auth, gesture-driven payments, real-time spending analytics, and push notifications — built as a portfolio demonstration of senior-level mobile engineering patterns.
Tech Stack: React Native (Expo) · TypeScript · Redux Toolkit · React Navigation · Reanimated · Expo Camera · Expo Notifications
Flo is a CRED/PhonePe-style digital wallet covering the full core loop of a consumer fintech app — authentication, sending money, tracking spend, and staying informed — built with the same architectural discipline I apply to production work: normalized Redux state, memoized selectors, gesture-based interactions, and a clean separation between UI, business logic, and services.
Note: All data — balances, transactions, contacts — is mock data generated locally. No real payment gateway, bank, or UPI integration is connected. This is a portfolio and architecture demonstration, not a functioning financial product.
- Biometric Authentication — Face ID / fingerprint gate on app launch via
expo-local-authentication, with graceful fallback on devices without biometric hardware - Animated Onboarding — Reanimated-driven swipeable slides with interpolated scale/opacity transitions synced to scroll position
- Home Dashboard — Balance card with show/hide toggle, animated reveal, quick actions, 7-day spending sparkline, and pull-to-refresh
- Send Money Flow — Contact search → custom numpad amount entry with real-time balance validation → gesture-based slide-to-confirm → success state, all backed by live Redux state updates
- QR Code Scanner — Camera-based QR scanning with animated scan overlay, permission handling, and a mocked payment resolution flow
- Transaction History — Search, type/category filtering, and automatic date-based grouping (Today / Yesterday / Earlier)
- Spending Analytics — Category breakdown (hand-rolled SVG donut chart) and monthly spending trend (SVG bar chart), both derived live from transaction data — no hardcoded chart values
- Push Notifications — Local notifications for payment confirmations and budget threshold alerts, with a full in-app notification center (unread badge, mark-as-read)
- Profile & Settings — Biometric toggle wired to Redux, KYC status display, and a logout flow that resets navigation state cleanly
Transactions, wallet balance, user, and notifications each live in their own slice. Transaction data is stored as an ID-keyed dictionary (byId + allIds) rather than a flat array — the same normalization pattern used in the AMPA Admin Portal — so a single record update never forces every list-consuming component to re-render.
store/
├── slices/
│ ├── walletSlice.ts # balance, visibility toggle
│ ├── transactionSlice.ts # normalized transaction store
│ ├── userSlice.ts # auth state, biometric preference
│ └── notificationSlice.ts # in-app notification history
└── selectors/
└── index.ts # Reselect memoized selectors
All derived data — recent transactions, category totals, monthly trends, unread counts — goes through memoized Reselect selectors, not inline .filter()/.map() in components.
Screens stay presentational. Business logic lives in hooks:
hooks/
├── useBalance.ts # wallet balance + visibility
├── useTransactions.ts # transaction list access
├── useTransactionFilter.ts # search + type + category filtering
└── useBiometric.ts # biometric auth flow wrapper
Each bottom tab that needs to push a detail screen on top of it gets its own stack navigator, rather than fighting React Navigation's cross-stack limitations:
RootStack
├── Splash → Onboarding → Biometric
└── Main (Bottom Tabs)
├── HomeStack (Home → Notifications)
├── TransactionsStack (History → Detail)
├── Analytics
└── Profile
+ SendMoneyFlow (sibling stack: Contacts → Amount → Confirm → QR Scan)
This keeps the bottom tab bar visible while browsing, and lets payment/detail flows take over the full screen without needing awkward workarounds.
The analytics charts (donut + bar) are built with react-native-svg directly rather than a charting library. Victory Native's current major version renders through React Native Skia, which is a heavier dependency for what is, here, a small and fixed set of visualizations. Hand-rolled SVG keeps the bundle lean, avoids an extra native dependency, and gives full control over styling — the same trade-off reasoning I'd apply on a real production feature decision, not just a convenience shortcut.
The Send Money confirmation screen uses react-native-gesture-handler + react-native-reanimated for a slide-to-confirm interaction (the same UX pattern used by PhonePe/GPay for payment authorization) rather than a plain button — including:
- Gesture disabled during processing and after completion (prevents double-submission)
- Hardware back button blocked mid-transaction via
BackHandler - Threshold-based release detection (85% of track width) with spring-back on incomplete gestures
FloFresh/
├── App.tsx # Root providers + navigation container
├── app/ # Screens, organized by flow
│ ├── auth/ # Splash, Onboarding, Biometric
│ ├── main/ # Tab screens + their nested stacks
│ └── shared/ # Screens used across multiple flows
├── src/
│ ├── components/ # Reusable, presentation-only UI
│ ├── hooks/ # Business logic layer
│ ├── store/ # Redux slices + selectors
│ ├── services/ # notificationService, etc.
│ ├── theme/ # Design tokens (colors, type, spacing)
│ ├── types/ # Shared TypeScript interfaces
│ ├── constants/ # Mock data, category config
│ └── utils/ # currency, date formatting/grouping
git clone https://github.com/rishabhdeotyagi/flo-wallet-app.git
cd flo-wallet-app
npm install --legacy-peer-deps
npx expo start --clearScan the QR code with Expo Go, or run on a physical device — camera-based QR scanning and biometric auth require a physical device; neither works in a simulator without additional configuration.
Being transparent about scope, since this is a portfolio project:
- QR scanning is mocked — it detects any valid QR code and resolves to a random contact rather than parsing real UPI payload data (
upi://pay?pa=...) - Biometric preference doesn't persist across app restarts — stored in Redux only, not MMKV; a real app would persist this
- Budget check is simplified — a single hardcoded threshold check on payment completion rather than a full budgeting engine
- No real payment gateway — Razorpay SDK was scoped out in favor of depth on the core UX patterns (gestures, state architecture, navigation) over breadth of integrations
- Persist biometric/theme preferences via MMKV
- Real UPI QR payload parsing and validation
- Razorpay integration for actual wallet top-up
- React Query for any real backend-synced data (currently everything is local mock state)
- Detox E2E tests for the payment and auth flows specifically — the highest-stakes user journeys
MIT



