feat(mining-app): implement hide/show amounts toggle

- Add hideAmountsProvider to control amount visibility
- Add tap handler to eye icon in total contribution card
- Toggle icon between visibility_outlined and visibility_off_outlined
- Hide amounts with **** when toggled in:
  - Total contribution value
  - Three column stats (personal, team level, team bonus)
  - Today's estimated earnings
  - Contribution detail summary rows

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-14 19:22:03 -08:00
parent 02cc79d67a
commit 91b8cca41c
2 changed files with 37 additions and 16 deletions

View File

@ -71,16 +71,16 @@ class ContributionPage extends ConsumerWidget {
sliver: SliverList(
delegate: SliverChildListDelegate([
//
_buildTotalContributionCard(contribution, isLoading),
_buildTotalContributionCard(ref, contribution, isLoading),
const SizedBox(height: 16),
//
_buildThreeColumnStats(contribution, isLoading),
_buildThreeColumnStats(ref, contribution, isLoading),
const SizedBox(height: 16),
//
_buildTodayEstimateCard(estimatedEarnings, isLoading || isStatsLoading),
_buildTodayEstimateCard(ref, estimatedEarnings, isLoading || isStatsLoading),
const SizedBox(height: 16),
//
_buildContributionDetailCard(context, contribution, isLoading),
_buildContributionDetailCard(context, ref, contribution, isLoading),
const SizedBox(height: 16),
//
_buildTeamStatsCard(contribution, isLoading),
@ -156,8 +156,9 @@ class ContributionPage extends ConsumerWidget {
);
}
Widget _buildTotalContributionCard(Contribution? contribution, bool isLoading) {
Widget _buildTotalContributionCard(WidgetRef ref, Contribution? contribution, bool isLoading) {
final total = contribution?.totalContribution ?? '0';
final hideAmounts = ref.watch(hideAmountsProvider);
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
@ -174,12 +175,21 @@ class ContributionPage extends ConsumerWidget {
'总贡献值',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: _grayText),
),
Icon(Icons.visibility_outlined, color: _grayText.withOpacity(0.5), size: 18),
GestureDetector(
onTap: () {
ref.read(hideAmountsProvider.notifier).state = !hideAmounts;
},
child: Icon(
hideAmounts ? Icons.visibility_off_outlined : Icons.visibility_outlined,
color: _grayText.withOpacity(0.5),
size: 18,
),
),
],
),
const SizedBox(height: 8),
DataText(
data: isLoading ? null : formatAmount(total),
data: isLoading ? null : (hideAmounts ? '******' : formatAmount(total)),
isLoading: isLoading,
placeholder: '----',
style: const TextStyle(
@ -214,7 +224,8 @@ class ContributionPage extends ConsumerWidget {
);
}
Widget _buildThreeColumnStats(Contribution? contribution, bool isLoading) {
Widget _buildThreeColumnStats(WidgetRef ref, Contribution? contribution, bool isLoading) {
final hideAmounts = ref.watch(hideAmountsProvider);
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
@ -223,15 +234,15 @@ class ContributionPage extends ConsumerWidget {
),
child: Row(
children: [
_buildStatColumn('个人贡献值', contribution?.personalContribution, isLoading, false),
_buildStatColumn('团队层级', contribution?.teamLevelContribution, isLoading, true),
_buildStatColumn('团队奖励', contribution?.teamBonusContribution, isLoading, true),
_buildStatColumn('个人贡献值', contribution?.personalContribution, isLoading, false, hideAmounts),
_buildStatColumn('团队层级', contribution?.teamLevelContribution, isLoading, true, hideAmounts),
_buildStatColumn('团队奖励', contribution?.teamBonusContribution, isLoading, true, hideAmounts),
],
),
);
}
Widget _buildStatColumn(String label, String? value, bool isLoading, bool showLeftBorder) {
Widget _buildStatColumn(String label, String? value, bool isLoading, bool showLeftBorder, bool hideAmounts) {
return Expanded(
child: Container(
decoration: showLeftBorder
@ -245,7 +256,7 @@ class ContributionPage extends ConsumerWidget {
Text(label, style: const TextStyle(fontSize: 12, color: _grayText)),
const SizedBox(height: 4),
DataText(
data: value != null ? formatAmount(value) : null,
data: value != null ? (hideAmounts ? '****' : formatAmount(value)) : null,
isLoading: isLoading,
placeholder: '--',
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: _darkText),
@ -257,7 +268,8 @@ class ContributionPage extends ConsumerWidget {
);
}
Widget _buildTodayEstimateCard(EstimatedEarnings earnings, bool isLoading) {
Widget _buildTodayEstimateCard(WidgetRef ref, EstimatedEarnings earnings, bool isLoading) {
final hideAmounts = ref.watch(hideAmountsProvider);
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
@ -306,7 +318,7 @@ class ContributionPage extends ConsumerWidget {
TextSpan(
children: [
TextSpan(
text: earnings.isValid ? formatAmount(earnings.dailyShares) : '--',
text: hideAmounts ? '****' : (earnings.isValid ? formatAmount(earnings.dailyShares) : '--'),
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
@ -329,9 +341,11 @@ class ContributionPage extends ConsumerWidget {
Widget _buildContributionDetailCard(
BuildContext context,
WidgetRef ref,
Contribution? contribution,
bool isLoading,
) {
final hideAmounts = ref.watch(hideAmountsProvider);
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
@ -374,6 +388,7 @@ class ContributionPage extends ConsumerWidget {
title: '本人种植',
subtitle: '个人认种榴莲树产生的贡献值',
amount: contribution?.personalContribution ?? '0',
hideAmounts: hideAmounts,
),
const Divider(height: 24),
_buildDetailSummaryRow(
@ -382,6 +397,7 @@ class ContributionPage extends ConsumerWidget {
title: '团队层级',
subtitle: '直推及间推用户认种产生的贡献值',
amount: contribution?.teamLevelContribution ?? '0',
hideAmounts: hideAmounts,
),
const Divider(height: 24),
_buildDetailSummaryRow(
@ -390,6 +406,7 @@ class ContributionPage extends ConsumerWidget {
title: '团队奖励',
subtitle: '满足条件后获得的额外奖励贡献值',
amount: contribution?.teamBonusContribution ?? '0',
hideAmounts: hideAmounts,
),
],
),
@ -445,6 +462,7 @@ class ContributionPage extends ConsumerWidget {
required String title,
required String subtitle,
required String amount,
required bool hideAmounts,
}) {
return Row(
children: [
@ -475,7 +493,7 @@ class ContributionPage extends ConsumerWidget {
),
),
Text(
formatAmount(amount),
hideAmounts ? '****' : formatAmount(amount),
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: _green),
),
],

View File

@ -173,3 +173,6 @@ final estimatedEarningsProvider = Provider.family<EstimatedEarnings, String>((re
isValid: true,
);
});
///
final hideAmountsProvider = StateProvider<bool>((ref) => false);