feat(settings): SMS 읽기 권한 상태/요청 위젯 추가 (Android)
- 설정 화면에 SMS 권한 카드 추가: 상태 표시(허용/미허용/영구 거부), 권한 요청/설정 이동 지원\n- 기존 알림 권한 카드 스타일과 일관성 유지 feat(permissions): 최초 실행 시 SMS 권한 온보딩 화면 추가 및 Splash에서 라우팅 (Android) - 권한 필요 이유/수집 범위 현지화 문구 추가\n- 거부/영구거부 케이스 처리 및 설정 이동 chore(codex): AGENTS.md/체크 스크립트/CI/프롬프트 템플릿 추가 - AGENTS.md, scripts/check.sh, scripts/fix.sh, .github/workflows/flutter_ci.yml, .claude/agents/codex.md, 문서 템플릿 추가 refactor(logging): 경로별 print 제거 후 경량 로거(Log) 도입 - SMS 스캐너/컨트롤러, URL 매처, 데이터 리포지토리, 내비게이션, 메모리/성능 유틸 등 핵심 경로 치환 feat(exchange): 환율 API URL을 --dart-define로 오버라이드 가능 + 폴백 로깅 강화 test: URL 매처/환율 스모크 테스트 추가 chore(android): RECEIVE_SMS 권한 제거 (READ_SMS만 유지) fix(lints): dart fix + 수동 정리로 경고 대폭 감소, 비동기 context(mounted) 보강 fix(deprecations):\n- flutter_local_notifications의 androidAllowWhileIdle → androidScheduleMode 전환\n- WillPopScope → PopScope 교체 i18n: SMS 권한 온보딩/설정 문구 현지화 키 추가
This commit is contained in:
@@ -3,7 +3,6 @@ import '../../controllers/add_subscription_controller.dart';
|
||||
import '../common/form_fields/currency_input_field.dart';
|
||||
import '../common/form_fields/date_picker_field.dart';
|
||||
import '../../theme/app_colors.dart';
|
||||
import '../../l10n/app_localizations.dart';
|
||||
|
||||
/// 구독 추가 화면의 이벤트/할인 섹션
|
||||
class AddSubscriptionEventSection extends StatelessWidget {
|
||||
@@ -47,11 +46,11 @@ class AddSubscriptionEventSection extends StatelessWidget {
|
||||
color: AppColors.glassBorder.withValues(alpha: 0.1),
|
||||
width: 1,
|
||||
),
|
||||
boxShadow: [
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: AppColors.shadowBlack,
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -147,7 +146,7 @@ class AddSubscriptionEventSection extends StatelessWidget {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
const Icon(
|
||||
Icons.info_outline_rounded,
|
||||
color: AppColors.infoColor,
|
||||
size: 20,
|
||||
@@ -175,7 +174,7 @@ class AddSubscriptionEventSection extends StatelessWidget {
|
||||
}
|
||||
return Text(
|
||||
infoText,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.darkNavy,
|
||||
fontWeight: FontWeight.w500,
|
||||
|
||||
@@ -5,7 +5,6 @@ import '../../models/subscription_model.dart';
|
||||
import '../../services/currency_util.dart';
|
||||
import '../../providers/locale_provider.dart';
|
||||
import '../../theme/app_colors.dart';
|
||||
import '../../l10n/app_localizations.dart';
|
||||
|
||||
/// 파이 차트에서 선택된 섹션에 표시되는 배지 위젯
|
||||
class AnalysisBadge extends StatelessWidget {
|
||||
@@ -33,7 +32,7 @@ class AnalysisBadge extends StatelessWidget {
|
||||
color: borderColor,
|
||||
width: 2,
|
||||
),
|
||||
boxShadow: [
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: AppColors.shadowBlack,
|
||||
blurRadius: 10,
|
||||
|
||||
@@ -6,6 +6,7 @@ import '../screens/app_lock_screen.dart';
|
||||
import '../models/subscription_model.dart';
|
||||
import '../providers/navigation_provider.dart';
|
||||
import '../routes/app_routes.dart';
|
||||
import '../utils/logger.dart';
|
||||
import 'animated_page_transitions.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
|
||||
@@ -44,7 +45,7 @@ class AppNavigator {
|
||||
/// 구독 상세 화면으로 네비게이션
|
||||
static Future<void> toDetail(
|
||||
BuildContext context, SubscriptionModel subscription) async {
|
||||
print('AppNavigator.toDetail 호출됨: ${subscription.serviceName}');
|
||||
Log.d('AppNavigator.toDetail 호출됨: ${subscription.serviceName}');
|
||||
HapticFeedback.lightImpact();
|
||||
|
||||
try {
|
||||
@@ -52,9 +53,9 @@ class AppNavigator {
|
||||
AppRoutes.subscriptionDetail,
|
||||
arguments: subscription,
|
||||
);
|
||||
print('DetailScreen 네비게이션 성공');
|
||||
Log.d('DetailScreen 네비게이션 성공');
|
||||
} catch (e) {
|
||||
print('DetailScreen 네비게이션 오류: $e');
|
||||
Log.e('DetailScreen 네비게이션 오류', e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ class _SecondaryButtonState extends State<SecondaryButton> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final effectiveBorderColor = widget.borderColor ?? AppColors.secondaryColor;
|
||||
final effectiveTextColor = widget.textColor ?? AppColors.primaryColor;
|
||||
|
||||
|
||||
@@ -81,8 +81,8 @@ class LoadingDialog {
|
||||
context: context,
|
||||
barrierDismissible: barrierDismissible,
|
||||
barrierColor: barrierColor ?? Colors.black54,
|
||||
builder: (context) => WillPopScope(
|
||||
onWillPop: () async => barrierDismissible,
|
||||
builder: (context) => PopScope(
|
||||
canPop: barrierDismissible,
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
|
||||
@@ -66,7 +66,7 @@ class BaseTextField extends StatelessWidget {
|
||||
if (label != null) ...[
|
||||
Text(
|
||||
label!,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textSecondary,
|
||||
@@ -91,13 +91,13 @@ class BaseTextField extends StatelessWidget {
|
||||
readOnly: readOnly,
|
||||
cursorColor: cursorColor ?? theme.primaryColor,
|
||||
style: style ??
|
||||
TextStyle(
|
||||
const TextStyle(
|
||||
fontSize: 16,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintText: hintText,
|
||||
hintStyle: TextStyle(
|
||||
hintStyle: const TextStyle(
|
||||
color: AppColors.textMuted,
|
||||
),
|
||||
prefixIcon: prefixIcon,
|
||||
|
||||
@@ -48,7 +48,7 @@ class DatePickerField extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.darkNavy,
|
||||
@@ -249,7 +249,7 @@ class _DateRangeItem extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
|
||||
@@ -200,7 +200,7 @@ class AppSnackBar {
|
||||
width: 24,
|
||||
height: 24,
|
||||
margin: const EdgeInsets.only(right: 12),
|
||||
child: CircularProgressIndicator(
|
||||
child: const CircularProgressIndicator(
|
||||
strokeWidth: 2.5,
|
||||
color: AppColors.pureWhite,
|
||||
),
|
||||
|
||||
@@ -44,11 +44,11 @@ class DetailEventSection extends StatelessWidget {
|
||||
color: AppColors.glassBorder.withValues(alpha: 0.1),
|
||||
width: 1,
|
||||
),
|
||||
boxShadow: [
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: AppColors.shadowBlack,
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -118,7 +118,7 @@ class DetailEventSection extends StatelessWidget {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
const Icon(
|
||||
Icons.info_outline_rounded,
|
||||
color: AppColors.infoColor,
|
||||
size: 20,
|
||||
@@ -127,7 +127,7 @@ class DetailEventSection extends StatelessWidget {
|
||||
Expanded(
|
||||
child: Text(
|
||||
AppLocalizations.of(context).eventPriceHint,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.darkNavy,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -253,8 +253,8 @@ class _DiscountBadge extends StatelessWidget {
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
_getLocalizedDiscountAmount(context, currency, discountAmount),
|
||||
style: TextStyle(
|
||||
color: const Color(0xFF15803D),
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF15803D),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
|
||||
@@ -49,11 +49,11 @@ class DetailFormSection extends StatelessWidget {
|
||||
color: AppColors.glassBorder.withValues(alpha: 0.1),
|
||||
width: 1,
|
||||
),
|
||||
boxShadow: [
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: AppColors.shadowBlack,
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../models/subscription_model.dart';
|
||||
import '../../controllers/detail_screen_controller.dart';
|
||||
import '../../providers/locale_provider.dart';
|
||||
|
||||
@@ -41,11 +41,11 @@ class DetailUrlSection extends StatelessWidget {
|
||||
color: AppColors.glassBorder.withValues(alpha: 0.1),
|
||||
width: 1,
|
||||
),
|
||||
boxShadow: [
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: AppColors.shadowBlack,
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -89,7 +89,7 @@ class DetailUrlSection extends StatelessWidget {
|
||||
label: AppLocalizations.of(context).websiteUrl,
|
||||
hintText: AppLocalizations.of(context).urlExample,
|
||||
keyboardType: TextInputType.url,
|
||||
prefixIcon: Icon(
|
||||
prefixIcon: const Icon(
|
||||
Icons.link_rounded,
|
||||
color: AppColors.navyGray,
|
||||
),
|
||||
@@ -114,7 +114,7 @@ class DetailUrlSection extends StatelessWidget {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
const Icon(
|
||||
Icons.info_outline_rounded,
|
||||
color: AppColors.warningColor,
|
||||
size: 20,
|
||||
@@ -122,7 +122,7 @@ class DetailUrlSection extends StatelessWidget {
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
AppLocalizations.of(context).cancelGuide,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.darkNavy,
|
||||
@@ -133,7 +133,7 @@ class DetailUrlSection extends StatelessWidget {
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
AppLocalizations.of(context).cancelServiceGuide,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.darkNavy,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -167,7 +167,7 @@ class DetailUrlSection extends StatelessWidget {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
const Icon(
|
||||
Icons.auto_fix_high_rounded,
|
||||
color: AppColors.infoColor,
|
||||
size: 20,
|
||||
@@ -176,7 +176,7 @@ class DetailUrlSection extends StatelessWidget {
|
||||
Expanded(
|
||||
child: Text(
|
||||
AppLocalizations.of(context).urlAutoMatchInfo,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.darkNavy,
|
||||
fontWeight: FontWeight.w500,
|
||||
|
||||
@@ -111,7 +111,7 @@ class EmptyStateWidget extends StatelessWidget {
|
||||
},
|
||||
child: Text(
|
||||
AppLocalizations.of(context).addSubscription,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: 0.5,
|
||||
|
||||
@@ -163,7 +163,7 @@ class _GlassmorphicScaffoldState extends State<GlassmorphicScaffold>
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: gradientColors
|
||||
.map((color) => color.withOpacity(0.3))
|
||||
.map((color) => color.withValues(alpha: 0.3))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../utils/logger.dart';
|
||||
import 'dart:ui';
|
||||
import '../theme/app_colors.dart';
|
||||
import 'themed_text.dart';
|
||||
@@ -74,12 +75,12 @@ class GlassmorphismCard extends StatelessWidget {
|
||||
),
|
||||
boxShadow: boxShadow ??
|
||||
[
|
||||
BoxShadow(
|
||||
const BoxShadow(
|
||||
color: AppColors
|
||||
.shadowBlack, // color.md 가이드: rgba(0,0,0,0.08)
|
||||
blurRadius: 20,
|
||||
spreadRadius: -5,
|
||||
offset: const Offset(0, 10),
|
||||
offset: Offset(0, 10),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -200,7 +201,7 @@ class _AnimatedGlassmorphismCardState extends State<AnimatedGlassmorphismCard>
|
||||
_handleTapUp(details);
|
||||
// onTap 콜백 실행
|
||||
if (widget.onTap != null) {
|
||||
print('[AnimatedGlassmorphismCard] onTap 콜백 실행');
|
||||
Log.d('[AnimatedGlassmorphismCard] onTap 콜백 실행');
|
||||
widget.onTap!();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -90,7 +90,7 @@ class MainScreenSummaryCard extends StatelessWidget {
|
||||
Text(
|
||||
AppLocalizations.of(context)
|
||||
.monthlyTotalSubscriptionCost,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
color: AppColors
|
||||
.darkNavy, // color.md 가이드: 밝은 배경 위 어두운 텍스트
|
||||
fontSize: 15,
|
||||
@@ -215,7 +215,7 @@ class MainScreenSummaryCard extends StatelessWidget {
|
||||
context,
|
||||
title: AppLocalizations.of(context)
|
||||
.estimatedAnnualCost,
|
||||
value: '${NumberFormat.currency(
|
||||
value: NumberFormat.currency(
|
||||
locale: defaultCurrency == 'KRW'
|
||||
? 'ko_KR'
|
||||
: defaultCurrency == 'JPY'
|
||||
@@ -225,7 +225,7 @@ class MainScreenSummaryCard extends StatelessWidget {
|
||||
: 'en_US',
|
||||
symbol: currencySymbol,
|
||||
decimalDigits: decimals,
|
||||
).format(yearlyCost)}',
|
||||
).format(yearlyCost),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
_buildInfoBox(
|
||||
@@ -282,7 +282,7 @@ class MainScreenSummaryCard extends StatelessWidget {
|
||||
Text(
|
||||
AppLocalizations.of(context)
|
||||
.eventDiscountActive,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
color: AppColors
|
||||
.darkNavy, // color.md 가이드: 밝은 배경 위 어두운 텍스트
|
||||
fontSize: 11,
|
||||
@@ -373,7 +373,7 @@ class MainScreenSummaryCard extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
color: AppColors.navyGray, // color.md 가이드: 밝은 배경 위 서브 텍스트
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
|
||||
@@ -14,7 +14,7 @@ class ScanLoadingWidget extends StatelessWidget {
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
CircularProgressIndicator(
|
||||
const CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(AppColors.primaryColor),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../models/subscription_model.dart';
|
||||
import '../providers/category_provider.dart';
|
||||
@@ -202,8 +201,9 @@ class _SubscriptionCardState extends State<SubscriptionCard>
|
||||
daysUntilNext = 7; // 다음 주 같은 요일
|
||||
}
|
||||
|
||||
if (daysUntilNext == 0)
|
||||
if (daysUntilNext == 0) {
|
||||
return AppLocalizations.of(context).paymentDueToday;
|
||||
}
|
||||
return AppLocalizations.of(context).paymentDueInDays(daysUntilNext);
|
||||
}
|
||||
|
||||
@@ -303,8 +303,7 @@ class _SubscriptionCardState extends State<SubscriptionCard>
|
||||
width: double.infinity, // 전체 너비를 차지하도록 설정
|
||||
onTap: widget.onTap ??
|
||||
() async {
|
||||
print(
|
||||
'[SubscriptionCard] AnimatedGlassmorphismCard onTap 호출됨 - ${widget.subscription.serviceName}');
|
||||
// ignore: use_build_context_synchronously
|
||||
await AppNavigator.toDetail(context, widget.subscription);
|
||||
},
|
||||
child: Column(
|
||||
|
||||
@@ -12,6 +12,7 @@ import '../services/subscription_url_matcher.dart';
|
||||
import './dialogs/delete_confirmation_dialog.dart';
|
||||
import './common/snackbar/app_snackbar.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
import '../utils/logger.dart';
|
||||
|
||||
/// 카테고리별로 구독 목록을 표시하는 위젯
|
||||
class SubscriptionListWidget extends StatelessWidget {
|
||||
@@ -100,7 +101,7 @@ class SubscriptionListWidget extends StatelessWidget {
|
||||
child: SwipeableSubscriptionCard(
|
||||
subscription: subscriptions[subIndex],
|
||||
onTap: () {
|
||||
print(
|
||||
Log.d(
|
||||
'[SubscriptionListWidget] SwipeableSubscriptionCard onTap 호출됨');
|
||||
AppNavigator.toDetail(
|
||||
context, subscriptions[subIndex]);
|
||||
@@ -122,13 +123,15 @@ class SubscriptionListWidget extends StatelessWidget {
|
||||
);
|
||||
|
||||
// 삭제 확인 다이얼로그 표시
|
||||
if (!context.mounted) return;
|
||||
final shouldDelete =
|
||||
await DeleteConfirmationDialog.show(
|
||||
context: context,
|
||||
serviceName: displayName,
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
|
||||
if (shouldDelete && context.mounted) {
|
||||
if (shouldDelete) {
|
||||
// 사용자가 확인한 경우에만 삭제 진행
|
||||
final provider =
|
||||
Provider.of<SubscriptionProvider>(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import '../models/subscription_model.dart';
|
||||
import '../utils/haptic_feedback_helper.dart';
|
||||
import 'subscription_card.dart';
|
||||
@@ -29,7 +28,6 @@ class _SwipeableSubscriptionCardState extends State<SwipeableSubscriptionCard>
|
||||
static const double _tapTolerance = 20.0; // 탭 허용 범위
|
||||
static const double _actionThresholdPercent = 0.15;
|
||||
static const double _deleteThresholdPercent = 0.40;
|
||||
static const int _tapDurationMs = 500;
|
||||
static const double _velocityThreshold = 800.0;
|
||||
// static const double _animationDuration = 300.0;
|
||||
|
||||
@@ -39,8 +37,7 @@ class _SwipeableSubscriptionCardState extends State<SwipeableSubscriptionCard>
|
||||
|
||||
// 제스처 추적
|
||||
Offset? _startPosition;
|
||||
DateTime? _startTime;
|
||||
bool _isValidTap = true;
|
||||
// 제스처 관련 보조 변수(간소화)
|
||||
|
||||
// 상태 관리
|
||||
double _currentOffset = 0;
|
||||
@@ -95,8 +92,6 @@ class _SwipeableSubscriptionCardState extends State<SwipeableSubscriptionCard>
|
||||
// 제스처 핸들러
|
||||
void _handlePanStart(DragStartDetails details) {
|
||||
_startPosition = details.localPosition;
|
||||
_startTime = DateTime.now();
|
||||
_isValidTap = true;
|
||||
_hapticTriggered = false;
|
||||
_controller.stop();
|
||||
}
|
||||
@@ -104,12 +99,7 @@ class _SwipeableSubscriptionCardState extends State<SwipeableSubscriptionCard>
|
||||
void _handlePanUpdate(DragUpdateDetails details) {
|
||||
final currentPosition = details.localPosition;
|
||||
final delta = currentPosition.dx - _startPosition!.dx;
|
||||
final distance = (currentPosition - _startPosition!).distance;
|
||||
|
||||
// 탭 유효성 검사 - 거리가 허용 범위를 벗어나면 스와이프로 간주
|
||||
if (distance > _tapTolerance) {
|
||||
_isValidTap = false;
|
||||
}
|
||||
// 탭/스와이프 판별 거리는 외부에서 사용하지 않아 제거
|
||||
|
||||
// 카드 이동
|
||||
setState(() {
|
||||
@@ -129,14 +119,7 @@ class _SwipeableSubscriptionCardState extends State<SwipeableSubscriptionCard>
|
||||
}
|
||||
|
||||
// 헬퍼 메서드
|
||||
void _processTap() {
|
||||
print('[SwipeableSubscriptionCard] _processTap 호출됨');
|
||||
if (widget.onTap != null) {
|
||||
print('[SwipeableSubscriptionCard] onTap 콜백 실행');
|
||||
widget.onTap!();
|
||||
}
|
||||
_animateToOffset(0);
|
||||
}
|
||||
// 탭 처리는 SubscriptionCard에서 수행
|
||||
|
||||
void _processSwipe(double velocity) {
|
||||
final extent = _currentOffset.abs();
|
||||
|
||||
Reference in New Issue
Block a user