feat: login page with sucessful login authentication into secure shared preferences
This commit is contained in:
parent
930e6e5284
commit
622db644fa
9
frontend/lib/constants/api_constants.dart
Normal file
9
frontend/lib/constants/api_constants.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
class ApiConstants {
|
||||||
|
static const String baseUrl = 'http://localhost:8080';
|
||||||
|
|
||||||
|
// Auth endpoints
|
||||||
|
static const String loginEndpoint = '/login';
|
||||||
|
|
||||||
|
// User endpoints
|
||||||
|
static const String getUserEndpoint = '/getUser';
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ import 'package:firebase_core/firebase_core.dart';
|
|||||||
import 'firebase_options.dart';
|
import 'firebase_options.dart';
|
||||||
import 'screens/home_screen.dart';
|
import 'screens/home_screen.dart';
|
||||||
import 'services/notification_service.dart';
|
import 'services/notification_service.dart';
|
||||||
|
import 'services/auth_service.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
@ -28,63 +29,16 @@ class LandingPage extends StatefulWidget {
|
|||||||
_LandingPageState createState() => _LandingPageState();
|
_LandingPageState createState() => _LandingPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LandingPageState extends State<LandingPage>
|
class _LandingPageState extends State<LandingPage> {
|
||||||
with SingleTickerProviderStateMixin {
|
void _navigateDirectlyToLogin() {
|
||||||
late AnimationController _animationController;
|
|
||||||
late Animation<double> _slideAnimation;
|
|
||||||
bool _showBottomSheet = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_animationController = AnimationController(
|
|
||||||
duration: Duration(milliseconds: 800),
|
|
||||||
vsync: this,
|
|
||||||
);
|
|
||||||
_slideAnimation = Tween<double>(begin: 1.0, end: 0.0).animate(
|
|
||||||
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_animationController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showGetStartedBottomSheet() {
|
|
||||||
setState(() {
|
|
||||||
_showBottomSheet = true;
|
|
||||||
});
|
|
||||||
_animationController.forward();
|
|
||||||
|
|
||||||
// Request notification permissions on user action
|
// Request notification permissions on user action
|
||||||
NotificationService().requestPermissionAndSetup();
|
NotificationService().requestPermissionAndSetup();
|
||||||
}
|
|
||||||
|
|
||||||
void _hideBottomSheet() {
|
|
||||||
_animationController.reverse().then((_) {
|
|
||||||
setState(() {
|
|
||||||
_showBottomSheet = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _navigateToSignIn() {
|
|
||||||
Navigator.of(
|
Navigator.of(
|
||||||
context,
|
context,
|
||||||
).push(MaterialPageRoute(builder: (context) => SignInPage()));
|
).push(MaterialPageRoute(builder: (context) => SignInPage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _navigateToHome() {
|
|
||||||
// Request notification permissions on user action
|
|
||||||
NotificationService().requestPermissionAndSetup();
|
|
||||||
|
|
||||||
Navigator.of(
|
|
||||||
context,
|
|
||||||
).push(MaterialPageRoute(builder: (context) => HomeScreen()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -156,7 +110,7 @@ class _LandingPageState extends State<LandingPage>
|
|||||||
width: 280,
|
width: 280,
|
||||||
height: 56,
|
height: 56,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: _showGetStartedBottomSheet,
|
onPressed: _navigateDirectlyToLogin,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: Color(0xFF6A4C93),
|
backgroundColor: Color(0xFF6A4C93),
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
@ -183,25 +137,6 @@ class _LandingPageState extends State<LandingPage>
|
|||||||
),
|
),
|
||||||
|
|
||||||
SizedBox(height: 24),
|
SizedBox(height: 24),
|
||||||
|
|
||||||
// Login link
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
onTap: _navigateToHome,
|
|
||||||
child: Text(
|
|
||||||
'Skip to Home',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
decoration: TextDecoration.underline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -209,158 +144,6 @@ class _LandingPageState extends State<LandingPage>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Bottom sheet overlay
|
|
||||||
if (_showBottomSheet)
|
|
||||||
AnimatedBuilder(
|
|
||||||
animation: _slideAnimation,
|
|
||||||
builder: (context, child) {
|
|
||||||
return Positioned(
|
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
child: Transform.translate(
|
|
||||||
offset: Offset(0, _slideAnimation.value * 400),
|
|
||||||
child: Container(
|
|
||||||
height: 400,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(24),
|
|
||||||
topRight: Radius.circular(24),
|
|
||||||
),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.2),
|
|
||||||
blurRadius: 20,
|
|
||||||
offset: Offset(0, -5),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
// Handle bar
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.only(top: 12),
|
|
||||||
width: 40,
|
|
||||||
height: 4,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.grey[300],
|
|
||||||
borderRadius: BorderRadius.circular(2),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Close button
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.topRight,
|
|
||||||
child: IconButton(
|
|
||||||
onPressed: _hideBottomSheet,
|
|
||||||
icon: Icon(Icons.close, color: Colors.grey[600]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Expanded(
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Welcome In',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 28,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
SizedBox(height: 12),
|
|
||||||
|
|
||||||
Text(
|
|
||||||
'Join your community.. change your social life!',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.grey[600],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
SizedBox(height: 40),
|
|
||||||
|
|
||||||
// Sign Up button
|
|
||||||
Container(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 56,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
// Handle sign up
|
|
||||||
ScaffoldMessenger.of(
|
|
||||||
context,
|
|
||||||
).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text('Sign Up pressed'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Color(0xFF6A4C93),
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(
|
|
||||||
12,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
elevation: 2,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'Sign Up Now',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
SizedBox(height: 16),
|
|
||||||
|
|
||||||
// Login button
|
|
||||||
Container(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 56,
|
|
||||||
child: OutlinedButton(
|
|
||||||
onPressed: _navigateToSignIn,
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
foregroundColor: Color(0xFF6A4C93),
|
|
||||||
side: BorderSide(
|
|
||||||
color: Color(0xFF6A4C93),
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(
|
|
||||||
12,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'Login',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -379,6 +162,72 @@ class _SignInPageState extends State<SignInPage> {
|
|||||||
bool _isPasswordVisible = false;
|
bool _isPasswordVisible = false;
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
|
|
||||||
|
void _showHelpBottomSheet() {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
|
||||||
|
),
|
||||||
|
builder: (context) => Container(
|
||||||
|
padding: EdgeInsets.all(24),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 40,
|
||||||
|
height: 4,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[300],
|
||||||
|
borderRadius: BorderRadius.circular(2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 24),
|
||||||
|
Icon(Icons.contact_support, size: 48, color: Color(0xFF6A4C93)),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Need Help?',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'For account creation or password reset, please contact ERP Management Group from your Aramco email address.',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.grey[700],
|
||||||
|
height: 1.4,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 24),
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Color(0xFF6A4C93),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 16),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Got It',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_emailController.dispose();
|
_emailController.dispose();
|
||||||
@ -392,19 +241,44 @@ class _SignInPageState extends State<SignInPage> {
|
|||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Simulate API call
|
final result = await AuthService.login(
|
||||||
await Future.delayed(Duration(seconds: 2));
|
_emailController.text.trim(),
|
||||||
|
_passwordController.text,
|
||||||
|
);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
ScaffoldMessenger.of(
|
if (result['success'] == true) {
|
||||||
context,
|
Navigator.of(context).pushReplacement(
|
||||||
).showSnackBar(SnackBar(content: Text('Sign in successful!')));
|
MaterialPageRoute(builder: (context) => HomeScreen()),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
_showErrorAlert(result['message'] ?? 'Login failed');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showErrorAlert(String message) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: Text('Login Failed'),
|
||||||
|
content: Text(message),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(
|
||||||
|
'OK',
|
||||||
|
style: TextStyle(color: Color(0xFF6A4C93)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -493,7 +367,7 @@ class _SignInPageState extends State<SignInPage> {
|
|||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
'Sign in to your account',
|
'Sign in to socialize with your colleagues\nand transform your social life!',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: Colors.grey[600],
|
color: Colors.grey[600],
|
||||||
@ -575,13 +449,7 @@ class _SignInPageState extends State<SignInPage> {
|
|||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () {
|
onPressed: _showHelpBottomSheet,
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text('Forgot password pressed'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Text(
|
child: Text(
|
||||||
'Forgot Password?',
|
'Forgot Password?',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -625,7 +493,7 @@ class _SignInPageState extends State<SignInPage> {
|
|||||||
|
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
|
|
||||||
// Sign up link
|
// Contact link for new accounts
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@ -634,13 +502,9 @@ class _SignInPageState extends State<SignInPage> {
|
|||||||
style: TextStyle(color: Colors.grey[600]),
|
style: TextStyle(color: Colors.grey[600]),
|
||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: _showHelpBottomSheet,
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text('Sign up pressed')),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Text(
|
child: Text(
|
||||||
'Sign Up',
|
'Contact Support',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF6A4C93),
|
color: Color(0xFF6A4C93),
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import '../../services/notification_service.dart';
|
import '../../services/notification_service.dart';
|
||||||
|
import '../../services/user_service.dart';
|
||||||
|
|
||||||
class ProfilePage extends StatefulWidget {
|
class ProfilePage extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@ -11,6 +12,8 @@ class _ProfilePageState extends State<ProfilePage> {
|
|||||||
String? fcmToken;
|
String? fcmToken;
|
||||||
final TextEditingController _tokenController = TextEditingController();
|
final TextEditingController _tokenController = TextEditingController();
|
||||||
bool isLoading = false;
|
bool isLoading = false;
|
||||||
|
Map<String, dynamic>? userData;
|
||||||
|
bool isLoadingUser = true;
|
||||||
|
|
||||||
final List<Map<String, dynamic>> mockUserPosts = [
|
final List<Map<String, dynamic>> mockUserPosts = [
|
||||||
{
|
{
|
||||||
@ -46,6 +49,7 @@ class _ProfilePageState extends State<ProfilePage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_loadFCMToken();
|
_loadFCMToken();
|
||||||
|
_loadUserData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -75,6 +79,43 @@ class _ProfilePageState extends State<ProfilePage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _loadUserData() async {
|
||||||
|
setState(() {
|
||||||
|
isLoadingUser = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
final result = await UserService.getCurrentUser();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
isLoadingUser = false;
|
||||||
|
if (result['success'] == true) {
|
||||||
|
userData = result['data'];
|
||||||
|
} else {
|
||||||
|
userData = null;
|
||||||
|
_showErrorAlert(result['message'] ?? 'Failed to load user data');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showErrorAlert(String message) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: Text('Error'),
|
||||||
|
content: Text(message),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(
|
||||||
|
'OK',
|
||||||
|
style: TextStyle(color: Color(0xFF6A4C93)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _copyToClipboard() async {
|
Future<void> _copyToClipboard() async {
|
||||||
if (fcmToken != null && fcmToken!.isNotEmpty) {
|
if (fcmToken != null && fcmToken!.isNotEmpty) {
|
||||||
await Clipboard.setData(ClipboardData(text: fcmToken!));
|
await Clipboard.setData(ClipboardData(text: fcmToken!));
|
||||||
@ -119,200 +160,232 @@ class _ProfilePageState extends State<ProfilePage> {
|
|||||||
padding: EdgeInsets.all(24),
|
padding: EdgeInsets.all(24),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
CircleAvatar(
|
if (isLoadingUser)
|
||||||
radius: 50,
|
CircularProgressIndicator(
|
||||||
backgroundColor: Color(0xFF6A4C93),
|
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFF6A4C93)),
|
||||||
child: Text(
|
)
|
||||||
'A',
|
else if (userData != null) ...[
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 50,
|
||||||
|
backgroundColor: Color(0xFF6A4C93),
|
||||||
|
child: Text(
|
||||||
|
(userData!['displayName'] ?? 'U').substring(0, 1).toUpperCase(),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 36,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
userData!['displayName'] ?? 'Unknown User',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
fontSize: 24,
|
||||||
fontSize: 36,
|
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
SizedBox(height: 4),
|
||||||
SizedBox(height: 16),
|
Text(
|
||||||
Text(
|
'@${userData!['username'] ?? 'unknown'}',
|
||||||
'Abu Norah',
|
style: TextStyle(fontSize: 16, color: Colors.grey[600]),
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
),
|
||||||
),
|
] else ...[
|
||||||
SizedBox(height: 4),
|
Icon(
|
||||||
Text(
|
Icons.error_outline,
|
||||||
'@yasser_hajri',
|
size: 50,
|
||||||
style: TextStyle(fontSize: 16, color: Colors.grey[600]),
|
color: Colors.grey[400],
|
||||||
),
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Failed to load user data',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 8),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _loadUserData,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Color(0xFF6A4C93),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
child: Text('Retry'),
|
||||||
|
),
|
||||||
|
],
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
Row(
|
if (userData != null)
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
Row(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'${mockUserPosts.length}',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF6A4C93),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'Posts',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: Colors.grey[600],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'127',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF6A4C93),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'Followers',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: Colors.grey[600],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
height: 1,
|
|
||||||
color: Colors.grey[200],
|
|
||||||
margin: EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
),
|
|
||||||
SizedBox(height: 16),
|
|
||||||
ListView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: NeverScrollableScrollPhysics(),
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
itemCount: mockUserPosts.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final post = mockUserPosts[index];
|
|
||||||
return Container(
|
|
||||||
margin: EdgeInsets.only(bottom: 16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.05),
|
|
||||||
blurRadius: 10,
|
|
||||||
offset: Offset(0, 2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(16),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
CircleAvatar(
|
Text(
|
||||||
radius: 16,
|
'${mockUserPosts.length}',
|
||||||
backgroundColor: Color(0xFF6A4C93),
|
style: TextStyle(
|
||||||
child: Text(
|
fontSize: 20,
|
||||||
'J',
|
fontWeight: FontWeight.bold,
|
||||||
style: TextStyle(
|
color: Color(0xFF6A4C93),
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 12),
|
Text(
|
||||||
Expanded(
|
'Posts',
|
||||||
child: Column(
|
style: TextStyle(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
fontSize: 14,
|
||||||
children: [
|
color: Colors.grey[600],
|
||||||
Text(
|
|
||||||
'Abu Norah',
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 14,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
post['timestamp'],
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.grey[600],
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 12),
|
Column(
|
||||||
Text(
|
|
||||||
post['content'],
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
height: 1.4,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 12),
|
|
||||||
Row(
|
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
|
||||||
post['isLiked']
|
|
||||||
? Icons.favorite
|
|
||||||
: Icons.favorite_border,
|
|
||||||
color: post['isLiked']
|
|
||||||
? Colors.red
|
|
||||||
: Colors.grey[600],
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
SizedBox(width: 4),
|
|
||||||
Text(
|
Text(
|
||||||
'${post['likes']}',
|
'127',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.grey[700],
|
fontSize: 20,
|
||||||
fontSize: 14,
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Color(0xFF6A4C93),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 16),
|
|
||||||
Icon(
|
|
||||||
Icons.chat_bubble_outline,
|
|
||||||
color: Colors.grey[600],
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
SizedBox(width: 4),
|
|
||||||
Text(
|
Text(
|
||||||
'${post['comments']}',
|
'Followers',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.grey[700],
|
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
color: Colors.grey[600],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
);
|
),
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
if (userData != null) ...[
|
||||||
|
Container(
|
||||||
|
height: 1,
|
||||||
|
color: Colors.grey[200],
|
||||||
|
margin: EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
itemCount: mockUserPosts.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final post = mockUserPosts[index];
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(bottom: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.05),
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: Offset(0, 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 16,
|
||||||
|
backgroundColor: Color(0xFF6A4C93),
|
||||||
|
child: Text(
|
||||||
|
(userData!['displayName'] ?? 'U').substring(0, 1).toUpperCase(),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
userData!['displayName'] ?? 'Unknown User',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
post['timestamp'],
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey[600],
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
post['content'],
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
height: 1.4,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
post['isLiked']
|
||||||
|
? Icons.favorite
|
||||||
|
: Icons.favorite_border,
|
||||||
|
color: post['isLiked']
|
||||||
|
? Colors.red
|
||||||
|
: Colors.grey[600],
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
'${post['likes']}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey[700],
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 16),
|
||||||
|
Icon(
|
||||||
|
Icons.chat_bubble_outline,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
'${post['comments']}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey[700],
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
SizedBox(height: 24),
|
SizedBox(height: 24),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -525,4 +598,4 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
72
frontend/lib/services/auth_service.dart
Normal file
72
frontend/lib/services/auth_service.dart
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import '../constants/api_constants.dart';
|
||||||
|
|
||||||
|
class AuthService {
|
||||||
|
static const FlutterSecureStorage _storage = FlutterSecureStorage();
|
||||||
|
static const String _tokenKey = 'jwt_token';
|
||||||
|
|
||||||
|
static Future<Map<String, dynamic>> login(
|
||||||
|
String emailOrUsername,
|
||||||
|
String password,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
final response = await http.post(
|
||||||
|
Uri.parse('${ApiConstants.baseUrl}${ApiConstants.loginEndpoint}'),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode({
|
||||||
|
'emailOrUsername': emailOrUsername,
|
||||||
|
'password': password,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = jsonDecode(response.body);
|
||||||
|
final token = data['token'];
|
||||||
|
|
||||||
|
if (token != null) {
|
||||||
|
await _storage.write(key: _tokenKey, value: token);
|
||||||
|
return {'success': true};
|
||||||
|
}
|
||||||
|
return {'success': false, 'message': 'No token received'};
|
||||||
|
} else if (response.statusCode == 403 || response.statusCode == 400) {
|
||||||
|
return {'success': false, 'message': 'Invalid credentials'};
|
||||||
|
} else if (response.statusCode == 401) {
|
||||||
|
return {'success': false, 'message': 'Invalid credentials'};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'success': false,
|
||||||
|
'message': 'Server error (${response.statusCode})',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Login error: $e');
|
||||||
|
return {
|
||||||
|
'success': false,
|
||||||
|
'message': 'Network error. Please check your connection.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<String?> getToken() async {
|
||||||
|
return await _storage.read(key: _tokenKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> isLoggedIn() async {
|
||||||
|
final token = await getToken();
|
||||||
|
return token != null && token.isNotEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> logout() async {
|
||||||
|
await _storage.delete(key: _tokenKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Map<String, String>> getAuthHeaders() async {
|
||||||
|
final token = await getToken();
|
||||||
|
return {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
if (token != null) 'Authorization': 'Bearer $token',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
40
frontend/lib/services/http_service.dart
Normal file
40
frontend/lib/services/http_service.dart
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import '../constants/api_constants.dart';
|
||||||
|
import 'auth_service.dart';
|
||||||
|
|
||||||
|
class HttpService {
|
||||||
|
static Future<http.Response> get(String endpoint) async {
|
||||||
|
final headers = await AuthService.getAuthHeaders();
|
||||||
|
return await http.get(
|
||||||
|
Uri.parse('${ApiConstants.baseUrl}$endpoint'),
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<http.Response> post(String endpoint, Map<String, dynamic> body) async {
|
||||||
|
final headers = await AuthService.getAuthHeaders();
|
||||||
|
return await http.post(
|
||||||
|
Uri.parse('${ApiConstants.baseUrl}$endpoint'),
|
||||||
|
headers: headers,
|
||||||
|
body: jsonEncode(body),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<http.Response> put(String endpoint, Map<String, dynamic> body) async {
|
||||||
|
final headers = await AuthService.getAuthHeaders();
|
||||||
|
return await http.put(
|
||||||
|
Uri.parse('${ApiConstants.baseUrl}$endpoint'),
|
||||||
|
headers: headers,
|
||||||
|
body: jsonEncode(body),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<http.Response> delete(String endpoint) async {
|
||||||
|
final headers = await AuthService.getAuthHeaders();
|
||||||
|
return await http.delete(
|
||||||
|
Uri.parse('${ApiConstants.baseUrl}$endpoint'),
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
frontend/lib/services/user_service.dart
Normal file
25
frontend/lib/services/user_service.dart
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import '../constants/api_constants.dart';
|
||||||
|
import 'http_service.dart';
|
||||||
|
|
||||||
|
class UserService {
|
||||||
|
static Future<Map<String, dynamic>> getCurrentUser() async {
|
||||||
|
try {
|
||||||
|
final response = await HttpService.get(ApiConstants.getUserEndpoint);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = jsonDecode(response.body);
|
||||||
|
return {'success': true, 'data': data};
|
||||||
|
} else if (response.statusCode == 401) {
|
||||||
|
return {'success': false, 'message': 'Session expired. Please login again.'};
|
||||||
|
} else if (response.statusCode == 403) {
|
||||||
|
return {'success': false, 'message': 'Access denied. Invalid credentials.'};
|
||||||
|
} else {
|
||||||
|
return {'success': false, 'message': 'Server error (${response.statusCode})'};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching user: $e');
|
||||||
|
return {'success': false, 'message': 'Network error. Please check your connection.'};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
|
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||||
|
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
flutter_secure_storage_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
@ -7,8 +7,12 @@ import Foundation
|
|||||||
|
|
||||||
import firebase_core
|
import firebase_core
|
||||||
import firebase_messaging
|
import firebase_messaging
|
||||||
|
import flutter_secure_storage_macos
|
||||||
|
import path_provider_foundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||||
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
|
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
|
||||||
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -81,6 +81,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.3"
|
version: "1.3.3"
|
||||||
|
ffi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi
|
||||||
|
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
firebase_core:
|
firebase_core:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -142,6 +150,54 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "5.0.0"
|
||||||
|
flutter_secure_storage:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage
|
||||||
|
sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.2.4"
|
||||||
|
flutter_secure_storage_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_linux
|
||||||
|
sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.3"
|
||||||
|
flutter_secure_storage_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_macos
|
||||||
|
sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.3"
|
||||||
|
flutter_secure_storage_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_platform_interface
|
||||||
|
sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
flutter_secure_storage_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_web
|
||||||
|
sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
flutter_secure_storage_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_windows
|
||||||
|
sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.2"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -184,6 +240,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.2"
|
version: "4.1.2"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.7"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -248,6 +312,62 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.9.1"
|
||||||
|
path_provider:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider
|
||||||
|
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.5"
|
||||||
|
path_provider_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_android
|
||||||
|
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.17"
|
||||||
|
path_provider_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_foundation
|
||||||
|
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
|
path_provider_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_linux
|
||||||
|
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1"
|
||||||
|
path_provider_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_platform_interface
|
||||||
|
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
path_provider_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_windows
|
||||||
|
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
|
platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: platform
|
||||||
|
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.6"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -341,6 +461,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.14.0"
|
||||||
|
xdg_directories:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xdg_directories
|
||||||
|
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.8.1 <4.0.0"
|
dart: ">=3.8.1 <4.0.0"
|
||||||
flutter: ">=3.22.0"
|
flutter: ">=3.27.0"
|
||||||
|
|||||||
@ -14,6 +14,7 @@ dependencies:
|
|||||||
firebase_messaging: ^15.2.9
|
firebase_messaging: ^15.2.9
|
||||||
http: ^1.1.0
|
http: ^1.1.0
|
||||||
googleapis_auth: ^1.6.0
|
googleapis_auth: ^1.6.0
|
||||||
|
flutter_secure_storage: ^9.2.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
@ -7,8 +7,11 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <firebase_core/firebase_core_plugin_c_api.h>
|
#include <firebase_core/firebase_core_plugin_c_api.h>
|
||||||
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
FirebaseCorePluginCApiRegisterWithRegistrar(
|
FirebaseCorePluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
|
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
|
||||||
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
firebase_core
|
firebase_core
|
||||||
|
flutter_secure_storage_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user