
Photo by Annie Spratt on Unsplash
Flutter Ecommerce App Templates: Cross-Platform Mobile Commerce with Dart
Flutter Ecommerce App Templates: Cross-Platform Mobile Commerce with Dart
Flutter and React Native are the two production-grade cross-platform mobile frameworks in 2026. React Native bridges to native platform widgets; Flutter renders every pixel itself via the Skia graphics engine. Both produce real native-feeling apps. The decision between them is almost entirely about team composition and UI control — and for teams where mobile polish is a competitive differentiator, Flutter often wins.
This guide walks through Flutter's architecture, the Dart language learning curve, when to pick Flutter over React Native, and what MVPHub ships in each Flutter ecommerce template.
TL;DR
- Flutter renders every pixel with Skia — identical UI across iOS, Android, and (optionally) desktop and web
- Dart is the language — unfamiliar to JS teams but picks up in 2-4 weeks
- Best UI consistency across devices — no platform-widget drift
- Riverpod for state, Dio for networking, Material 3 + Cupertino widgets out of the box
- Main trade-off: code sharing with a web frontend is limited. If mobile-first with no web, Flutter is great. If web already exists and you want shared logic, React Native fits better.
- Pick Flutter when mobile UI polish matters more than web/mobile code sharing
Browse Flutter ecommerce app templates
Why Flutter's rendering model matters
React Native works by bridging to native widgets. When you render a <View> it becomes a UIView on iOS and a ViewGroup on Android. When you render a <Button> it becomes a UIButton and a Button. The bridge is clever and mostly transparent but it means your app inherits each platform's UI inconsistencies — animations, font rendering, scroll physics, and accessibility behaviors all vary.
Flutter does not bridge. Flutter ships its own rendering engine (Skia, the same engine Chrome uses) that draws every pixel of every widget. A Container renders the same way on a 5-year-old Android phone and a brand-new iPhone. Scroll physics are identical. Animations hit 60fps consistently. Font rendering matches the design across devices.
For ecommerce this matters because:
- Brand visuals stay consistent across customers' devices — important for beauty, fashion, luxury goods
- Custom animations (product flip-throughs, variant pickers, hero transitions) work the same everywhere
- There's no "native widget weirdness" where a button looks subtly wrong on one platform
- Desktop and web targets use the same Skia engine, so expanding to those is mostly free
The cost is that Flutter apps are slightly bigger (Skia adds ~3-5 MB to the app binary) and Dart isn't a language most web developers know.
The Dart learning curve
Dart is pleasant but unfamiliar. For a React or TypeScript team, expect 2-4 weeks of ramp-up to become productive in Flutter. The hard parts:
- Null safety — Dart's null safety is similar to TypeScript's strict mode but with different syntax (
String?vsstring | null) - Class-based widgets — Flutter widgets are classes that extend
StatefulWidgetorStatelessWidget. Closer to older React class components than modern hooks. - Build methods — every widget has a
build(BuildContext context)method that returns a widget tree. It's JSX-adjacent but uses named parameters instead of JSX syntax. - State management — Riverpod (recommended), Provider (older), BLoC (complex but powerful). Three popular options, each with a different learning curve.
The easy parts:
- Hot reload is excellent — faster than any JavaScript framework's dev loop
- Error messages are clear — the Flutter analyzer is strict but helpful
- Tooling is mature — VS Code and Android Studio both have first-class Flutter support
- Package ecosystem is solid —
pub.devhas well-maintained libraries for networking, state, auth, Stripe, etc.
Architecture: Flutter + Medusa
MVPHub Flutter ecommerce templates follow a clean layered architecture:
lib/
main.dart # App entry point + Riverpod scope
app/
theme.dart # Material 3 + Cupertino themes
router.dart # go_router config
features/
products/
data/
product_repository.dart # Medusa API client
domain/
product.dart # Data model (from Dart's freezed)
presentation/
product_list_screen.dart
product_detail_screen.dart
widgets/
variant_picker.dart
add_to_cart_button.dart
cart/
data/
cart_repository.dart
presentation/
cart_screen.dart
cart_drawer.dart
checkout/
presentation/
checkout_screen.dart
stripe_payment_sheet.dart
account/
...
shared/
widgets/
loading_indicator.dart
error_view.dart
providers/
medusa_client.dart
Networking with Dio:
// lib/features/products/data/product_repository.dart
import 'package:dio/dio.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../domain/product.dart';
class ProductRepository {
final Dio _dio;
ProductRepository(this._dio);
Future<Product> getProduct(String handle) async {
final response = await _dio.get('/store/products/$handle');
return Product.fromJson(response.data['product']);
}
Future<List<Product>> listProducts({int limit = 20, int offset = 0}) async {
final response = await _dio.get('/store/products', queryParameters: {
'limit': limit,
'offset': offset,
});
return (response.data['products'] as List)
.map((json) => Product.fromJson(json))
.toList();
}
}
@riverpod
ProductRepository productRepository(ProductRepositoryRef ref) {
return ProductRepository(ref.watch(dioProvider));
}
State with Riverpod:
// lib/features/cart/application/cart_notifier.dart
@riverpod
class CartNotifier extends _$CartNotifier {
@override
Future<Cart> build() async {
final cartId = await ref.watch(cartIdProvider.future);
if (cartId == null) {
return await ref.read(cartRepositoryProvider).createCart();
}
return await ref.read(cartRepositoryProvider).getCart(cartId);
}
Future<void> addItem(String variantId, int quantity) async {
final cart = state.valueOrNull;
if (cart == null) return;
final updated = await ref
.read(cartRepositoryProvider)
.addLineItem(cart.id, variantId, quantity);
state = AsyncData(updated);
}
}
Every MVPHub Flutter template uses this pattern: data layer (repositories + Dio), domain layer (freezed models), presentation layer (widgets + Riverpod notifiers).
Material 3 + Cupertino
Flutter gives you two widget families:
- Material widgets (
material.dart) — Google's Material 3 design system, used on Android - Cupertino widgets (
cupertino.dart) — Apple's iOS design language, used on iOS
MVPHub templates use Material 3 as the base and pull in Cupertino widgets for iOS-specific UX (e.g., swipe-to-go-back navigation, iOS-style date pickers). This gives you a consistent brand look with platform-appropriate interactions.
If you want pixel-identical UI across both platforms (e.g., for a brand where consistency matters more than platform convention), you can use Material widgets on both and Flutter won't complain. Some brands prefer this for recognition.
Flutter vs React Native for ecommerce
| Dimension | Flutter | React Native (Expo) |
|---|---|---|
| Language | Dart | JavaScript/TypeScript |
| UI rendering | Skia (custom) | Native widgets (bridged) |
| UI consistency | Pixel-perfect | Varies by platform |
| Learning curve (from React) | 2-4 weeks | 1-2 weeks |
| Code sharing with web | Limited (Flutter Web exists but ≠ Next.js) | High (monorepo with types and API clients) |
| Hiring pool | Smaller (Dart) | Larger (JavaScript) |
| Animation performance | 60fps with Skia | 60fps with Reanimated 3 |
| App binary size | +3-5 MB (Skia) | Smaller baseline |
| OTA updates | Limited (Codemagic) | Mature (Expo Updates) |
| Mobile-first strategy | Strong fit | Strong fit |
| Web+mobile strategy | Weaker fit | Strong fit |
Pick Flutter when:
- Mobile UI polish is a competitive differentiator
- You have (or can hire) dedicated mobile engineers willing to learn Dart
- Mobile is your primary channel — web is secondary or non-existent
- You need pixel-perfect consistency across devices for brand reasons
Pick React Native when:
- Your web team already writes React and you want shared code between web + mobile
- OTA updates are important (faster content/pricing changes)
- JavaScript hiring pool matters more than pixel-perfect UI
See the dedicated comparison: Flutter vs React Native for Ecommerce.
Stripe integration on Flutter
MVPHub Flutter templates ship with the official flutter_stripe package wired up. The pattern for checkout:
- User taps "Checkout" on the cart screen
- App calls your backend to create a Stripe PaymentIntent
- App presents Stripe's native PaymentSheet (iOS) or PaymentSheet (Android)
- Customer enters card details via the native UI (PCI-compliant — your app never sees card data)
- Stripe confirms the payment, the app polls your backend for order status
- Backend receives the webhook, creates the order in Medusa
Future<void> checkout() async {
// 1. Create PaymentIntent on backend
final intent = await ref.read(checkoutRepositoryProvider).createIntent(cartId);
// 2. Initialize Stripe PaymentSheet
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: intent.clientSecret,
merchantDisplayName: 'Beauty Store',
),
);
// 3. Present the sheet
await Stripe.instance.presentPaymentSheet();
// 4. Done — backend will receive the webhook
}
This is equivalent to the web Stripe Checkout flow, just with a native UI. See the Stripe checkout integration guide for the full webhook pattern.
Common questions
How does code sharing with a web frontend work?
Limited. Flutter uses Dart, web uses TypeScript. The main ways to share:
- API contracts — generate a shared OpenAPI spec from your backend, codegen Dart + TypeScript clients
- Business logic — rewrite in both languages. Unavoidable.
- Data models — use freezed + json_serializable in Dart, zod + types in TS. Keep them in sync manually.
If code sharing is a hard requirement, pick React Native instead — a React/TypeScript monorepo can share components, types, and business logic between web and mobile natively.
Can Flutter apps publish to the App Store and Play Store?
Yes. Flutter apps pass review on both stores with the same process as any native app. MVPHub templates ship with iOS and Android build configs ready to go.
Does Flutter Web work for ecommerce?
Technically yes, practically no. Flutter Web produces a canvas-rendered single-page app that's slow to load and bad for SEO. For web ecommerce, use one of the 5 SSR frameworks (Next.js, Nuxt, SvelteKit, Remix, Astro). Flutter Web is fine for internal tools and admin dashboards but not customer-facing storefronts.
What about desktop?
Flutter desktop builds for macOS, Windows, and Linux from the same codebase. Ecommerce isn't a typical desktop use case, but if you're building a B2B wholesale portal or a POS app, Flutter desktop is a reasonable choice.
Which state management library should I use?
MVPHub Flutter templates ship with Riverpod as the default. It's the most widely recommended choice in 2026, has strong TypeScript-like type inference, and supports async notifiers out of the box. Alternatives:
- Provider (Riverpod's predecessor) — simpler but less powerful
- BLoC — stream-based, verbose but great for complex flows
- Redux/MobX — ports of web patterns, rarely used in production
Riverpod is the safest default for new Flutter projects.
Next steps
- Browse Flutter ecommerce app templates
- Flutter vs React Native comparison
- React Native templates — the JavaScript alternative
- Web templates — pair Flutter with a Next.js/Nuxt/SvelteKit web storefront
- Template Finder quiz
- Compatibility matrix
Flutter is a serious commitment — you're choosing Dart over JavaScript and mobile-first polish over web/mobile code sharing. For brands where those trade-offs match the business, it produces the best mobile ecommerce experience in the catalog. Pick it deliberately; don't drift into it.







