This commit is contained in:
2026-06-29 16:04:15 +03:00
parent 58bfc6b12c
commit 4f56e4983c
13 changed files with 869 additions and 7 deletions
+5
View File
@@ -11,6 +11,7 @@ import '../features/onboarding/screen.dart';
import '../shared/models/transaction.dart';
import '../shared/paywall/paywall_screen.dart';
import '../shared/services/onboarding_service.dart';
import '../shared/widgets/pro_screen.dart';
final _shellKey = GlobalKey<NavigatorState>();
@@ -67,6 +68,10 @@ final appRouter = GoRouter(
path: '/paywall',
builder: (context, state) => const PaywallScreen(),
),
GoRoute(
path: '/pro',
builder: (context, state) => const ProScreen(),
),
],
);
+4 -2
View File
@@ -132,10 +132,12 @@ class AppTheme {
static ThemeData get lightTheme {
final base = ThemeData.light(useMaterial3: true);
final textTheme = GoogleFonts.poppinsTextTheme(base.textTheme).apply(
final textTheme = _withCyrillicFallback(
base.textTheme.apply(
fontFamily: 'Poppins',
bodyColor: const Color(0xFF1A1A2E),
displayColor: const Color(0xFF1A1A2E),
fontFamilyFallback: ['Roboto'],
),
);
return base.copyWith(
+54
View File
@@ -278,4 +278,58 @@ class AppStrings {
? 'Если готовы — свайпните вправо, чтобы открыть приложение'
: "If you're ready — swipe right to open the app";
String get onboardingSwipeRight => _ru ? 'Свайп вправо' : 'Swipe Right';
String get proTitle => _ru ? 'Casha Pro' : 'Casha Pro';
String get proAboutPro => _ru ? 'Подробнее' : 'About Pro';
String get proSubtitle => _ru
? 'Раскройте весь потенциал Casha'
: 'Unlock the full power of Casha';
String get proBuy => _ru ? 'Купить Pro' : 'Buy Pro';
String get proFeatureCloudSync =>
_ru ? 'Облачная синхронизация' : 'Cloud Sync';
String get proFeatureCloudSyncDesc => _ru
? 'Синхронизация между всеми вашими устройствами'
: 'Sync across all your devices';
String get proFeatureBiometric =>
_ru ? 'Биометрическая защита' : 'Biometric Protection';
String get proFeatureBiometricDesc => _ru
? 'Защитите свои данные отпечатком пальца или Face ID'
: 'Secure your data with fingerprint or Face ID';
String get proFeatureAnalytics =>
_ru ? 'Детальная аналитика' : 'Detailed Analytics';
String get proFeatureAnalyticsDesc => _ru
? 'Графики и статистика по расходам и доходам'
: 'Charts and stats for income and spending';
String get proFeatureCustomization =>
_ru ? 'Кастомизация карточек' : 'Card Customization';
String get proFeatureCustomizationDesc => _ru
? 'Цвета, градиенты, высота — всё под вашим контролем'
: 'Colors, gradients, height — all under your control';
String get proFeatureAccounts =>
_ru ? 'До 8 счетов' : 'Up to 8 Accounts';
String get proFeatureAccountsDesc => _ru
? 'Создавайте больше счетов для разных целей'
: 'Create more accounts for different goals';
String get proTryPro => _ru ? 'Попробовать Pro' : 'Try Pro';
String get proRestorePurchases =>
_ru ? 'Восстановить покупки' : 'Restore Purchases';
String get proActive => _ru ? 'Pro активна' : 'Pro Active';
String get proSignInForSync =>
_ru ? 'Войдите, чтобы включить синхронизацию' : 'Sign in to enable sync';
String get proSignInGoogle =>
_ru ? 'Войти через Google' : 'Sign in with Google';
String get proSyncEnabled =>
_ru ? 'Синхронизация включена' : 'Sync enabled';
String get proLastBackup => _ru ? 'Последняя резервная копия' : 'Last backup';
String get proSignOut => _ru ? 'Выйти' : 'Sign Out';
String get proPurchaseSuccess =>
_ru ? 'Pro успешно активирована!' : 'Pro successfully activated!';
String get proPurchaseFailed =>
_ru ? 'Не удалось оформить подписку' : 'Purchase failed';
String get proRestoreSuccess =>
_ru ? 'Покупки восстановлены!' : 'Purchases restored!';
String get proRestoreNotFound =>
_ru ? 'Предыдущие покупки не найдены' : 'No previous purchases found';
String get proTapToClose =>
_ru ? 'Нажмите чтобы закрыть' : 'Tap anywhere to close';
}
+2 -3
View File
@@ -5,7 +5,6 @@ import '../../core/l10n/locale_provider.dart';
import '../../core/services/biometric_service.dart';
import '../../core/services/haptic_service.dart';
import '../dashboard/provider.dart';
import 'provider.dart';
import 'widgets/theme_section.dart';
import 'widgets/card_text_color_section.dart';
import 'widgets/haptic_section.dart';
@@ -14,7 +13,7 @@ import 'widgets/language_section.dart';
import 'widgets/currency_section.dart';
import 'widgets/amount_format_section.dart';
import 'widgets/categories_section.dart';
import 'widgets/premium_section.dart';
import '../../shared/widgets/pro_subscription_card.dart';
class SettingsScreen extends ConsumerWidget {
const SettingsScreen({super.key});
@@ -117,7 +116,7 @@ class SettingsScreen extends ConsumerWidget {
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.fromLTRB(20, 16, 20, 40),
children: [
const PremiumSection(),
const ProSubscriptionCard(),
const SizedBox(height: 16),
const ThemeSection(),
const SizedBox(height: 16),
@@ -0,0 +1,8 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../features/dashboard/provider.dart';
import '../services/billing_service.dart';
final billingServiceProvider = Provider<BillingService>((ref) {
final prefs = ref.watch(sharedPreferencesProvider);
return MockBillingService(prefs);
});
@@ -0,0 +1,12 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_sign_in/google_sign_in.dart';
import '../services/google_auth_service.dart';
final googleAuthProvider = Provider<GoogleAuthService>((ref) {
return GoogleAuthService();
});
final googleCurrentUserProvider = StreamProvider<GoogleSignInAccount?>((ref) {
final service = ref.watch(googleAuthProvider);
return service.onCurrentUserChanged;
});
+34
View File
@@ -0,0 +1,34 @@
import 'package:shared_preferences/shared_preferences.dart';
import '../models/user_model.dart';
abstract class BillingService {
Future<bool> purchasePro();
Future<bool> restorePurchases();
Future<UserPlan> getCurrentPlan();
}
class MockBillingService implements BillingService {
static const _key = 'user_plan';
final SharedPreferences _prefs;
MockBillingService(this._prefs);
@override
Future<bool> purchasePro() async {
await Future.delayed(const Duration(seconds: 1));
await _prefs.setString(_key, 'vip');
return true;
}
@override
Future<bool> restorePurchases() async {
await Future.delayed(const Duration(seconds: 1));
return false;
}
@override
Future<UserPlan> getCurrentPlan() async {
final value = _prefs.getString(_key);
return value == 'vip' ? UserPlan.vip : UserPlan.free;
}
}
@@ -0,0 +1,21 @@
import 'dart:async';
import 'package:google_sign_in/google_sign_in.dart';
class GoogleAuthService {
final GoogleSignIn _googleSignIn;
GoogleAuthService() : _googleSignIn = GoogleSignIn(scopes: ['email']);
GoogleSignInAccount? get currentUser => _googleSignIn.currentUser;
Stream<GoogleSignInAccount?> get onCurrentUserChanged =>
_googleSignIn.onCurrentUserChanged;
Future<void> signIn() async {
await _googleSignIn.signIn();
}
Future<void> signOut() async {
await _googleSignIn.signOut();
}
}
+550
View File
@@ -0,0 +1,550 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../core/l10n/app_strings.dart';
import '../../core/l10n/locale_provider.dart';
import '../../core/services/haptic_service.dart';
import '../feature_flags/feature_flags_provider.dart';
import '../models/user_model.dart';
import '../providers/billing_provider.dart';
import '../providers/current_user_provider.dart';
import '../providers/google_auth_provider.dart';
import 'error_snackbar.dart';
class ProScreen extends ConsumerStatefulWidget {
const ProScreen({super.key});
@override
ConsumerState<ProScreen> createState() => _ProScreenState();
}
class _ProScreenState extends ConsumerState<ProScreen>
with SingleTickerProviderStateMixin {
bool _purchasing = false;
bool _restoring = false;
bool _showSuccess = false;
late final AnimationController _successController;
late final Animation<double> _successScale;
static const _gradientColors = [
Color(0xFF1A237E),
Color(0xFF6A1B9A),
Color(0xFFE65100),
];
@override
void initState() {
super.initState();
_successController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 400),
);
_successScale = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _successController,
curve: Curves.elasticOut,
),
);
}
@override
void dispose() {
_successController.dispose();
super.dispose();
}
void _showSuccessOverlay() {
setState(() => _showSuccess = true);
_successController.forward(from: 0.0);
}
void _dismissSuccessOverlay() {
_successController.stop();
if (mounted) {
setState(() => _showSuccess = false);
}
}
@override
Widget build(BuildContext context) {
final s = ref.watch(stringsProvider);
final flags = ref.watch(featureFlagsProvider);
final isVip = flags.maxAccounts != 3;
final googleUserAsync = ref.watch(googleCurrentUserProvider);
final googleUser = googleUserAsync.value;
final colorScheme = Theme.of(context).colorScheme;
return Stack(
children: [
Scaffold(
body: SafeArea(
child: Column(
children: [
_buildHeader(context, s, colorScheme),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 4),
_buildHeroBanner(context, s, colorScheme, isVip),
const SizedBox(height: 20),
_buildFeatureList(context, s, colorScheme),
const SizedBox(height: 16),
if (isVip) ...[
_buildProActiveSection(context, s, colorScheme, googleUser),
],
],
),
),
),
_buildBottomBar(context, s, colorScheme, isVip),
],
),
),
),
if (_showSuccess) _buildSuccessOverlay(context, s, colorScheme),
],
);
}
Widget _buildHeader(
BuildContext context,
AppStrings s,
ColorScheme colorScheme,
) {
return Padding(
padding: const EdgeInsets.fromLTRB(8, 4, 16, 0),
child: Row(
children: [
IconButton(
icon: const Icon(Icons.arrow_back_rounded),
onPressed: () => context.pop(),
),
],
),
);
}
Widget _buildHeroBanner(
BuildContext context,
AppStrings s,
ColorScheme colorScheme,
bool isVip,
) {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 20),
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: _gradientColors,
),
borderRadius: BorderRadius.circular(20),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
s.proTitle,
style: Theme.of(context).textTheme.displaySmall?.copyWith(
fontWeight: FontWeight.w900,
color: Colors.white,
letterSpacing: -0.5,
),
),
const SizedBox(height: 6),
Text(
isVip ? s.proActive : s.proSubtitle,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: Colors.white.withOpacity(0.9),
fontWeight: isVip ? FontWeight.w700 : FontWeight.w500,
),
),
],
),
);
}
Widget _buildSuccessOverlay(
BuildContext context,
AppStrings s,
ColorScheme colorScheme,
) {
return GestureDetector(
onTap: _dismissSuccessOverlay,
child: Container(
color: Colors.black54,
child: Center(
child: GestureDetector(
onTap: _dismissSuccessOverlay,
child: AnimatedBuilder(
animation: _successController,
builder: (context, child) {
return Transform.scale(
scale: _successScale.value,
child: child,
);
},
child: Container(
padding: const EdgeInsets.all(32),
margin: const EdgeInsets.symmetric(horizontal: 40),
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(24),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: colorScheme.primary.withOpacity(0.15),
shape: BoxShape.circle,
),
child: Icon(
Icons.check_circle_rounded,
color: colorScheme.primary,
size: 64,
),
),
const SizedBox(height: 20),
Text(
s.proPurchaseSuccess,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w800,
color: colorScheme.onSurface,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 12),
Text(
s.proTapToClose,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurface.withOpacity(0.5),
),
textAlign: TextAlign.center,
),
],
),
),
),
),
),
),
);
}
Widget _buildFeatureList(
BuildContext context,
AppStrings s,
ColorScheme colorScheme,
) {
final features = [
(Icons.cloud_sync_rounded, s.proFeatureCloudSync, s.proFeatureCloudSyncDesc),
(Icons.analytics_rounded, s.proFeatureAnalytics, s.proFeatureAnalyticsDesc),
(Icons.palette_rounded, s.proFeatureCustomization, s.proFeatureCustomizationDesc),
(Icons.fingerprint_rounded, s.proFeatureBiometric, s.proFeatureBiometricDesc),
(Icons.account_balance_wallet_rounded, s.proFeatureAccounts, s.proFeatureAccountsDesc),
];
return Column(
children: features.map((f) {
return Padding(
padding: const EdgeInsets.only(bottom: 14),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(
f.$1,
color: colorScheme.primary,
size: 22,
),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
f.$2,
style: Theme.of(context).textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w700,
color: colorScheme.onSurface,
),
),
const SizedBox(height: 4),
Text(
f.$3,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurface.withOpacity(0.6),
),
),
],
),
),
],
),
);
}).toList(),
);
}
Widget _buildProActiveSection(
BuildContext context,
AppStrings s,
ColorScheme colorScheme,
dynamic googleUser,
) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: colorScheme.primary.withOpacity(0.2),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (googleUser != null) ...[
Row(
children: [
Icon(Icons.email_outlined, size: 18, color: colorScheme.onSurface.withOpacity(0.5)),
const SizedBox(width: 8),
Expanded(
child: Text(
googleUser.email,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurface,
),
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Icon(Icons.sync_rounded, size: 18, color: colorScheme.primary),
const SizedBox(width: 8),
Expanded(
child: Text(
s.proSyncEnabled,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurface,
),
),
),
Icon(Icons.check_circle_rounded, color: colorScheme.primary, size: 20),
],
),
const SizedBox(height: 8),
Row(
children: [
Icon(Icons.backup_outlined, size: 18, color: colorScheme.onSurface.withOpacity(0.5)),
const SizedBox(width: 8),
Text(
'${s.proLastBackup}: —',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurface.withOpacity(0.5),
),
),
],
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: _handleGoogleSignOut,
icon: Icon(Icons.logout_rounded, color: colorScheme.error),
label: Text(s.proSignOut),
style: OutlinedButton.styleFrom(
foregroundColor: colorScheme.error,
),
),
),
],
),
] else ...[
Text(
s.proSignInForSync,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurface.withOpacity(0.7),
),
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
onPressed: _handleGoogleSignIn,
icon: Icon(Icons.login_rounded, color: colorScheme.primary),
label: Text(s.proSignInGoogle),
),
),
],
],
),
);
}
Widget _buildBottomBar(
BuildContext context,
AppStrings s,
ColorScheme colorScheme,
bool isVip,
) {
if (isVip) {
return const SizedBox.shrink();
}
return Container(
padding: const EdgeInsets.fromLTRB(20, 12, 20, 24),
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
border: Border(
top: BorderSide(
color: colorScheme.outlineVariant.withOpacity(0.3),
width: 1,
),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: double.infinity,
child: FilledButton(
onPressed: _purchasing ? null : _handlePurchase,
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: _purchasing
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: colorScheme.onPrimary,
),
)
: Text(
s.proBuy,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
),
),
),
),
const SizedBox(height: 8),
SizedBox(
width: double.infinity,
child: TextButton(
onPressed: _restoring ? null : _handleRestore,
child: _restoring
? SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
color: colorScheme.primary,
),
)
: Text(s.proRestorePurchases),
),
),
],
),
);
}
Future<void> _handlePurchase() async {
HapticService.light();
setState(() => _purchasing = true);
try {
final billing = ref.read(billingServiceProvider);
final success = await billing.purchasePro();
if (success) {
await ref.read(currentUserProvider.notifier).setPlan(UserPlan.vip);
if (mounted) {
_showSuccessOverlay();
HapticService.medium();
}
} else {
if (mounted) {
showErrorSnackbar(context, ref.read(stringsProvider).proPurchaseFailed);
}
}
} catch (e) {
if (mounted) {
showErrorSnackbar(context, e.toString());
}
} finally {
if (mounted) setState(() => _purchasing = false);
}
}
Future<void> _handleRestore() async {
HapticService.light();
setState(() => _restoring = true);
try {
final billing = ref.read(billingServiceProvider);
final success = await billing.restorePurchases();
if (success) {
await ref.read(currentUserProvider.notifier).setPlan(UserPlan.vip);
if (mounted) {
_showSuccessOverlay();
HapticService.medium();
}
} else {
if (mounted) {
showWarningSnackbar(context, ref.read(stringsProvider).proRestoreNotFound);
}
}
} catch (e) {
if (mounted) {
showErrorSnackbar(context, e.toString());
}
} finally {
if (mounted) setState(() => _restoring = false);
}
}
Future<void> _handleGoogleSignIn() async {
HapticService.light();
try {
final service = ref.read(googleAuthProvider);
await service.signIn();
} catch (e) {
if (mounted) {
showErrorSnackbar(context, e.toString());
}
}
}
Future<void> _handleGoogleSignOut() async {
HapticService.light();
try {
final service = ref.read(googleAuthProvider);
await service.signOut();
} catch (e) {
if (mounted) {
showErrorSnackbar(context, e.toString());
}
}
}
}
@@ -0,0 +1,126 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../core/l10n/locale_provider.dart';
import '../../core/services/haptic_service.dart';
import '../feature_flags/feature_flags_provider.dart';
import '../providers/google_auth_provider.dart';
class ProSubscriptionCard extends ConsumerWidget {
const ProSubscriptionCard({super.key});
static const _gradientColors = [
Color(0xFF1A237E),
Color(0xFF6A1B9A),
Color(0xFFE65100),
];
@override
Widget build(BuildContext context, WidgetRef ref) {
final s = ref.watch(stringsProvider);
final flags = ref.watch(featureFlagsProvider);
final isVip = flags.maxAccounts != 3;
final googleUserAsync = ref.watch(googleCurrentUserProvider);
final googleUser = googleUserAsync.value;
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: _gradientColors,
),
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(12),
),
child: Icon(
isVip ? Icons.verified_rounded : Icons.workspace_premium_rounded,
color: Colors.white,
size: 24,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
s.proTitle,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w900,
color: Colors.white,
),
),
if (isVip) ...[
const SizedBox(height: 2),
Text(
googleUser != null
? '${s.proActive} \u2705'
: s.proActive,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.white.withOpacity(0.9),
fontWeight: FontWeight.w600,
),
),
],
],
),
),
OutlinedButton(
onPressed: () {
HapticService.light();
context.push('/pro');
},
style: OutlinedButton.styleFrom(
foregroundColor: Colors.white,
side: BorderSide(color: Colors.white.withOpacity(0.4), width: 1.5),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
child: Text(
s.proAboutPro,
style: const TextStyle(fontWeight: FontWeight.w700),
),
),
],
),
if (isVip && googleUser == null) ...[
const SizedBox(height: 14),
SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
onPressed: () {
HapticService.light();
ref.read(googleAuthProvider).signIn();
},
icon: const Icon(Icons.login_rounded, color: Colors.white, size: 18),
label: Text(s.proSignInGoogle),
style: OutlinedButton.styleFrom(
foregroundColor: Colors.white,
side: BorderSide(color: Colors.white.withOpacity(0.3), width: 1),
padding: const EdgeInsets.symmetric(vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
],
],
),
);
}
}
@@ -5,10 +5,12 @@
import FlutterMacOS
import Foundation
import google_sign_in_ios
import local_auth_darwin
import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
}
+48
View File
@@ -376,6 +376,54 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.1.0"
google_identity_services_web:
dependency: transitive
description:
name: google_identity_services_web
sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454"
url: "https://pub.dev"
source: hosted
version: "0.3.3+1"
google_sign_in:
dependency: "direct main"
description:
name: google_sign_in
sha256: d0a2c3bcb06e607bb11e4daca48bd4b6120f0bbc4015ccebbe757d24ea60ed2a
url: "https://pub.dev"
source: hosted
version: "6.3.0"
google_sign_in_android:
dependency: transitive
description:
name: google_sign_in_android
sha256: d5e23c56a4b84b6427552f1cf3f98f716db3b1d1a647f16b96dbb5b93afa2805
url: "https://pub.dev"
source: hosted
version: "6.2.1"
google_sign_in_ios:
dependency: transitive
description:
name: google_sign_in_ios
sha256: "102005f498ce18442e7158f6791033bbc15ad2dcc0afa4cf4752e2722a516c96"
url: "https://pub.dev"
source: hosted
version: "5.9.0"
google_sign_in_platform_interface:
dependency: transitive
description:
name: google_sign_in_platform_interface
sha256: "5f6f79cf139c197261adb6ac024577518ae48fdff8e53205c5373b5f6430a8aa"
url: "https://pub.dev"
source: hosted
version: "2.5.0"
google_sign_in_web:
dependency: transitive
description:
name: google_sign_in_web
sha256: "460547beb4962b7623ac0fb8122d6b8268c951cf0b646dd150d60498430e4ded"
url: "https://pub.dev"
source: hosted
version: "0.12.4+4"
graphs:
dependency: transitive
description:
+1
View File
@@ -25,6 +25,7 @@ dependencies:
drift: ^2.14.1
sqlite3_flutter_libs: ^0.6.0+eol
path: ^1.8.3
google_sign_in: ^6.2.1
flutter_launcher_icons:
android: true