fix: web 端会员页崩溃(dart:io 不可用)+ 嵌套 AppBar 标题重复
- member_center_page 改用 kIsWeb + defaultTargetPlatform 判断 iOS, 删 dart:io 依赖(web 访问 Platform 抛异常导致页面灰屏) - HomePage 移除外层 AppBar,4 个 tab 页各自持有单一标题栏 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -21,8 +21,6 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
int _index = 0;
|
||||
bool _splashShown = false;
|
||||
|
||||
static const _titles = ['我的形象', '我的衣橱', '穿搭方案', '会员中心'];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -44,7 +42,6 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(_titles[_index])),
|
||||
body: IndexedStack(
|
||||
index: _index,
|
||||
children: const [
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
@@ -69,7 +68,9 @@ class _MemberCenterPageState extends ConsumerState<MemberCenterPage> {
|
||||
int? _typeFilter;
|
||||
bool _rewarding = false;
|
||||
|
||||
bool get _isIOS => Platform.isIOS;
|
||||
// web 上无 Platform(dart:io 不可用),且 web 端默认走系统浏览器收银台
|
||||
bool get _isIOS =>
|
||||
!kIsWeb && defaultTargetPlatform == TargetPlatform.iOS;
|
||||
|
||||
Future<void> _openPlans() async {
|
||||
final plans = await showModalBottomSheet<MemberPlan>(
|
||||
@@ -121,101 +122,105 @@ class _MemberCenterPageState extends ConsumerState<MemberCenterPage> {
|
||||
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,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const _RecentDealsCard(),
|
||||
if (member.value?.isVip == false) ...[
|
||||
const SizedBox(height: 12),
|
||||
_AdsRewardCard(
|
||||
rewarding: _rewarding,
|
||||
onClaim: (adType, msg) => _claimReward(adType, msg),
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('会员中心')),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
_MemberCard(
|
||||
member: member,
|
||||
isIOS: _isIOS,
|
||||
onOpenPlans: _openPlans,
|
||||
),
|
||||
],
|
||||
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(height: 12),
|
||||
const _RecentDealsCard(),
|
||||
if (member.value?.isVip == false) ...[
|
||||
const SizedBox(height: 12),
|
||||
_AdsRewardCard(
|
||||
rewarding: _rewarding,
|
||||
onClaim: (adType, msg) => _claimReward(adType, msg),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
for (final entry in _storeTypeLabels.entries) ...[
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Text('合作门店',
|
||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
|
||||
const Spacer(),
|
||||
ChoiceChip(
|
||||
label: Text(entry.value),
|
||||
selected: _typeFilter == entry.key,
|
||||
onSelected: (_) => setState(() => _typeFilter = entry.key),
|
||||
label: const Text('全部'),
|
||||
selected: _typeFilter == null,
|
||||
onSelected: (_) => setState(() => _typeFilter = null),
|
||||
),
|
||||
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: member.value?.isVip == true
|
||||
? const Chip(
|
||||
label: Text('会员价'),
|
||||
labelStyle: TextStyle(
|
||||
color: Colors.white, fontSize: 10),
|
||||
backgroundColor: Colors.amber,
|
||||
visualDensity: VisualDensity.compact,
|
||||
padding: EdgeInsets.zero,
|
||||
)
|
||||
: const Icon(Icons.chevron_right,
|
||||
color: Colors.grey),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 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: member.value?.isVip == true
|
||||
? const Chip(
|
||||
label: Text('会员价'),
|
||||
labelStyle: TextStyle(
|
||||
color: Colors.white, fontSize: 10),
|
||||
backgroundColor: Colors.amber,
|
||||
visualDensity: VisualDensity.compact,
|
||||
padding: EdgeInsets.zero,
|
||||
)
|
||||
: const Icon(Icons.chevron_right,
|
||||
color: Colors.grey),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user