案例5:Flutter电商App完整实现¶
📋 案例概述¶
项目简介¶
构建一个功能完整的跨平台电商应用,展示Flutter在电商场景下的最佳实践。本案例涵盖商品展示、购物车、订单管理、支付集成等核心电商功能。
预计开发时间: 10-12小时 难度等级: ⭐⭐⭐⭐⭐ 涉及知识点: Flutter状态管理、路由导航、网络请求、本地存储、支付集成
功能特性¶
- 🏠 首页轮播图与商品分类
- 🔍 商品搜索与筛选
- 🛒 购物车管理
- 💳 订单创建与支付
- 📦 订单追踪
- 👤 用户中心与地址管理
- ❤️ 商品收藏
- 🌙 深色模式支持
🏗️ 项目架构¶
Text Only
lib/
├── main.dart
├── app.dart
├── config/
│ ├── routes.dart
│ ├── theme.dart
│ └── constants.dart
├── data/
│ ├── models/
│ │ ├── product.dart
│ │ ├── category.dart
│ │ ├── cart_item.dart
│ │ ├── order.dart
│ │ └── user.dart
│ ├── repositories/
│ │ ├── product_repository.dart
│ │ ├── cart_repository.dart
│ │ ├── order_repository.dart
│ │ └── user_repository.dart
│ └── providers/
│ ├── api_provider.dart
│ └── storage_provider.dart
├── services/
│ ├── api_service.dart
│ ├── auth_service.dart
│ └── payment_service.dart
├── state/
│ ├── cart_provider.dart
│ ├── product_provider.dart
│ ├── order_provider.dart
│ └── theme_provider.dart
├── ui/
│ ├── screens/
│ │ ├── home/
│ │ │ ├── home_screen.dart
│ │ │ └── widgets/
│ │ ├── product/
│ │ │ ├── product_list_screen.dart
│ │ │ ├── product_detail_screen.dart
│ │ │ └── widgets/
│ │ ├── cart/
│ │ │ ├── cart_screen.dart
│ │ │ └── widgets/
│ │ ├── order/
│ │ │ ├── order_list_screen.dart
│ │ │ ├── order_detail_screen.dart
│ │ │ └── checkout_screen.dart
│ │ ├── profile/
│ │ │ ├── profile_screen.dart
│ │ │ ├── address_screen.dart
│ │ │ └── settings_screen.dart
│ │ └── auth/
│ │ ├── login_screen.dart
│ │ └── register_screen.dart
│ ├── widgets/
│ │ ├── common/
│ │ │ ├── loading_widget.dart
│ │ │ ├── error_widget.dart
│ │ │ └── empty_widget.dart
│ │ └── product/
│ │ ├── product_card.dart
│ │ ├── product_grid.dart
│ │ └── quantity_selector.dart
│ └── theme/
│ ├── app_theme.dart
│ └── app_colors.dart
└── utils/
├── extensions.dart
├── helpers.dart
└── validators.dart
🛠️ 技术实现¶
1. 数据模型¶
product.dart¶
Dart
import 'package:freezed_annotation/freezed_annotation.dart';
// freezed代码生成文件,运行 dart run build_runner build 自动生成
part 'product.freezed.dart';
part 'product.g.dart';
// 使用freezed创建不可变数据模型,自动生成copyWith/==/toString等方法
@freezed
class Product with _$Product {
// 工厂构造函数定义商品属性,required表示必填字段
const factory Product({
required String id,
required String name,
required String description,
required double price, // 当前售价
required double originalPrice, // 原价,用于计算折扣
required List<String> images,
required String categoryId,
required String categoryName,
required int stock,
required double rating,
required int reviewCount,
required List<String> tags,
@Default(false) bool isFavorite, // @Default设置默认值,JSON缺失时使用
DateTime? createdAt, // 可空类型,表示可选字段
}) = _Product;
// 从JSON Map反序列化,实现由json_serializable自动生成
factory Product.fromJson(Map<String, dynamic> json) =>
_$ProductFromJson(json);
}
// 商品分类模型,支持嵌套子分类
@freezed
class Category with _$Category {
const factory Category({
required String id,
required String name,
required String icon,
required String image,
@Default([]) List<Category> subCategories, // 子分类列表,默认空
}) = _Category;
factory Category.fromJson(Map<String, dynamic> json) =>
_$CategoryFromJson(json);
}
cart_item.dart¶
Dart
import 'package:freezed_annotation/freezed_annotation.dart';
import 'product.dart';
part 'cart_item.freezed.dart';
part 'cart_item.g.dart';
// 购物车单项模型
@freezed
class CartItem with _$CartItem {
// 私有构造函数,允许在freezed类中定义自定义getter方法
const CartItem._();
const factory CartItem({
required String id,
required Product product,
required int quantity,
String? selectedVariant, // 已选规格(如颜色、尺码)
DateTime? addedAt,
}) = _CartItem;
factory CartItem.fromJson(Map<String, dynamic> json) =>
_$CartItemFromJson(json);
// 计算属性:单项总价 = 单价 × 数量
double get totalPrice => product.price * quantity;
}
// 购物车模型,包含商品列表和优惠信息
@freezed
class Cart with _$Cart {
const Cart._(); // 私有构造函数,启用自定义getter
const factory Cart({
@Default([]) List<CartItem> items, // 购物车商品列表
String? couponCode, // 优惠券码
double? discount, // 折扣金额
}) = _Cart;
factory Cart.fromJson(Map<String, dynamic> json) => _$CartFromJson(json);
// 以下为计算属性,基于购物车内容动态计算
double get subtotal => items.fold(0, (sum, item) => sum + item.totalPrice); // 商品小计
double get total => subtotal - (discount ?? 0); // 实付金额(??为空合并运算符)
int get itemCount => items.fold(0, (sum, item) => sum + item.quantity); // 总商品数
bool get isEmpty => items.isEmpty;
}
order.dart¶
Dart
import 'package:freezed_annotation/freezed_annotation.dart';
import 'cart_item.dart';
import 'user.dart';
part 'order.freezed.dart';
part 'order.g.dart';
// 订单状态枚举,表示订单生命周期的各个阶段
enum OrderStatus {
pending, // 待支付
paid, // 已支付
processing, // 处理中
shipped, // 已发货
delivered, // 已送达
cancelled, // 已取消
refunded, // 已退款
}
// 支付方式枚举
enum PaymentMethod {
alipay, // 支付宝
wechatPay, // 微信支付
creditCard, // 信用卡
}
// 订单模型,包含订单完整信息
@freezed
class Order with _$Order {
const factory Order({
required String id,
required String orderNumber, // 用户可见的订单编号
required String userId,
required List<CartItem> items, // 订单商品列表
required Address shippingAddress, // 收货地址
required double subtotal, // 商品小计
required double shippingCost, // 运费
required double discount, // 折扣金额
required double total, // 实付总额
required OrderStatus status,
required PaymentMethod paymentMethod,
String? paymentId, // 第三方支付流水号
String? trackingNumber, // 物流追踪号
DateTime? paidAt, // 支付时间
DateTime? shippedAt, // 发货时间
DateTime? deliveredAt, // 签收时间
required DateTime createdAt,
DateTime? updatedAt,
}) = _Order;
factory Order.fromJson(Map<String, dynamic> json) => _$OrderFromJson(json);
}
2. 状态管理 (Riverpod)¶
cart_provider.dart¶
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../data/models/cart_item.dart';
import '../data/models/product.dart';
import '../data/repositories/cart_repository.dart';
// Provider: 提供CartRepository单例,注入本地存储依赖
final cartRepositoryProvider = Provider<CartRepository>((ref) {
return CartRepository(ref.read(storageProvider));
});
// StateNotifierProvider: 管理购物车状态,暴露CartNotifier和Cart
final cartProvider = StateNotifierProvider<CartNotifier, Cart>((ref) {
return CartNotifier(ref.read(cartRepositoryProvider));
});
// StateNotifier: 购物车业务逻辑,修改state自动通知所有监听者
class CartNotifier extends StateNotifier<Cart> {
final CartRepository _repository;
// 初始化空购物车,然后异步加载本地持久化数据
CartNotifier(this._repository) : super(const Cart()) {
_loadCart();
}
// 从本地存储恢复购物车数据
Future<void> _loadCart() async {
final cart = await _repository.getCart();
state = cart;
}
// 添加商品到购物车,已存在则累加数量
Future<void> addToCart(Product product, {int quantity = 1}) async {
// 查找购物车中是否已有该商品
final existingItemIndex = state.items.indexWhere(
(item) => item.product.id == product.id,
);
if (existingItemIndex >= 0) {
// 已存在:用copyWith创建更新后的不可变对象
final updatedItems = [...state.items];
final existingItem = updatedItems[existingItemIndex];
updatedItems[existingItemIndex] = existingItem.copyWith(
quantity: existingItem.quantity + quantity,
);
state = state.copyWith(items: updatedItems);
} else {
// 添加新商品
final newItem = CartItem(
id: DateTime.now().millisecondsSinceEpoch.toString(),
product: product,
quantity: quantity,
addedAt: DateTime.now(),
);
state = state.copyWith(items: [...state.items, newItem]);
}
await _repository.saveCart(state);
}
// 更新商品数量,数量<=0时自动移除
Future<void> updateQuantity(String itemId, int quantity) async {
if (quantity <= 0) {
await removeFromCart(itemId);
return;
}
// 使用map遍历并更新匹配项,保持不可变性
final updatedItems = state.items.map((item) {
if (item.id == itemId) {
return item.copyWith(quantity: quantity);
}
return item;
}).toList();
state = state.copyWith(items: updatedItems);
await _repository.saveCart(state); // 同步持久化到本地存储
}
// 从购物车移除指定商品
Future<void> removeFromCart(String itemId) async {
final updatedItems = state.items.where((item) => item.id != itemId).toList();
state = state.copyWith(items: updatedItems);
await _repository.saveCart(state);
}
// 清空购物车
Future<void> clearCart() async {
state = const Cart(); // 重置为空购物车常量
await _repository.clearCart();
}
// 应用优惠券,验证通过后更新折扣金额
Future<void> applyCoupon(String code) async {
final discount = await _repository.validateCoupon(code);
if (discount != null) {
state = state.copyWith(
couponCode: code,
discount: discount,
);
}
}
}
product_provider.dart¶
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../data/models/product.dart';
import '../data/repositories/product_repository.dart';
// Provider注入API依赖,提供商品仓库实例
final productRepositoryProvider = Provider<ProductRepository>((ref) {
return ProductRepository(ref.read(apiProvider));
});
// 商品列表 - FutureProvider.family支持传参(筛选条件),自动管理异步状态
final productsProvider = FutureProvider.family<List<Product>, ProductFilter>(
(ref, filter) async {
final repository = ref.read(productRepositoryProvider);
return repository.getProducts(filter);
},
);
// 商品详情 - family参数为商品ID,每个ID对应独立的缓存
final productDetailProvider = FutureProvider.family<Product, String>(
(ref, productId) async {
final repository = ref.read(productRepositoryProvider);
return repository.getProductDetail(productId);
},
);
// 分类列表 - 无参FutureProvider,全局共享缓存
final categoriesProvider = FutureProvider<List<Category>>((ref) async {
final repository = ref.read(productRepositoryProvider);
return repository.getCategories();
});
// 搜索商品 - StateNotifierProvider管理搜索状态,支持手动触发搜索
final productSearchProvider = StateNotifierProvider<
ProductSearchNotifier, AsyncValue<List<Product>>>(
(ref) => ProductSearchNotifier(ref.read(productRepositoryProvider)),
);
// 商品搜索状态管理,AsyncValue封装 加载/成功/错误 三种状态
class ProductSearchNotifier extends StateNotifier<AsyncValue<List<Product>>> {
final ProductRepository _repository;
ProductSearchNotifier(this._repository) : super(const AsyncValue.loading());
// 执行搜索,自动管理加载状态和错误处理
Future<void> search(String query, {String? categoryId}) async {
state = const AsyncValue.loading(); // 切换到加载状态
try {
final products = await _repository.searchProducts(
query: query,
categoryId: categoryId,
);
state = AsyncValue.data(products); // 成功:包装数据
} catch (e, stack) {
state = AsyncValue.error(e, stack); // 失败:捕获错误和堆栈
}
}
}
// 商品筛选条件,作为FutureProvider.family的参数
class ProductFilter {
final String? categoryId; // 分类ID
final String? sortBy; // 排序方式
final double? minPrice; // 最低价格
final double? maxPrice; // 最高价格
final double? minRating; // 最低评分
final int page; // 分页页码
final int pageSize; // 每页数量
const ProductFilter({
this.categoryId,
this.sortBy,
this.minPrice,
this.maxPrice,
this.minRating,
this.page = 1,
this.pageSize = 20,
});
}
3. UI层实现¶
home_screen.dart¶
Dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// ConsumerWidget: Riverpod版StatelessWidget,通过ref访问Provider
class HomeScreen extends ConsumerWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
// ref.watch监听Provider变化,数据更新时自动重建Widget
final categoriesAsync = ref.watch(categoriesProvider);
final featuredProductsAsync = ref.watch(
productsProvider(const ProductFilter(pageSize: 10)),
);
return Scaffold(
appBar: AppBar(
title: const Text('Flutter商城'),
actions: [
IconButton(
icon: const Icon(Icons.search),
onPressed: () => Navigator.pushNamed(context, '/search'),
),
IconButton(
icon: const Icon(Icons.shopping_cart),
onPressed: () => Navigator.pushNamed(context, '/cart'),
),
],
),
// 下拉刷新:ref.refresh强制Provider重新获取数据
body: RefreshIndicator(
onRefresh: () async {
ref.refresh(categoriesProvider);
ref.refresh(productsProvider(const ProductFilter(pageSize: 10)));
},
// CustomScrollView + Sliver组合实现多种布局混合滚动
child: CustomScrollView(
slivers: [
// 轮播图
const SliverToBoxAdapter(
child: BannerCarousel(),
),
// 分类 - when模式匹配AsyncValue的三种状态
SliverToBoxAdapter(
child: categoriesAsync.when(
data: (categories) => CategoryGrid(categories: categories),
loading: () => const SizedBox.shrink(), // 加载中不显示
error: (_, __) => const SizedBox.shrink(), // 出错时静默处理
),
),
// 特色商品标题
const SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
'热门商品',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
),
// 商品网格 - Sliver懒加载,仅构建可见区域的Widget
featuredProductsAsync.when(
data: (products) => SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 16),
sliver: SliverGrid(
// 固定2列网格,宽高比0.7(卡片高度大于宽度)
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.7,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
delegate: SliverChildBuilderDelegate(
(context, index) => ProductCard(product: products[index]),
childCount: products.length,
),
),
),
loading: () => const SliverFillRemaining(
child: Center(child: CircularProgressIndicator()),
),
error: (error, _) => SliverFillRemaining(
child: Center(child: Text('Error: $error')),
),
),
],
),
),
);
}
}
product_card.dart¶
Dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../data/models/product.dart';
import '../../state/cart_provider.dart';
// 商品卡片组件,使用ConsumerWidget以访问Riverpod状态
class ProductCard extends ConsumerWidget {
final Product product;
const ProductCard({
Key? key,
required this.product,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return Card(
clipBehavior: Clip.antiAlias, // 裁剪子Widget溢出部分,实现圆角效果
child: InkWell(
onTap: () => Navigator.pushNamed( // 点击跳转商品详情页
context,
'/product-detail',
arguments: product.id,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 商品图片
AspectRatio(
aspectRatio: 1,
child: Stack(
fit: StackFit.expand,
children: [
Image.network(
product.images.first,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const Center(child: CircularProgressIndicator());
},
),
// 折扣标签
if (product.price < product.originalPrice)
Positioned(
top: 8,
left: 8,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(4),
),
child: Text(
'-${((1 - product.price / product.originalPrice) * 100).toInt()}%',
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
),
// 商品信息
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 4),
// 评分
Row(
children: [
Icon(
Icons.star,
size: 14,
color: Colors.amber[600],
),
const SizedBox(width: 4),
Text(
'${product.rating}',
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
const SizedBox(width: 4),
Text(
'(${product.reviewCount})',
style: TextStyle(
fontSize: 12,
color: Colors.grey[400],
),
),
],
),
const SizedBox(height: 8),
// 价格
Row(
children: [
Text(
'¥${product.price.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryColor,
),
),
if (product.price < product.originalPrice) ...[
const SizedBox(width: 8),
Text(
'¥${product.originalPrice.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 12,
color: Colors.grey[400],
decoration: TextDecoration.lineThrough,
),
),
],
const Spacer(),
// 加入购物车按钮
InkWell(
onTap: () {
// ref.read用于一次性操作(非监听),.notifier获取StateNotifier实例
ref.read(cartProvider.notifier).addToCart(product);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('已添加到购物车'),
duration: Duration(seconds: 1),
),
);
},
child: Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(4),
),
child: const Icon(
Icons.add_shopping_cart,
size: 16,
color: Colors.white,
),
),
),
],
),
],
),
),
],
),
),
);
}
}
cart_screen.dart¶
Dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../state/cart_provider.dart';
class CartScreen extends ConsumerWidget {
const CartScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
// ref.watch监听购物车状态,任何变更自动触发UI重建
final cart = ref.watch(cartProvider);
return Scaffold(
appBar: AppBar(
title: const Text('购物车'),
actions: [
if (!cart.isEmpty)
TextButton(
onPressed: () {
ref.read(cartProvider.notifier).clearCart();
},
child: const Text('清空'),
),
],
),
body: cart.isEmpty
? const EmptyCartWidget()
: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: cart.items.length,
itemBuilder: (context, index) {
final item = cart.items[index];
return CartItemTile(
item: item,
onQuantityChanged: (quantity) {
ref
.read(cartProvider.notifier)
.updateQuantity(item.id, quantity);
},
onRemove: () {
ref
.read(cartProvider.notifier)
.removeFromCart(item.id);
},
);
},
),
),
CartSummary(cart: cart),
],
),
);
}
}
class CartItemTile extends StatelessWidget {
final CartItem item;
final ValueChanged<int> onQuantityChanged;
final VoidCallback onRemove;
const CartItemTile({
Key? key,
required this.item,
required this.onQuantityChanged,
required this.onRemove,
}) : super(key: key);
@override
Widget build(BuildContext context) {
// Dismissible实现左滑删除手势交互
return Dismissible(
key: Key(item.id), // 唯一Key用于动画和状态识别
direction: DismissDirection.endToStart, // 仅支持从右向左滑动
background: Container(
color: Colors.red,
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 16),
child: const Icon(Icons.delete, color: Colors.white),
),
onDismissed: (_) => onRemove(), // 滑动完成后执行删除回调
child: Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
// 商品图片
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
item.product.images.first,
width: 80,
height: 80,
fit: BoxFit.cover,
),
),
const SizedBox(width: 12),
// 商品信息
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.product.name,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w500),
),
const SizedBox(height: 4),
Text(
'¥${item.product.price.toStringAsFixed(2)}',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
),
),
],
),
),
// 数量选择器
QuantitySelector(
quantity: item.quantity,
onChanged: onQuantityChanged,
),
],
),
),
),
);
}
}
class CartSummary extends StatelessWidget {
final Cart cart;
const CartSummary({Key? key, required this.cart}) : super(key: key);
@override
Widget build(BuildContext context) {
// 底部结算栏,使用阴影与列表区域视觉分离
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, -5), // 向上投射阴影
),
],
),
child: SafeArea( // 避免底部被系统导航栏遮挡
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 小计
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('共 ${cart.itemCount} 件商品'),
Text(
'合计: ¥${cart.total.toStringAsFixed(2)}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 16),
// 结算按钮 - 购物车为空时禁用(onPressed传null)
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: cart.isEmpty
? null // 禁用状态,按钮变灰
: () => Navigator.pushNamed(context, '/checkout'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text(
'去结算',
style: TextStyle(fontSize: 16),
),
),
),
],
),
),
);
}
}
4. 路由配置¶
Dart
// routes.dart - 集中管理应用所有路由配置
import 'package:flutter/material.dart';
import '../ui/screens/home/home_screen.dart';
import '../ui/screens/product/product_detail_screen.dart';
import '../ui/screens/cart/cart_screen.dart';
import '../ui/screens/order/checkout_screen.dart';
import '../ui/screens/profile/profile_screen.dart';
// 路由管理类,定义路径常量和页面映射
class AppRoutes {
// 路由路径常量,避免硬编码字符串
static const String home = '/';
static const String productList = '/products';
static const String productDetail = '/product-detail';
static const String cart = '/cart';
static const String checkout = '/checkout';
static const String orders = '/orders';
static const String orderDetail = '/order-detail';
static const String profile = '/profile';
static const String addresses = '/addresses';
static const String login = '/login';
static const String register = '/register';
static const String search = '/search';
static const String favorites = '/favorites';
static const String settings = '/settings';
// 静态路由表:无参数的简单页面使用命名路由映射
static Map<String, WidgetBuilder> get routes => {
home: (context) => const HomeScreen(),
cart: (context) => const CartScreen(),
checkout: (context) => const CheckoutScreen(),
profile: (context) => const ProfileScreen(),
// ... 其他路由
};
// 动态路由生成:处理需要传参的页面(如商品详情需要productId)
static Route<dynamic>? onGenerateRoute(RouteSettings settings) {
switch (settings.name) {
case productDetail:
// 从路由参数中提取商品ID
final productId = settings.arguments as String;
return MaterialPageRoute(
builder: (_) => ProductDetailScreen(productId: productId),
);
case orderDetail:
final orderId = settings.arguments as String;
return MaterialPageRoute(
builder: (_) => OrderDetailScreen(orderId: orderId),
);
default:
return null; // 未匹配的路由返回null,交由系统处理
}
}
}
🔧 依赖配置¶
YAML
# pubspec.yaml
name: flutter_shop
description: A Flutter e-commerce app
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
flutter:
sdk: flutter
# 状态管理
flutter_riverpod: ^2.4.9
riverpod_annotation: ^2.3.3
# 网络请求
dio: ^5.4.0
retrofit: ^4.0.3
# 本地存储
shared_preferences: ^2.2.2
hive: ^2.2.3
hive_flutter: ^1.1.0
# 路由
go_router: ^13.0.1
# UI组件
cached_network_image: ^3.3.0
flutter_staggered_grid_view: ^0.7.0
carousel_slider: ^4.2.1
shimmer: ^3.0.0
flutter_slidable: ^3.0.1
# 支付
flutter_stripe: ^10.0.0
alipay_kit: ^4.0.0
# 其他
freezed_annotation: ^2.4.1
json_annotation: ^4.8.1
intl: ^0.18.1
logger: ^2.0.2
url_launcher: ^6.2.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.1
build_runner: ^2.4.7
freezed: ^2.4.5
json_serializable: ^6.7.1
retrofit_generator: ^8.0.6
riverpod_generator: ^2.3.9
hive_generator: ^2.0.1
🎯 练习任务¶
基础任务¶
- ✅ 完成商品列表和详情页
- ✅ 实现购物车功能
- ✅ 添加商品分类浏览
进阶任务¶
- 🔄 集成支付功能(支付宝/微信支付)
- 🔄 实现订单追踪功能
- 🔄 添加商品评价功能
挑战任务¶
- 🎯 实现直播带货功能
- 🎯 添加AR试穿功能
- 🎯 实现秒杀活动功能
📚 学习要点¶
- Flutter状态管理: Riverpod的最佳实践
- 跨平台开发: 一套代码多端运行
- 性能优化: 列表优化、图片缓存
- 支付集成: 第三方支付SDK集成
- 用户体验: 动画、过渡效果