feat: 会员中心页(会员卡/套餐弹层/广告激励/合作门店)
This commit is contained in:
@@ -1,178 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../core/auth/auth_provider.dart';
|
||||
import '../../shared/widgets/error_view.dart';
|
||||
import '../../shared/widgets/loading_view.dart';
|
||||
|
||||
class PartnerStoreInfo {
|
||||
final int id;
|
||||
final String name;
|
||||
final int type; // 1 造型/发型店,2 服装店
|
||||
final String address;
|
||||
final String commissionPolicy;
|
||||
|
||||
const PartnerStoreInfo({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.address,
|
||||
required this.commissionPolicy,
|
||||
});
|
||||
}
|
||||
|
||||
class StoreNotifier extends AsyncNotifier<List<PartnerStoreInfo>> {
|
||||
@override
|
||||
Future<List<PartnerStoreInfo>> build() async {
|
||||
final api = ref.read(apiClientProvider);
|
||||
final list = await api.get<List<dynamic>>('/partner-store/list');
|
||||
return list
|
||||
.map((e) => PartnerStoreInfo(
|
||||
id: (e['id'] as num).toInt(),
|
||||
name: e['name'] as String? ?? '',
|
||||
type: (e['type'] as num?)?.toInt() ?? 1,
|
||||
address: e['address'] as String? ?? '',
|
||||
commissionPolicy: e['commission_policy'] as String? ?? '',
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
state = const AsyncLoading();
|
||||
state = await AsyncValue.guard(build);
|
||||
}
|
||||
}
|
||||
|
||||
final storeProvider =
|
||||
AsyncNotifierProvider<StoreNotifier, List<PartnerStoreInfo>>(
|
||||
StoreNotifier.new);
|
||||
|
||||
const _storeTypeLabels = {1: '造型', 2: '服装'};
|
||||
|
||||
/// 商业化:订阅权益 + 合作门店(佣金导流)
|
||||
class CommercialPage extends ConsumerStatefulWidget {
|
||||
const CommercialPage({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<CommercialPage> createState() => _CommercialPageState();
|
||||
}
|
||||
|
||||
class _CommercialPageState extends ConsumerState<CommercialPage> {
|
||||
int? _typeFilter;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final stores = ref.watch(storeProvider);
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('门店与服务')),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
// 订阅卡片(MVP 占位,支付接入后启用)
|
||||
Card(
|
||||
color: scheme.primaryContainer,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.workspace_premium,
|
||||
size: 36, color: scheme.primary),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('形象会员',
|
||||
style: TextStyle(
|
||||
fontSize: 16, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'会员专享:无限次效果图生成 · 优先 AI 方案 · 门店专属折扣(接入支付后开通)',
|
||||
style: TextStyle(
|
||||
fontSize: 12, color: scheme.primary),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Text('合作门店',
|
||||
style: TextStyle(
|
||||
fontSize: 15, fontWeight: FontWeight.bold)),
|
||||
const Spacer(),
|
||||
ChoiceChip(
|
||||
label: const Text('全部'),
|
||||
selected: _typeFilter == null,
|
||||
onSelected: (_) => setState(() => _typeFilter = null),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
for (final entry in _storeTypeLabels.entries) ...[
|
||||
ChoiceChip(
|
||||
label: Text(entry.value),
|
||||
selected: _typeFilter == entry.key,
|
||||
onSelected: (_) =>
|
||||
setState(() => _typeFilter = entry.key),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
stores.when(
|
||||
loading: () => const Padding(
|
||||
padding: EdgeInsets.only(top: 32),
|
||||
child: LoadingView(text: '加载门店...')),
|
||||
error: (e, _) => Padding(
|
||||
padding: const EdgeInsets.only(top: 32),
|
||||
child: ErrorView(
|
||||
message: e.toString().replaceFirst('Exception: ', ''),
|
||||
onRetry: () => ref.read(storeProvider.notifier).refresh(),
|
||||
),
|
||||
),
|
||||
data: (list) {
|
||||
final shown = _typeFilter == null
|
||||
? list
|
||||
: list.where((s) => s.type == _typeFilter).toList();
|
||||
if (shown.isEmpty) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.only(top: 32),
|
||||
child: Center(
|
||||
child: Text('附近暂无可合作门店',
|
||||
style: TextStyle(color: Colors.grey))),
|
||||
);
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
for (final s in shown) ...[
|
||||
Card(
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: scheme.primaryContainer,
|
||||
child: Icon(s.type == 1
|
||||
? Icons.content_cut
|
||||
: Icons.checkroom),
|
||||
),
|
||||
title: Text(s.name),
|
||||
subtitle: Text('${s.address}\n${s.commissionPolicy}'),
|
||||
isThreeLine: true,
|
||||
trailing: const Icon(Icons.chevron_right,
|
||||
color: Colors.grey),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../commercial/commercial_page.dart';
|
||||
import '../member/member_center_page.dart';
|
||||
import '../outfit/outfit_page.dart';
|
||||
import '../profile/profile_page.dart';
|
||||
import '../wardrobe/wardrobe_page.dart';
|
||||
@@ -16,7 +16,7 @@ class HomePage extends StatefulWidget {
|
||||
class _HomePageState extends State<HomePage> {
|
||||
int _index = 0;
|
||||
|
||||
static const _titles = ['我的形象', '我的衣橱', '穿搭方案', '门店电商'];
|
||||
static const _titles = ['我的形象', '我的衣橱', '穿搭方案', '会员中心'];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -28,7 +28,7 @@ class _HomePageState extends State<HomePage> {
|
||||
ProfilePage(),
|
||||
WardrobePage(),
|
||||
OutfitPage(),
|
||||
CommercialPage(),
|
||||
MemberCenterPage(),
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: NavigationBar(
|
||||
@@ -50,7 +50,7 @@ class _HomePageState extends State<HomePage> {
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.store_outlined),
|
||||
selectedIcon: Icon(Icons.store),
|
||||
label: '门店'),
|
||||
label: '会员'),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,416 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../core/ads/ads_service.dart';
|
||||
import '../../core/auth/auth_provider.dart';
|
||||
import '../../shared/widgets/error_view.dart';
|
||||
import '../../shared/widgets/loading_view.dart';
|
||||
import 'member_provider.dart';
|
||||
import 'pay_page.dart';
|
||||
|
||||
class PartnerStoreInfo {
|
||||
final int id;
|
||||
final String name;
|
||||
final int type; // 1 造型/发型店,2 服装店
|
||||
final String address;
|
||||
final String commissionPolicy;
|
||||
|
||||
const PartnerStoreInfo({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.address,
|
||||
required this.commissionPolicy,
|
||||
});
|
||||
}
|
||||
|
||||
class StoreNotifier extends AsyncNotifier<List<PartnerStoreInfo>> {
|
||||
@override
|
||||
Future<List<PartnerStoreInfo>> build() async {
|
||||
final api = ref.read(apiClientProvider);
|
||||
final list = await api.get<List<dynamic>>('/partner-store/list');
|
||||
return list
|
||||
.map((e) => PartnerStoreInfo(
|
||||
id: (e['id'] as num).toInt(),
|
||||
name: e['name'] as String? ?? '',
|
||||
type: (e['type'] as num?)?.toInt() ?? 1,
|
||||
address: e['address'] as String? ?? '',
|
||||
commissionPolicy: e['commission_policy'] as String? ?? '',
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
state = const AsyncLoading();
|
||||
state = await AsyncValue.guard(build);
|
||||
}
|
||||
}
|
||||
|
||||
final storeProvider =
|
||||
AsyncNotifierProvider<StoreNotifier, List<PartnerStoreInfo>>(StoreNotifier.new);
|
||||
|
||||
const _storeTypeLabels = {1: '造型', 2: '服装'};
|
||||
|
||||
/// 会员中心:会员状态/套餐充值/广告激励 + 合作门店(P0;最近优惠 P1)
|
||||
class MemberCenterPage extends ConsumerStatefulWidget {
|
||||
const MemberCenterPage({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<MemberCenterPage> createState() => _MemberCenterPageState();
|
||||
}
|
||||
|
||||
class _MemberCenterPageState extends ConsumerState<MemberCenterPage> {
|
||||
int? _typeFilter;
|
||||
bool _rewarding = false;
|
||||
|
||||
bool get _isIOS => Platform.isIOS;
|
||||
|
||||
Future<void> _openPlans() async {
|
||||
final plans = await showModalBottomSheet<MemberPlan>(
|
||||
context: context,
|
||||
builder: (ctx) => const _PlanSheet(),
|
||||
);
|
||||
if (plans == null || !mounted) return;
|
||||
try {
|
||||
final result = await createMemberOrder(ref, plans.id);
|
||||
if (!mounted || result.payUrl.isEmpty) return;
|
||||
await context.push('/pay', extra: PayArgs(orderNo: result.orderNo, payUrl: result.payUrl));
|
||||
ref.read(memberProvider.notifier).refresh();
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
Fluttertoast.showToast(
|
||||
msg: e.toString().replaceFirst('Exception: ', ''));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _claimReward(String adType, String successMsg) async {
|
||||
if (_rewarding) return;
|
||||
final ads = ref.read(adsServiceProvider);
|
||||
if (!ads.enabled) {
|
||||
Fluttertoast.showToast(msg: '广告功能暂未开通');
|
||||
return;
|
||||
}
|
||||
setState(() => _rewarding = true);
|
||||
try {
|
||||
final watched = await ads.showRewarded();
|
||||
if (!watched) {
|
||||
Fluttertoast.showToast(msg: '未完整观看,无法领取');
|
||||
return;
|
||||
}
|
||||
final remaining =
|
||||
await ref.read(memberProvider.notifier).claimReward(adType);
|
||||
if (!mounted) return;
|
||||
Fluttertoast.showToast(msg: '$successMsg(今日剩余 $remaining 次)');
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
Fluttertoast.showToast(
|
||||
msg: e.toString().replaceFirst('Exception: ', ''));
|
||||
} finally {
|
||||
if (mounted) setState(() => _rewarding = false);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final member = ref.watch(memberProvider);
|
||||
final stores = ref.watch(storeProvider);
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
_MemberCard(
|
||||
member: member,
|
||||
isIOS: _isIOS,
|
||||
onOpenPlans: _openPlans,
|
||||
),
|
||||
if (member.value?.isVip == false) ...[
|
||||
const SizedBox(height: 12),
|
||||
_AdsRewardCard(
|
||||
rewarding: _rewarding,
|
||||
onClaim: (adType, msg) => _claimReward(adType, msg),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Text('合作门店',
|
||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
|
||||
const Spacer(),
|
||||
ChoiceChip(
|
||||
label: const Text('全部'),
|
||||
selected: _typeFilter == null,
|
||||
onSelected: (_) => setState(() => _typeFilter = null),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
for (final entry in _storeTypeLabels.entries) ...[
|
||||
ChoiceChip(
|
||||
label: Text(entry.value),
|
||||
selected: _typeFilter == entry.key,
|
||||
onSelected: (_) => setState(() => _typeFilter = entry.key),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
stores.when(
|
||||
loading: () => const Padding(
|
||||
padding: EdgeInsets.only(top: 32), child: LoadingView(text: '加载门店...')),
|
||||
error: (e, _) => Padding(
|
||||
padding: const EdgeInsets.only(top: 32),
|
||||
child: ErrorView(
|
||||
message: e.toString().replaceFirst('Exception: ', ''),
|
||||
onRetry: () => ref.read(storeProvider.notifier).refresh(),
|
||||
),
|
||||
),
|
||||
data: (list) {
|
||||
final shown = _typeFilter == null
|
||||
? list
|
||||
: list.where((s) => s.type == _typeFilter).toList();
|
||||
if (shown.isEmpty) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.only(top: 32),
|
||||
child: Center(
|
||||
child: Text('附近暂无可合作门店',
|
||||
style: TextStyle(color: Colors.grey))),
|
||||
);
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
for (final s in shown) ...[
|
||||
Card(
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: scheme.primaryContainer,
|
||||
child: Icon(
|
||||
s.type == 1 ? Icons.content_cut : Icons.checkroom),
|
||||
),
|
||||
title: Text(s.name),
|
||||
subtitle: Text('${s.address}\n${s.commissionPolicy}'),
|
||||
isThreeLine: true,
|
||||
trailing:
|
||||
const Icon(Icons.chevron_right, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MemberCard extends ConsumerWidget {
|
||||
final AsyncValue<MemberInfo> member;
|
||||
final bool isIOS;
|
||||
final VoidCallback onOpenPlans;
|
||||
|
||||
const _MemberCard(
|
||||
{required this.member, required this.isIOS, required this.onOpenPlans});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
return Card(
|
||||
color: scheme.primaryContainer,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: member.when(
|
||||
loading: () => const Text('加载会员状态...',
|
||||
style: TextStyle(fontSize: 13)),
|
||||
error: (e, _) => Text('会员状态加载失败:$e',
|
||||
style: const TextStyle(fontSize: 12)),
|
||||
data: (m) {
|
||||
if (m.isVip) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(children: [
|
||||
Icon(Icons.workspace_premium, size: 30, color: scheme.primary),
|
||||
const SizedBox(width: 10),
|
||||
const Text('形象会员',
|
||||
style:
|
||||
TextStyle(fontSize: 17, fontWeight: FontWeight.bold)),
|
||||
const Spacer(),
|
||||
Chip(
|
||||
label: Text(m.planName),
|
||||
labelStyle:
|
||||
TextStyle(color: scheme.primary, fontSize: 12),
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 6),
|
||||
Text('有效期至 ${m.expireAt}',
|
||||
style: TextStyle(fontSize: 12, color: scheme.primary)),
|
||||
if (benefitTexts(m).isNotEmpty) ...[
|
||||
const SizedBox(height: 8),
|
||||
Wrap(
|
||||
spacing: 6,
|
||||
runSpacing: 6,
|
||||
children: [
|
||||
for (final b in benefitTexts(m))
|
||||
Chip(
|
||||
label: Text(b),
|
||||
labelStyle: const TextStyle(fontSize: 11),
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Row(children: [
|
||||
Icon(Icons.workspace_premium, size: 30),
|
||||
SizedBox(width: 10),
|
||||
Text('形象会员',
|
||||
style:
|
||||
TextStyle(fontSize: 17, fontWeight: FontWeight.bold)),
|
||||
]),
|
||||
const SizedBox(height: 6),
|
||||
const Text('会员专享:无限次效果图生成 · 优先 AI 方案 · 门店专属折扣',
|
||||
style: TextStyle(fontSize: 12)),
|
||||
const SizedBox(height: 10),
|
||||
if (isIOS)
|
||||
const Text('iOS 端暂不支持充值(App Store 政策),可观看广告获得体验会员',
|
||||
style: TextStyle(fontSize: 11, color: Colors.grey))
|
||||
else
|
||||
FilledButton.icon(
|
||||
onPressed: onOpenPlans,
|
||||
icon: const Icon(Icons.payment, size: 18),
|
||||
label: const Text('开通会员'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AdsRewardCard extends ConsumerWidget {
|
||||
final bool rewarding;
|
||||
final void Function(String adType, String msg) onClaim;
|
||||
|
||||
const _AdsRewardCard({required this.rewarding, required this.onClaim});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('免费获取权益',
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 8),
|
||||
ListTile(
|
||||
dense: true,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: const Icon(Icons.ondemand_video, color: Colors.deepPurple),
|
||||
title: const Text('看视频 · 效果图 +1'),
|
||||
subtitle: const Text('每日最多 2 次,次日重置'),
|
||||
trailing: OutlinedButton(
|
||||
onPressed: rewarding
|
||||
? null
|
||||
: () => onClaim('effect_extra', '已获得 1 次效果图'),
|
||||
child: const Text('看视频'),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: const Icon(Icons.ondemand_video, color: Colors.teal),
|
||||
title: const Text('看视频 · 体验会员 1 天'),
|
||||
subtitle: const Text('每日最多 1 次,含无限效果图'),
|
||||
trailing: OutlinedButton(
|
||||
onPressed: rewarding
|
||||
? null
|
||||
: () => onClaim('vip_trial', '已获得 1 天体验会员'),
|
||||
child: const Text('看视频'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PlanSheet extends ConsumerWidget {
|
||||
const _PlanSheet();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final plans = ref.watch(memberPlanProvider);
|
||||
return SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Text('选择会员套餐',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 12),
|
||||
plans.when(
|
||||
loading: () => const Padding(
|
||||
padding: EdgeInsets.all(24), child: LoadingView()),
|
||||
error: (e, _) => Text('套餐加载失败:$e',
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(color: Colors.grey)),
|
||||
data: (list) => list.isEmpty
|
||||
? const Padding(
|
||||
padding: EdgeInsets.all(24),
|
||||
child: Text('暂未开放套餐', textAlign: TextAlign.center),
|
||||
)
|
||||
: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
for (final p in list)
|
||||
ListTile(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8)),
|
||||
tileColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.primaryContainer
|
||||
.withValues(alpha: 0.5),
|
||||
title: Text(p.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 15, fontWeight: FontWeight.w600)),
|
||||
subtitle: Text(
|
||||
'${p.durationDays} 天 · ${p.features.map((f) => benefitLabels[f] ?? f).join(' · ')}',
|
||||
style: const TextStyle(fontSize: 12),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
trailing: Text(p.priceText,
|
||||
style: TextStyle(
|
||||
color:
|
||||
Theme.of(context).colorScheme.primary,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold)),
|
||||
onTap: () => Navigator.pop(context, p),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -3,8 +3,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'features/auth/login_page.dart';
|
||||
import 'features/commercial/commercial_page.dart';
|
||||
import 'features/home/home_page.dart';
|
||||
import 'features/member/member_center_page.dart';
|
||||
import 'features/outfit/outfit_page.dart';
|
||||
import 'features/outfit/outfit_provider.dart';
|
||||
import 'features/outfit/plan_effect_page.dart';
|
||||
@@ -77,7 +77,7 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
),
|
||||
GoRoute(
|
||||
path: '/commercial',
|
||||
builder: (ctx, state) => const CommercialPage(),
|
||||
builder: (ctx, state) => const MemberCenterPage(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/plan-viewer',
|
||||
|
||||
Reference in New Issue
Block a user