feat: add payment card grouping and analysis
This commit is contained in:
@@ -1,16 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../providers/subscription_provider.dart';
|
||||
import '../providers/category_provider.dart';
|
||||
import '../utils/subscription_category_helper.dart';
|
||||
import '../widgets/native_ad_widget.dart';
|
||||
import '../widgets/main_summary_card.dart';
|
||||
import '../widgets/subscription_list_widget.dart';
|
||||
import '../widgets/empty_state_widget.dart';
|
||||
// import '../theme/app_colors.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class HomeContent extends StatelessWidget {
|
||||
import '../l10n/app_localizations.dart';
|
||||
import '../providers/category_provider.dart';
|
||||
import '../providers/payment_card_provider.dart';
|
||||
import '../providers/subscription_provider.dart';
|
||||
import '../utils/subscription_grouping_helper.dart';
|
||||
import '../widgets/empty_state_widget.dart';
|
||||
import '../widgets/main_summary_card.dart';
|
||||
import '../widgets/native_ad_widget.dart';
|
||||
import '../widgets/subscription_list_widget.dart';
|
||||
|
||||
class HomeContent extends StatefulWidget {
|
||||
final AnimationController fadeController;
|
||||
final AnimationController rotateController;
|
||||
final AnimationController slideController;
|
||||
@@ -31,10 +33,53 @@ class HomeContent extends StatelessWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final provider = context.watch<SubscriptionProvider>();
|
||||
State<HomeContent> createState() => _HomeContentState();
|
||||
}
|
||||
|
||||
if (provider.isLoading) {
|
||||
class _HomeContentState extends State<HomeContent> {
|
||||
static const _groupingPrefKey = 'home_grouping_mode';
|
||||
SubscriptionGroupingMode _groupingMode = SubscriptionGroupingMode.category;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadGroupingPreference();
|
||||
}
|
||||
|
||||
Future<void> _loadGroupingPreference() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final stored = prefs.getString(_groupingPrefKey);
|
||||
if (stored == 'paymentCard') {
|
||||
setState(() {
|
||||
_groupingMode = SubscriptionGroupingMode.paymentCard;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _saveGroupingPreference(SubscriptionGroupingMode mode) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(
|
||||
_groupingPrefKey,
|
||||
mode == SubscriptionGroupingMode.paymentCard
|
||||
? 'paymentCard'
|
||||
: 'category');
|
||||
}
|
||||
|
||||
void _updateGroupingMode(SubscriptionGroupingMode mode) {
|
||||
if (_groupingMode == mode) return;
|
||||
setState(() {
|
||||
_groupingMode = mode;
|
||||
});
|
||||
_saveGroupingPreference(mode);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final subscriptionProvider = context.watch<SubscriptionProvider>();
|
||||
final categoryProvider = context.watch<CategoryProvider>();
|
||||
final paymentCardProvider = context.watch<PaymentCardProvider>();
|
||||
|
||||
if (subscriptionProvider.isLoading) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
@@ -44,32 +89,30 @@ class HomeContent extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
if (provider.subscriptions.isEmpty) {
|
||||
if (subscriptionProvider.subscriptions.isEmpty) {
|
||||
return EmptyStateWidget(
|
||||
fadeController: fadeController,
|
||||
rotateController: rotateController,
|
||||
slideController: slideController,
|
||||
onAddPressed: onAddPressed,
|
||||
fadeController: widget.fadeController,
|
||||
rotateController: widget.rotateController,
|
||||
slideController: widget.slideController,
|
||||
onAddPressed: widget.onAddPressed,
|
||||
);
|
||||
}
|
||||
|
||||
// 카테고리별 구독 구분
|
||||
final categoryProvider =
|
||||
Provider.of<CategoryProvider>(context, listen: false);
|
||||
final categorizedSubscriptions =
|
||||
SubscriptionCategoryHelper.categorizeSubscriptions(
|
||||
provider.subscriptions,
|
||||
categoryProvider,
|
||||
context,
|
||||
final groupedSubscriptions = SubscriptionGroupingHelper.buildGroups(
|
||||
context: context,
|
||||
subscriptions: subscriptionProvider.subscriptions,
|
||||
mode: _groupingMode,
|
||||
categoryProvider: categoryProvider,
|
||||
paymentCardProvider: paymentCardProvider,
|
||||
);
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await provider.refreshSubscriptions();
|
||||
await subscriptionProvider.refreshSubscriptions();
|
||||
},
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
child: CustomScrollView(
|
||||
controller: scrollController,
|
||||
controller: widget.scrollController,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: [
|
||||
SliverToBoxAdapter(
|
||||
@@ -86,13 +129,13 @@ class HomeContent extends StatelessWidget {
|
||||
begin: const Offset(0, 0.2),
|
||||
end: Offset.zero,
|
||||
).animate(CurvedAnimation(
|
||||
parent: slideController, curve: Curves.easeOutCubic)),
|
||||
parent: widget.slideController, curve: Curves.easeOutCubic)),
|
||||
child: MainScreenSummaryCard(
|
||||
provider: provider,
|
||||
fadeController: fadeController,
|
||||
pulseController: pulseController,
|
||||
waveController: waveController,
|
||||
slideController: slideController,
|
||||
provider: subscriptionProvider,
|
||||
fadeController: widget.fadeController,
|
||||
pulseController: widget.pulseController,
|
||||
waveController: widget.waveController,
|
||||
slideController: widget.slideController,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -107,7 +150,8 @@ class HomeContent extends StatelessWidget {
|
||||
begin: const Offset(-0.2, 0),
|
||||
end: Offset.zero,
|
||||
).animate(CurvedAnimation(
|
||||
parent: slideController, curve: Curves.easeOutCubic)),
|
||||
parent: widget.slideController,
|
||||
curve: Curves.easeOutCubic)),
|
||||
child: Text(
|
||||
AppLocalizations.of(context).mySubscriptions,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
@@ -120,12 +164,13 @@ class HomeContent extends StatelessWidget {
|
||||
begin: const Offset(0.2, 0),
|
||||
end: Offset.zero,
|
||||
).animate(CurvedAnimation(
|
||||
parent: slideController, curve: Curves.easeOutCubic)),
|
||||
parent: widget.slideController,
|
||||
curve: Curves.easeOutCubic)),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
AppLocalizations.of(context)
|
||||
.subscriptionCount(provider.subscriptions.length),
|
||||
AppLocalizations.of(context).subscriptionCount(
|
||||
subscriptionProvider.subscriptions.length),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -145,9 +190,33 @@ class HomeContent extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
children: [
|
||||
ChoiceChip(
|
||||
label: Text(AppLocalizations.of(context).category),
|
||||
selected:
|
||||
_groupingMode == SubscriptionGroupingMode.category,
|
||||
onSelected: (_) =>
|
||||
_updateGroupingMode(SubscriptionGroupingMode.category),
|
||||
),
|
||||
ChoiceChip(
|
||||
label: Text(AppLocalizations.of(context).paymentCard),
|
||||
selected:
|
||||
_groupingMode == SubscriptionGroupingMode.paymentCard,
|
||||
onSelected: (_) => _updateGroupingMode(
|
||||
SubscriptionGroupingMode.paymentCard),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SubscriptionListWidget(
|
||||
categorizedSubscriptions: categorizedSubscriptions,
|
||||
fadeController: fadeController,
|
||||
groups: groupedSubscriptions,
|
||||
fadeController: widget.fadeController,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
|
||||
Reference in New Issue
Block a user