register oidc changes, Login by oidc

This commit is contained in:
venbaittech 2025-10-30 17:14:26 +05:30
parent 466eafb196
commit 001334819e
6 changed files with 1149 additions and 952 deletions

View File

@ -94,7 +94,25 @@ final GoRouter router = GoRouter(
return LoginRoute(fromPage: fromPage, userInfo: userInfo); return LoginRoute(fromPage: fromPage, userInfo: userInfo);
}, },
), ),
GoRoute(
path: '/register',
builder: (context, state) {
final extra = state.extra;
final fromPage = (extra is Map && extra['fromPage'] is String)
? extra['fromPage'] as String
: null;
final userInfo = (extra is Map && extra['userInfo'] is Map)
? Map<String, dynamic>.from(extra['userInfo'])
: <String, dynamic>{};
return RegisterScreen(fromPage: fromPage, userInfo: userInfo);
},
),
// GoRoute(
// path: '/register',
// builder: (context, state) => RegisterScreen(),
// ),
GoRoute( GoRoute(
path: '/fcscprofilelinking', path: '/fcscprofilelinking',
builder: (context, state) { builder: (context, state) {
@ -114,10 +132,7 @@ final GoRouter router = GoRouter(
path: '/myhomepage', path: '/myhomepage',
builder: (context, state) => MyHomePage(), builder: (context, state) => MyHomePage(),
), ),
GoRoute(
path: '/register',
builder: (context, state) => RegisterScreen(),
),
// GoRoute( // GoRoute(
// path: '/DemoHome/:dataSets', // path: '/DemoHome/:dataSets',
// builder: (context, state) { // builder: (context, state) {
@ -380,7 +395,7 @@ final GoRouter router = GoRouter(
builder: (context, state) { builder: (context, state) {
final report = state.extra as Map<String, dynamic>; final report = state.extra as Map<String, dynamic>;
return ReportDetailPage( return ReportDetailPage(
title:report['title']!, title: report['title']!,
); );
}, },
), ),
@ -395,10 +410,9 @@ final GoRouter router = GoRouter(
state.uri.path == '/login' || state.uri.path == '/login' ||
state.uri.path == '/register' || state.uri.path == '/register' ||
state.uri.path.startsWith('/forgot-password/') || state.uri.path.startsWith('/forgot-password/') ||
state.uri.path == '/termsandconditions'|| state.uri.path == '/termsandconditions' ||
state.uri.path == '/privacy_policy' || state.uri.path == '/privacy_policy' ||
state.uri.path == '/fcscprofilelinking' state.uri.path == '/fcscprofilelinking') {
) {
return null; return null;
} }

View File

@ -17,9 +17,19 @@ import 'package:uae_stat/l10n/app_localizations.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart'; import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/components/my_toggle.dart'; import 'package:uae_stat/presentation/components/my_toggle.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../routes/auth_routes/login_route.dart'; import '../../routes/auth_routes/login_route.dart';
class RegisterScreen extends ConsumerStatefulWidget { class RegisterScreen extends ConsumerStatefulWidget {
final String? fromPage;
final Map<String, dynamic>? userInfo;
const RegisterScreen({
Key? key,
this.fromPage,
this.userInfo,
}) : super(key: key);
@override @override
_RegisterScreenState createState() => _RegisterScreenState(); _RegisterScreenState createState() => _RegisterScreenState();
} }
@ -41,6 +51,7 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
bool registrationFailed = false; bool registrationFailed = false;
bool isRegistering = false; bool isRegistering = false;
dynamic userID; dynamic userID;
bool cameFromLinking = false;
final pb = PocketBase(apiUrl); final pb = PocketBase(apiUrl);
// final pb = PocketBase('http://127.0.0.1:8090'); // final pb = PocketBase('http://127.0.0.1:8090');
@ -66,6 +77,9 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
void initState() { void initState() {
super.initState(); super.initState();
print(
'REGISTRATION -FROMPAGE - ${widget.fromPage} - userInfo - ${widget.userInfo}');
// Add listeners for focus nodes // Add listeners for focus nodes
for (int i = 0; i < _focusNodes.length; i++) { for (int i = 0; i < _focusNodes.length; i++) {
_focusNodes[i].addListener(() { _focusNodes[i].addListener(() {
@ -236,16 +250,37 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
var mailID = _emailController.text; var mailID = _emailController.text;
var email = mailID.toLowerCase(); var email = mailID.toLowerCase();
// Create user in PocketBase // Create user in PocketBase
final response = await pb.collection('users').create(body: { final response = cameFromLinking
'uname': _usernameController.text, ? await pb.collection('users').create(
'email': email, body: {
'password': _passwordController.text, 'uname': _usernameController.text,
'passwordConfirm': _passwordController.text, 'email': email,
'status': 'Pending', 'password': '',
'role': 'user', 'passwordConfirm': '',
}, headers: { 'status': 'Approved',
// 'Authorization': adminToken 'role': 'user',
}); 'user_mail_verify': true,
'reviewed': true,
'emiratesid': widget.userInfo?['uuid'],
"is_oauth_login": 1,
},
headers: {
// 'Authorization': adminToken
},
)
: await pb.collection('users').create(
body: {
'uname': _usernameController.text,
'email': email,
'password': _passwordController.text,
'passwordConfirm': _passwordController.text,
'status': 'Pending',
'role': 'user',
},
headers: {
// 'Authorization': adminToken
},
);
if (response.id != null) { if (response.id != null) {
userID = response.id; userID = response.id;
// Request email verification // Request email verification
@ -359,11 +394,104 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
} }
} }
Future<void> oAuthOIDCLogin(
BuildContext context, WidgetRef ref, platform) async {
setState(() {
hasValidated = true;
});
if (!(_formKey.currentState?.validate() ?? false)) return;
if (!isChecked) {
setState(() => showError = true);
return;
}
setState(() {
isLoading = true;
isRegistering = true;
});
print('platform $platform');
try {
print('platform $platform');
final authData =
await pb.collection('users').authWithOAuth2(platform, (url) async {
await launchUrl(url);
});
print('authData $authData');
// Check if authData is valid
if (authData.token.isNotEmpty && authData.record != null) {
print("✅ Login Successful");
print(authData.record);
final metaDetails = authData.meta;
final userDetails = authData.record;
final token = authData.token;
final userId = userDetails?.id;
final email = userDetails?.data['email'] ?? '';
final isProfileComplete =
userDetails?.data['is_profile_completed'] ?? '';
final loginCount = userDetails?.data['login_count'] ?? '';
print("User ID: ${userId}");
print("Email: ${email}");
print("Email: ${isProfileComplete}");
print("Email: ${loginCount}");
// final session = await context.loaderWithErrorDialog(
// () => ref
// .read(
// authUseCaseProvider.notifier,
// )
// .googleAndAppleAuthencation(authData),
// errorDialogBuilder: (
// error, [
// StackTrace? stackTrace,
// ]) {
// if (error == LoginError.invalidEmailPw) {
// return context.simpleDialog(
// title: context.translate(
// 'Incorrect Credentials',
// 'أوراق غير صحيحة',
// ),
// content: context.translate(
// 'Your email or password is invalid. Please try again.',
// 'بريدك الإلكتروني أو كلمة المرور غير صالحة. حاول مرة اخرى.',
// ),
// );
// }
// return context.simpleDialog();
// },
} else {
print("❌ Login failed: Invalid authData");
}
} catch (e) {
print("❌ Error during Google login: $e");
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(content: Text("Login failed. Please try again.")),
// );
}
}
void handleSuccess(RecordModel response) {
setState(() {
userID = response.id;
registrationSuccess = true;
registrationFailed = false;
isRegistering = false;
isLoading = false;
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
double screenheight = MediaQuery.of(context).size.height; double screenheight = MediaQuery.of(context).size.height;
double screenwidth = MediaQuery.of(context).size.width; double screenwidth = MediaQuery.of(context).size.width;
final registerLocale = ref.watch(localeProvider); final registerLocale = ref.watch(localeProvider);
cameFromLinking = widget.fromPage == 'fcscprofilelinking';
ref.listen<AsyncValue<bool>>(connectivityProvider, (previous, hasInternet) { ref.listen<AsyncValue<bool>>(connectivityProvider, (previous, hasInternet) {
if (hasInternet.value == false) { if (hasInternet.value == false) {
@ -933,270 +1061,282 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
), ),
), ),
SizedBox(height: 15), SizedBox(height: 15),
Padding(
padding: const EdgeInsets.only(
top: 5.0,
bottom: 10,
left: 30,
right: 30),
child: TextFormField(
controller: _passwordController,
// focusNode: _focusNodes[2],
obscureText: _obscurePassword,
decoration: InputDecoration(
filled: true,
fillColor: isDarkTheme
? const Color(0xFF000000)
: Colors.white,
errorStyle: TextStyle(
color: Color(0xFFD83731),
// color: isDarkTheme
// ? Color(0xFFFFA200)
// : Color(0xFFb22222),
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
),
hintText: _showHints[2]
? AppLocalizations.of(
context)!
.enter_your_password
: null,
// _showHints[2] ? 'Enter your password' : null,
prefixIconConstraints:
const BoxConstraints(
maxWidth: 25 + 16 + 10,
maxHeight: 25 + (8 * 2),
),
prefixIcon: Padding(
padding:
const EdgeInsetsDirectional
.only(
start: 16,
end: 10,
),
child: Image.asset(
MiscIconAssetPath.lock,
fit: BoxFit.fitHeight,
height: 25,
width: 25,
),
),
// prefixIcon: Icon( if (!cameFromLinking) ...[
// Icons.lock, Padding(
// color: Color(0xFF90B0D5), padding: const EdgeInsets.only(
// ), top: 5.0,
suffixIcon: IconButton( bottom: 10,
// icon: Icon( left: 30,
// _obscurePassword right: 30),
// ? Icons.visibility_off child: TextFormField(
// : Icons.visibility, controller: _passwordController,
// color: Color(0xFF9EA2A9), // focusNode: _focusNodes[2],
obscureText: _obscurePassword,
decoration: InputDecoration(
filled: true,
fillColor: isDarkTheme
? const Color(0xFF000000)
: Colors.white,
errorStyle: TextStyle(
color: Color(0xFFD83731),
// color: isDarkTheme
// ? Color(0xFFFFA200)
// : Color(0xFFb22222),
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
),
hintText: _showHints[2]
? AppLocalizations.of(
context)!
.enter_your_password
: null,
// _showHints[2] ? 'Enter your password' : null,
prefixIconConstraints:
const BoxConstraints(
maxWidth: 25 + 16 + 10,
maxHeight: 25 + (8 * 2),
),
prefixIcon: Padding(
padding:
const EdgeInsetsDirectional
.only(
start: 16,
end: 10,
),
child: Image.asset(
MiscIconAssetPath.lock,
fit: BoxFit.fitHeight,
height: 25,
width: 25,
),
),
// prefixIcon: Icon(
// Icons.lock,
// color: Color(0xFF90B0D5),
// ), // ),
suffixIcon: IconButton(
// icon: Icon(
// _obscurePassword
// ? Icons.visibility_off
// : Icons.visibility,
// color: Color(0xFF9EA2A9),
// ),
icon: Image.asset( icon: Image.asset(
_obscurePassword _obscurePassword
? MiscIconAssetPath ? MiscIconAssetPath
.visibilityOff .visibilityOff
: MiscIconAssetPath : MiscIconAssetPath
.visibleOn, .visibleOn,
color: Color( color: Color(
0xFF9EA2A9), // Apply color if needed, but keep in mind `Image.asset` might not support color directly. 0xFF9EA2A9), // Apply color if needed, but keep in mind `Image.asset` might not support color directly.
width: 24, width: 24,
height: 24, height: 24,
),
onPressed: () {
setState(() {
_obscurePassword =
!_obscurePassword;
});
},
),
// border: OutlineInputBorder(),
enabledBorder:
OutlineInputBorder(
borderRadius:
BorderRadius.circular(10),
borderSide: BorderSide(
color: Color(0xFFB68A34),
width: 1),
),
focusedBorder:
OutlineInputBorder(
borderRadius:
BorderRadius.circular(10),
borderSide: BorderSide(
color: Color(0xFFB68A34),
width:
2), // Focused border
),
errorBorder: OutlineInputBorder(
borderRadius:
BorderRadius.circular(10),
borderSide: BorderSide(
color: Color(0xFFD83731),
width: 1), // Error border
),
focusedErrorBorder:
OutlineInputBorder(
borderRadius:
BorderRadius.circular(10),
borderSide: BorderSide(
color: Color(0xFFD83731),
width:
1), // Match the error color
), ),
onPressed: () { counterText: '',
setState(() { hintStyle: TextStyle(
_obscurePassword = fontFamily: context.translate(
!_obscurePassword; 'Roboto',
}); 'NotoKufi',
}, ),
), color:
// border: OutlineInputBorder(), const Color(0xFFC3C6CB),
enabledBorder: OutlineInputBorder( fontSize: registerLocale
borderRadius: ?.languageCode ==
BorderRadius.circular(10), 'ar'
borderSide: BorderSide( ? 14
color: Color(0xFFB68A34), : 16,
width: 1),
),
focusedBorder: OutlineInputBorder(
borderRadius:
BorderRadius.circular(10),
borderSide: BorderSide(
color: Color(0xFFB68A34),
width: 2), // Focused border
),
errorBorder: OutlineInputBorder(
borderRadius:
BorderRadius.circular(10),
borderSide: BorderSide(
color: Color(0xFFD83731),
width: 1), // Error border
),
focusedErrorBorder:
OutlineInputBorder(
borderRadius:
BorderRadius.circular(10),
borderSide: BorderSide(
color: Color(0xFFD83731),
width:
1), // Match the error color
),
counterText: '',
hintStyle: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
), ),
color: const Color(0xFFC3C6CB),
fontSize: registerLocale
?.languageCode ==
'ar'
? 14
: 16,
), ),
validator: _validatePassword,
maxLength: 40,
maxLengthEnforcement:
MaxLengthEnforcement.enforced,
), ),
validator: _validatePassword,
maxLength: 40,
maxLengthEnforcement:
MaxLengthEnforcement.enforced,
), ),
), SizedBox(height: 10),
SizedBox(height: 10), Padding(
Padding( padding: const EdgeInsets.only(
padding: const EdgeInsets.only( top: 5.0,
top: 5.0, bottom: 10,
bottom: 10, left: 30,
left: 30, right: 30),
right: 30), child: TextFormField(
child: TextFormField( controller:
controller: _confirmpasswordController,
_confirmpasswordController, // focusNode: _focusNodes[3],
// focusNode: _focusNodes[3], obscureText:
obscureText: _obscureConfirmPassword,
_obscureConfirmPassword, decoration: InputDecoration(
decoration: InputDecoration( filled: true,
filled: true, fillColor: isDarkTheme
fillColor: isDarkTheme ? const Color(0xFF000000)
? const Color(0xFF000000) : Colors.white,
: Colors.white, errorStyle: TextStyle(
errorStyle: TextStyle( color: Color(0xFFD83731),
color: Color(0xFFD83731), fontFamily: context.translate(
fontFamily: context.translate( 'Roboto',
'Roboto', 'NotoKufi',
'NotoKufi', ),
), ),
), hintText: _showHints[3]
hintText: _showHints[3] ? AppLocalizations.of(
? AppLocalizations.of( context)!
context)! .register_Confirm_password
.register_Confirm_password : null,
: null, // _showHints[3] ? 'Confirm password' : null,
// _showHints[3] ? 'Confirm password' : null, prefixIconConstraints:
prefixIconConstraints: const BoxConstraints(
const BoxConstraints( maxWidth: 25 + 16 + 10,
maxWidth: 25 + 16 + 10, maxHeight: 25 + (8 * 2),
maxHeight: 25 + (8 * 2),
),
prefixIcon: Padding(
padding:
const EdgeInsetsDirectional
.only(
start: 16,
end: 10,
), ),
child: Image.asset( prefixIcon: Padding(
MiscIconAssetPath.lock, padding:
fit: BoxFit.fitHeight, const EdgeInsetsDirectional
height: 25, .only(
width: 25, start: 16,
end: 10,
),
child: Image.asset(
MiscIconAssetPath.lock,
fit: BoxFit.fitHeight,
height: 25,
width: 25,
),
), ),
),
suffixIcon: IconButton( suffixIcon: IconButton(
icon: Image.asset( icon: Image.asset(
_obscureConfirmPassword _obscureConfirmPassword
? MiscIconAssetPath ? MiscIconAssetPath
.visibilityOff .visibilityOff
: MiscIconAssetPath : MiscIconAssetPath
.visibleOn, .visibleOn,
color: Color( color: Color(
0xFF9EA2A9), // Apply color if needed, but keep in mind `Image.asset` might not support color directly. 0xFF9EA2A9), // Apply color if needed, but keep in mind `Image.asset` might not support color directly.
width: 24, width: 24,
height: 24, height: 24,
),
onPressed: () {
setState(() {
_obscureConfirmPassword =
!_obscureConfirmPassword;
});
},
), ),
onPressed: () { // border: OutlineInputBorder(
setState(() { // borderSide: BorderSide(color: Colors.blue, width: 2), // Default border color
_obscureConfirmPassword = // ),
!_obscureConfirmPassword; enabledBorder:
}); OutlineInputBorder(
}, borderRadius:
), BorderRadius.circular(10),
// border: OutlineInputBorder( borderSide: BorderSide(
// borderSide: BorderSide(color: Colors.blue, width: 2), // Default border color color: Color(0xFFB68A34),
// ), width: 1),
enabledBorder: OutlineInputBorder( ),
borderRadius: focusedBorder:
BorderRadius.circular(10), OutlineInputBorder(
borderSide: BorderSide( borderRadius:
color: Color(0xFFB68A34), BorderRadius.circular(10),
width: 1), borderSide: BorderSide(
), color: Color(0xFFB68A34),
focusedBorder: OutlineInputBorder( width:
borderRadius: 2), // Focused border
BorderRadius.circular(10), ),
borderSide: BorderSide( errorBorder: OutlineInputBorder(
color: Color(0xFFB68A34), borderRadius:
width: 2), // Focused border BorderRadius.circular(10),
), borderSide: BorderSide(
errorBorder: OutlineInputBorder( color: Color(0xFFD83731),
borderRadius: width: 1), // Error border
BorderRadius.circular(10), ),
borderSide: BorderSide( focusedErrorBorder:
color: Color(0xFFD83731), OutlineInputBorder(
width: 1), // Error border borderRadius:
), BorderRadius.circular(10),
focusedErrorBorder: borderSide: BorderSide(
OutlineInputBorder( color: Color(0xFFD83731),
borderRadius: width:
BorderRadius.circular(10), 1), // Match the error color
borderSide: BorderSide( ),
color: Color(0xFFD83731), counterText: '',
width: hintStyle: TextStyle(
1), // Match the error color fontFamily: context.translate(
), 'Roboto',
counterText: '', 'NotoKufi',
hintStyle: TextStyle( ),
fontFamily: context.translate( color:
'Roboto', const Color(0xFFC3C6CB),
'NotoKufi', fontSize: registerLocale
?.languageCode ==
'ar'
? 14
: 16,
), ),
color: const Color(0xFFC3C6CB),
fontSize: registerLocale
?.languageCode ==
'ar'
? 14
: 16,
), ),
validator:
_validateConfirmPassword,
maxLength: 40,
maxLengthEnforcement:
MaxLengthEnforcement.enforced,
inputFormatters: [
LengthLimitingTextInputFormatter(
64), // Limit to 40 characters
],
), ),
validator: _validateConfirmPassword,
maxLength: 40,
maxLengthEnforcement:
MaxLengthEnforcement.enforced,
inputFormatters: [
LengthLimitingTextInputFormatter(
64), // Limit to 40 characters
],
), ),
), SizedBox(height: 10),
SizedBox(height: 10), ],
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
top: 0, top: 0,
@ -1429,9 +1569,19 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
child: SizedBox( child: SizedBox(
width: screenwidth / 1, width: screenwidth / 1,
child: ElevatedButton( child: ElevatedButton(
onPressed: isRegistering // onPressed: isRegistering
? null // ? null
: _registerUser, // : _registerUser?
onPressed: () {
if (isRegistering) {
_registerUser;
} else if (cameFromLinking) {
oAuthOIDCLogin(context, ref,
'oidc'); //Open ID connect
} else {
null;
}
},
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Color( backgroundColor: Color(
0xFFB68A34), // Brownish color for Register 0xFFB68A34), // Brownish color for Register
@ -1578,87 +1728,99 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
// ), // ),
// ), // ),
// ), // ),
if (!cameFromLinking) ...[
SizedBox(height: 20), SizedBox(height: 20),
Text( Text(
AppLocalizations.of(context)! AppLocalizations.of(context)!
.account_confirmation, .account_confirmation,
style: TextStyle( style: TextStyle(
fontFamily: context.translate( fontFamily: context.translate(
'Roboto', 'Roboto',
'NotoKufi', 'NotoKufi',
),
fontWeight: FontWeight.w500,
fontSize:
registerLocale?.languageCode ==
'ar'
? 12
: 14,
color: isDarkTheme
? Color(0xFFFFFFFF)
: Color(0xFF898C81),
),
),
SizedBox(height: 5),
Padding(
padding: const EdgeInsets.only(
top: 5.0,
bottom: 0,
left: 30,
right: 30),
child: SizedBox(
width: screenwidth / 1,
child: ElevatedButton(
onPressed: () {
context.pop();
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(
0xFFB68A34), // Blueish color for Login
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(10),
),
padding: EdgeInsets.symmetric(
vertical: 16.5),
), ),
child: Row( fontWeight: FontWeight.w500,
mainAxisSize: MainAxisSize.min, fontSize: registerLocale
children: [ ?.languageCode ==
Text( 'ar'
AppLocalizations.of( ? 12
context)! : 14,
.login_title, color: isDarkTheme
style: TextStyle( ? Color(0xFFFFFFFF)
fontFamily: : Color(0xFF898C81),
context.translate( ),
'Roboto', ),
'NotoKufi', SizedBox(height: 5),
), Padding(
fontSize: registerLocale padding: const EdgeInsets.only(
?.languageCode == top: 5.0,
'ar' bottom: 0,
? 14 left: 30,
: 16, right: 30),
color: Colors.white), child: SizedBox(
width: screenwidth / 1,
child: ElevatedButton(
onPressed: () {
context.pop();
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(
0xFFB68A34), // Blueish color for Login
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(
10),
), ),
SizedBox(width: 8), padding: EdgeInsets.symmetric(
Icon( vertical: 16.5),
Icons ),
.chevron_right_outlined, child: Row(
color: Colors mainAxisSize:
.white, // Set your desired color here MainAxisSize.min,
) children: [
], Text(
AppLocalizations.of(
context)!
.login_title,
style: TextStyle(
fontFamily:
context.translate(
'Roboto',
'NotoKufi',
),
fontSize: registerLocale
?.languageCode ==
'ar'
? 14
: 16,
color: Colors.white),
),
SizedBox(width: 8),
Icon(
Icons
.chevron_right_outlined,
color: Colors
.white, // Set your desired color here
)
],
),
), ),
), ),
), ),
), // Spacer(),
// Spacer(),
SizedBox( SizedBox(
height: 15, height: MediaQuery.of(context)
), .size
.height /
1.2,
),
],
if (cameFromLinking) ...[
SizedBox(
height: 85,
),
],
Padding( Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 70.0), horizontal: 70.0),

View File

@ -4,17 +4,17 @@ import 'package:go_router/go_router.dart';
import 'package:uae_stat/config/connectivity_provider.dart'; import 'package:uae_stat/config/connectivity_provider.dart';
import 'package:uae_stat/config/theme/theme_provider.dart'; import 'package:uae_stat/config/theme/theme_provider.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart'; import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart'; import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/components/my_toggle.dart'; import 'package:uae_stat/presentation/components/my_toggle.dart';
class FcscProfileLinking extends ConsumerStatefulWidget { class FcscProfileLinking extends ConsumerStatefulWidget {
final Map<String, dynamic> userInfo; final Map<String, dynamic> userInfo;
const FcscProfileLinking({super.key, required this.userInfo}); const FcscProfileLinking({super.key, required this.userInfo});
@override @override
ConsumerState<FcscProfileLinking> createState() => _FcscProfileLinkingState(); ConsumerState<FcscProfileLinking> createState() => _FcscProfileLinkingState();
} }
class _FcscProfileLinkingState extends ConsumerState<FcscProfileLinking> { class _FcscProfileLinkingState extends ConsumerState<FcscProfileLinking> {
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
@ -103,11 +103,11 @@ class _FcscProfileLinkingState extends ConsumerState<FcscProfileLinking> {
: Color(0xFF000000), : Color(0xFF000000),
), ),
), ),
SizedBox( SizedBox(
height: isTablet height: isTablet
? screenHeight * 0.12 // 12% of screen for tablet ? screenHeight * 0.12 // 12% of screen for tablet
: screenHeight * 0.08, // 8% for mobile : screenHeight * 0.08, // 8% for mobile
), ),
// Subtitle // Subtitle
Text( Text(
@ -124,104 +124,128 @@ class _FcscProfileLinkingState extends ConsumerState<FcscProfileLinking> {
const SizedBox(height: 32), const SizedBox(height: 32),
// Buttons Row // Buttons Row
Padding(
padding: EdgeInsets.symmetric(
horizontal: isTablet ? 80 : 24, // responsive horizontal space
),
child:Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// YES Button
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
const Color(0xFFB68A34), // gold tone
padding: EdgeInsets.symmetric(
vertical: isTablet ? 18 : 14,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
onPressed: () {
GoRouter.of(context).go(
'/login',
extra: {
'fromPage': 'fcscprofilelinking',
'userInfo': widget.userInfo, // 👈 Pass full object/map
}, // 👈 pass parameter here
);
},
child: Text(
"Yes *",
style: TextStyle(
fontSize: 16, color: const Color(0xFFFFFFFF)),
),
),
),
const SizedBox(width: 20),
// NO Button
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
const Color(0xFFB68A34), // gold tone
padding: EdgeInsets.symmetric(
vertical: isTablet ? 18 : 14,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
onPressed: () {
// Handle No
},
child: Text(
"No **",
style:
TextStyle(fontSize: 16, color: const Color(0xFFFFFFFF)),
),
),
),
],
),
),
const SizedBox(height: 24),
Padding( Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: isTablet ? 80 : 24, // responsive horizontal space horizontal: isTablet
? 80
: 24, // responsive horizontal space
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Text('* ', // YES Button
style: TextStyle(color: Color(0xFF898C81), fontWeight: FontWeight.bold)),
Expanded( Expanded(
child: Text( child: ElevatedButton(
'Your UAE PASS account will be linked to your existing FCSC profile', style: ElevatedButton.styleFrom(
style: TextStyle( backgroundColor:
fontSize: isTablet ? 16 : 14, const Color(0xFFB68A34), // gold tone
fontWeight: FontWeight.w500, padding: EdgeInsets.symmetric(
color: const Color(0xFF898C81), vertical: isTablet ? 18 : 14,
height: 1.5, ),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
onPressed: () {
print(
'YESfcscprofilelinking- ${widget.userInfo}');
GoRouter.of(context).go(
'/login',
extra: {
'fromPage': 'fcscprofilelinking',
'userInfo': widget
.userInfo, // 👈 Pass full object/map
}, // 👈 pass parameter here
);
},
child: Text(
"Yes *",
style: TextStyle(
fontSize: 16,
color: const Color(0xFFFFFFFF)),
),
),
),
const SizedBox(width: 20),
// NO Button
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
const Color(0xFFB68A34), // gold tone
padding: EdgeInsets.symmetric(
vertical: isTablet ? 18 : 14,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
onPressed: () {
print(
'NOfcscprofilelinking- ${widget.userInfo}');
// Handle No
GoRouter.of(context).go(
'/register',
extra: {
'fromPage': 'fcscprofilelinking',
'userInfo': widget
.userInfo, // 👈 Pass full object/map
}, // 👈 pass parameter here
);
},
child: Text(
"No **",
style: TextStyle(
fontSize: 16,
color: const Color(0xFFFFFFFF)),
), ),
), ),
), ),
], ],
) ),
), ),
const SizedBox(height: 24),
Padding(
padding: EdgeInsets.symmetric(
horizontal: isTablet
? 80
: 24, // responsive horizontal space
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('* ',
style: TextStyle(
color: Color(0xFF898C81),
fontWeight: FontWeight.bold)),
Expanded(
child: Text(
'Your UAE PASS account will be linked to your existing FCSC profile',
style: TextStyle(
fontSize: isTablet ? 16 : 14,
fontWeight: FontWeight.w500,
color: const Color(0xFF898C81),
height: 1.5,
),
),
),
],
)),
const SizedBox(height: 15), const SizedBox(height: 15),
Padding( Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: isTablet ? 80 : 24, // responsive horizontal space horizontal: isTablet
? 80
: 24, // responsive horizontal space
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('** ', const Text('** ',
style: TextStyle(color: Color(0xFF898C81), fontWeight: FontWeight.bold)), style: TextStyle(
color: Color(0xFF898C81),
fontWeight: FontWeight.bold)),
Expanded( Expanded(
child: Text( child: Text(
'Your UAE Stat Mobile App profile will be established based on the UAE PASS account', 'Your UAE Stat Mobile App profile will be established based on the UAE PASS account',
@ -234,19 +258,21 @@ class _FcscProfileLinkingState extends ConsumerState<FcscProfileLinking> {
), ),
), ),
], ],
) )),
),
SizedBox(height: 40), SizedBox(height: 40),
// Note section // Note section
Padding( Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: isTablet ? 80 : 24, // responsive horizontal space horizontal: isTablet
? 80
: 24, // responsive horizontal space
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('Note: ', Text(
'Note: ',
style: TextStyle( style: TextStyle(
fontSize: isTablet ? 16 : 14, fontSize: isTablet ? 16 : 14,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
@ -266,8 +292,7 @@ class _FcscProfileLinkingState extends ConsumerState<FcscProfileLinking> {
), ),
), ),
], ],
) )),
),
const SizedBox(height: 40), const SizedBox(height: 40),
@ -281,8 +306,7 @@ class _FcscProfileLinkingState extends ConsumerState<FcscProfileLinking> {
height: 30, height: 30,
), ),
) )
] ])
)
])), ])),
]))); ])));
} }

View File

@ -43,7 +43,6 @@ import '../../Screens/auth_verification/registration.dart';
import 'package:pocketbase/pocketbase.dart'; import 'package:pocketbase/pocketbase.dart';
import 'package:uae_stat/config/api_config.dart'; import 'package:uae_stat/config/api_config.dart';
import '../../components/indicators/locale_provider.dart'; import '../../components/indicators/locale_provider.dart';
import '../../components/my_toggle.dart'; import '../../components/my_toggle.dart';
@ -157,10 +156,9 @@ class LoginRoute extends HookConsumerWidget {
// } // }
Future<void> getDeviceToken( Future<void> getDeviceToken(
BuildContext context, String userId, WidgetRef ref,authtoken) async { BuildContext context, String userId, WidgetRef ref, authtoken) async {
await updateDeviceToken(userId, 'fcmToken', ref,authtoken); await updateDeviceToken(userId, 'fcmToken', ref, authtoken);
try { try {
// await _firebaseMessaging.requestPermission(); // await _firebaseMessaging.requestPermission();
// print("FCM Token Entry"); // print("FCM Token Entry");
// String? token = await FirebaseMessaging.instance.getToken(); // String? token = await FirebaseMessaging.instance.getToken();
@ -179,7 +177,7 @@ class LoginRoute extends HookConsumerWidget {
} }
if (token != null) { if (token != null) {
await updateDeviceToken(userId, token, ref,authtoken); await updateDeviceToken(userId, token, ref, authtoken);
} }
} else if (Platform.isAndroid) { } else if (Platform.isAndroid) {
// Get FCM token // Get FCM token
@ -187,7 +185,7 @@ class LoginRoute extends HookConsumerWidget {
print('🔥 FCM Token (Android): $fcmToken'); print('🔥 FCM Token (Android): $fcmToken');
if (fcmToken != null) { if (fcmToken != null) {
await updateDeviceToken(userId, fcmToken, ref,authtoken); await updateDeviceToken(userId, fcmToken, ref, authtoken);
} }
} }
@ -232,7 +230,7 @@ class LoginRoute extends HookConsumerWidget {
} }
Future<void> updateDeviceToken( Future<void> updateDeviceToken(
String userId, String token, WidgetRef ref,authtoken) async { String userId, String token, WidgetRef ref, authtoken) async {
print('UUID'); print('UUID');
final url = "${pb.baseUrl}api/collections/users/records/$userId"; final url = "${pb.baseUrl}api/collections/users/records/$userId";
@ -260,11 +258,12 @@ class LoginRoute extends HookConsumerWidget {
Uri.parse(url), Uri.parse(url),
headers: { headers: {
'Content-Type': 'application/json', // Add this 'Content-Type': 'application/json', // Add this
'Authorization': 'Bearer $authtoken', // Correct way to send auth token 'Authorization':
'Bearer $authtoken', // Correct way to send auth token
}, },
body: jsonEncode({ body: jsonEncode({
"device_token": token, "device_token": token,
if(cameFromLinking) "emiratesid": userInfo?['uuid'], if (cameFromLinking) "emiratesid": userInfo?['uuid'],
}), }),
); );
@ -376,8 +375,7 @@ class LoginRoute extends HookConsumerWidget {
} }
void openAppLinkSettings() { void openAppLinkSettings() {
const packageName = const packageName = 'ae.gov.fcsc.stats'; // Replace with your app's package
'ae.gov.fcsc.stats'; // Replace with your app's package
final intent = AndroidIntent( final intent = AndroidIntent(
action: 'android.settings.APP_OPEN_BY_DEFAULT_SETTINGS', action: 'android.settings.APP_OPEN_BY_DEFAULT_SETTINGS',
data: 'package:$packageName', data: 'package:$packageName',
@ -397,30 +395,56 @@ class LoginRoute extends HookConsumerWidget {
Future<void> signInWithUAEPASS() async { Future<void> signInWithUAEPASS() async {
try { try {
print('signInWithUAEPASS-1');
final authData = await pb.collection('users').authWithOAuth2( final authData = await pb.collection('users').authWithOAuth2(
'uaepass-stg', // 'uaepass-stg',
(url) async { 'oidc',
// Launch UAE PASS page // (url) async {
await launchUrl(Uri.parse(url as String), mode: LaunchMode.externalApplication); // // Launch UAE PASS page
// await launchUrl(Uri.parse(url as String),
// mode: LaunchMode.externalApplication);
// },
(uri) async {
// `uri` is already a Uri instance
await launchUrl(uri, mode: LaunchMode.externalApplication);
}, },
scopes: ['openid', 'profile', 'email'], scopes: ['openid', 'profile', 'email'],
// redirectUrl: 'uaepassdemo://callback', // redirectUrl: 'uaepassdemo://callback',
); );
// final authData = await pb.collection('users').authWithOAuth2(
// 'uaepass-stg', // 'google' or 'apple'
// (providerUrl) async {
// final Uri uri = Uri.parse(providerUrl.toString());
//
// // Inject redirect_url so PocketBase will redirect to the app when done
// final newUri = uri.replace(queryParameters: {
// ...uri.queryParameters,
// 'redirect_url': deepLinkRedirect,
// });
//
// // Open in external browser (ensures OS handles returning to app)
// if (!await launchUrl(newUri, mode: LaunchMode.externalApplication)) {
// throw 'Could not launch OAuth URL';
// }
// },
// );
print('signInWithUAEPASS-2');
print('✅ Logged in user: ${authData}'); print('✅ Logged in user: ${authData}');
} catch (e) { } catch (e) {
print('❌ Error: $e'); print('❌ Error: $e');
} }
} }
Future<void> login(BuildContext context,ref) async { Future<void> login(BuildContext context, ref) async {
final email = profileData?.email ?? 'surendarsuri3450@gmail.com'; final email = profileData?.email ?? 'surendarsuri3450@gmail.com';
final uuid = profileData?.uuid ?? 'eac45287-5e1b-4251-b338-6aa7f49464a0'; final uuid = profileData?.uuid ?? 'eac45287-5e1b-4251-b338-6aa7f49464a0';
await checkUserWithUhid( await checkUserWithUhid(
email: email ?? '', email: email ?? '',
uuid: uuid ?? '', uuid: uuid ?? '',
context: context, context: context,
userInfo:profileData, userInfo: profileData,
); );
authCode = null; authCode = null;
accessToken = null; accessToken = null;
@ -439,7 +463,7 @@ class LoginRoute extends HookConsumerWidget {
email: email ?? '', email: email ?? '',
uuid: uuid ?? '', uuid: uuid ?? '',
context: context, context: context,
userInfo:profileData, userInfo: profileData,
); );
} catch (e) { } catch (e) {
if (kDebugMode) { if (kDebugMode) {
@ -468,7 +492,7 @@ class LoginRoute extends HookConsumerWidget {
print('platform $platform'); print('platform $platform');
final authData = final authData =
await pb.collection('users').authWithOAuth2(platform, (url) async { await pb.collection('users').authWithOAuth2(platform, (url) async {
await launchUrl(url); await launchUrl(url);
}); });
// final authData = await pb.collection('users').authWithOAuth2( // final authData = await pb.collection('users').authWithOAuth2(
// platform, // 'google' or 'apple' // platform, // 'google' or 'apple'
@ -507,7 +531,6 @@ class LoginRoute extends HookConsumerWidget {
print("Email: ${isProfileComplete}"); print("Email: ${isProfileComplete}");
print("Email: ${loginCount}"); print("Email: ${loginCount}");
final session = await context.loaderWithErrorDialog( final session = await context.loaderWithErrorDialog(
() => ref () => ref
.read( .read(
@ -536,11 +559,10 @@ class LoginRoute extends HookConsumerWidget {
print("sessionTST - $session"); print("sessionTST - $session");
await loginCountApi(userId!); await loginCountApi(userId!);
await updateOAuthUserDetails(userId!, metaDetails, ref,token); await updateOAuthUserDetails(userId!, metaDetails, ref, token);
// await saveUserId(userId!, loginCount); // await saveUserId(userId!, loginCount);
await getDeviceToken(context, userId,ref,token); await getDeviceToken(context, userId, ref, token);
await initializeTheme(context, ref, userId); await initializeTheme(context, ref, userId);
// final se = _sessionEntityFromPocketbaseRecord(authData); // final se = _sessionEntityFromPocketbaseRecord(authData);
@ -603,7 +625,7 @@ class LoginRoute extends HookConsumerWidget {
} }
Future<void> updateOAuthUserDetails( Future<void> updateOAuthUserDetails(
String userId, metaDetails, WidgetRef ref,token) async { String userId, metaDetails, WidgetRef ref, token) async {
print('Update token : ${token}'); print('Update token : ${token}');
// final url = "${pb.baseUrl}api/collections/users/records/$userId"; // final url = "${pb.baseUrl}api/collections/users/records/$userId";
@ -644,7 +666,7 @@ class LoginRoute extends HookConsumerWidget {
"is_oauth_login": 1, "is_oauth_login": 1,
}, },
); );
print('User response $response'); print('User response $response');
// final response = await http.patch( // final response = await http.patch(
// Uri.parse(url), // Uri.parse(url),
// headers: { // headers: {
@ -749,7 +771,7 @@ print('User response $response');
// } // }
// } // }
Future<void> checkUserWithUhid({ Future<void> checkUserWithUhid1({
required String email, required String email,
required String uuid, required String uuid,
required BuildContext context, required BuildContext context,
@ -774,7 +796,7 @@ print('User response $response');
final data = jsonDecode(response.body); final data = jsonDecode(response.body);
print('✅ Success: $data'); print('✅ Success: $data');
// final Map<String, dynamic> userInfoDetails = userInfo; // final Map<String, dynamic> userInfoDetails = userInfo;
final Map<String, dynamic> userInfoDetails = { final Map<String, dynamic> userInfoDetails = {
"sub": "eac45287-5e1b-4251-b338-6aa7f49464a0", "sub": "eac45287-5e1b-4251-b338-6aa7f49464a0",
"fullnameAR": "سوريندر سوري", "fullnameAR": "سوريندر سوري",
"gender": "Male", "gender": "Male",
@ -793,7 +815,7 @@ print('User response $response');
"email": "surendarsuri30@gmail.com" "email": "surendarsuri30@gmail.com"
}; };
GoRouter.of(context).go('/fcscprofilelinking',extra: userInfoDetails); GoRouter.of(context).go('/fcscprofilelinking', extra: userInfoDetails);
} else { } else {
print('❌ Error: ${response.statusCode} - ${response.body}'); print('❌ Error: ${response.statusCode} - ${response.body}');
} }
@ -802,6 +824,17 @@ print('User response $response');
} }
} }
Future<void> checkUserWithUhid({
required String email,
required String uuid,
required BuildContext context,
required userInfo,
}) async {
print('checkUserWithUhid');
signInWithUAEPASS();
}
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
double screenHeight = MediaQuery.of(context).size.height; double screenHeight = MediaQuery.of(context).size.height;
@ -1197,7 +1230,8 @@ print('User response $response');
} }
await saveUserId(userId, loginCount); await saveUserId(userId, loginCount);
await getDeviceToken(context, userId, ref, PocketBaseService.authStore.token); await getDeviceToken(
context, userId, ref, PocketBaseService.authStore.token);
await initializeTheme(context, ref, userId); await initializeTheme(context, ref, userId);
} }
try { try {
@ -1289,9 +1323,10 @@ print('User response $response');
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
if(cameFromLinking)...[ if (cameFromLinking) ...[
Text( Text(
context.translate('Link account with UAE PASS','ربط الحساب مع بطاقة الهوية الإماراتية'), context.translate('Link account with UAE PASS',
'ربط الحساب مع بطاقة الهوية الإماراتية'),
style: TextStyle( style: TextStyle(
fontFamily: context.translate( fontFamily: context.translate(
'Roboto', 'Roboto',
@ -1319,7 +1354,6 @@ print('User response $response');
), ),
), ),
], ],
6.horizontalSpace, 6.horizontalSpace,
Icon( Icon(
Icons.chevron_right_outlined, Icons.chevron_right_outlined,
@ -1434,7 +1468,7 @@ print('User response $response');
child: forgotPwBtn, child: forgotPwBtn,
), ),
), ),
if(cameFromLinking)...[ if (cameFromLinking) ...[
11.verticalSpace, 11.verticalSpace,
dontHaveAnAccountRegisterBtn, dontHaveAnAccountRegisterBtn,
], ],
@ -1445,7 +1479,7 @@ print('User response $response');
); );
final helloAndPleaseLoginTexts = Column( final helloAndPleaseLoginTexts = Column(
children: [ children: [
if(cameFromLinking)...[ if (cameFromLinking) ...[
SizedBox(height: screenHeight / 60), SizedBox(height: screenHeight / 60),
// Title // Title
Text( Text(
@ -1454,9 +1488,7 @@ print('User response $response');
style: TextStyle( style: TextStyle(
fontSize: isTablet ? 36 : 30, fontSize: isTablet ? 36 : 30,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: isDarkTheme color: isDarkTheme ? Color(0xFFFFFFFF) : Color(0xFF000000),
? Color(0xFFFFFFFF)
: Color(0xFF000000),
), ),
), ),
SizedBox( SizedBox(
@ -1470,9 +1502,7 @@ print('User response $response');
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontSize: isTablet ? 18 : 16, fontSize: isTablet ? 18 : 16,
color: isDarkTheme color: isDarkTheme ? Color(0xFFFFFFFF) : Color(0xFF000000),
? Color(0xFFFFFFFF)
: Color(0xFF000000),
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
), ),
), ),
@ -1685,55 +1715,53 @@ print('User response $response');
), ),
); );
final signInWithUAEPassBtn = Padding( final signInWithUAEPassBtn = Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(left: 60, right: 60),
left: 60,
right: 60),
child: SizedBox( child: SizedBox(
width: double.infinity, width: double.infinity,
child: OutlinedButton( child: OutlinedButton(
onPressed: () async { onPressed: () async {
// signInWithUAEPASS(); // signInWithUAEPASS();
login(context, ref); login(context, ref);
// Navigator.push( // Navigator.push(
// context, // context,
// MaterialPageRoute(builder: (context) => const UAEPassSignInPage()), // MaterialPageRoute(builder: (context) => const UAEPassSignInPage()),
// ); // );
}, },
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
side: const BorderSide(color: Color(0xFF000000)), side: const BorderSide(color: Color(0xFF000000)),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
padding: const EdgeInsets.symmetric(vertical: 16.5), padding: const EdgeInsets.symmetric(vertical: 16.5),
backgroundColor: Colors.white, backgroundColor: Colors.white,
foregroundColor: Colors.black, foregroundColor: Colors.black,
textStyle: TextStyle( textStyle: TextStyle(
fontFamily: context.translate('Roboto', 'NotoKufi'), fontFamily: context.translate('Roboto', 'NotoKufi'),
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
MiscIconAssetPath
.signInWithUAEPass, // replace with your Google icon asset
height: 20,
),
const SizedBox(width: 10),
Text(
'Sign in with UAE PASS',
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFF000000),
fontWeight: FontWeight.w600,
), ),
), ),
], child: Row(
), mainAxisAlignment: MainAxisAlignment.center,
), children: [
)); Image.asset(
MiscIconAssetPath
.signInWithUAEPass, // replace with your Google icon asset
height: 20,
),
const SizedBox(width: 10),
Text(
'Sign in with UAE PASS',
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFF000000),
fontWeight: FontWeight.w600,
),
),
],
),
),
));
final fcscBanner = Padding( final fcscBanner = Padding(
padding: const EdgeInsets.symmetric(horizontal: 50), padding: const EdgeInsets.symmetric(horizontal: 50),
child: Image.asset( child: Image.asset(
@ -1783,23 +1811,24 @@ print('User response $response');
), ),
20.verticalSpace, 20.verticalSpace,
helloAndPleaseLoginTexts, helloAndPleaseLoginTexts,
if(cameFromLinking)...[ if (cameFromLinking) ...[
15.verticalSpace, 15.verticalSpace,
] ] else ...[
else...[
40.verticalSpace, 40.verticalSpace,
], ],
form, form,
if(cameFromLinking)...[ if (cameFromLinking) ...[
25.verticalSpace, 25.verticalSpace,
Padding( Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: isTablet ? 80 : 24, // responsive horizontal space horizontal:
isTablet ? 80 : 24, // responsive horizontal space
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('Note: ', Text(
'Note: ',
style: TextStyle( style: TextStyle(
fontSize: isTablet ? 16 : 14, fontSize: isTablet ? 16 : 14,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
@ -1819,10 +1848,9 @@ print('User response $response');
), ),
), ),
], ],
) )),
),
30.verticalSpace, 30.verticalSpace,
] else...[ ] else ...[
25.verticalSpace, 25.verticalSpace,
dontHaveAnAccountRegisterBtn, dontHaveAnAccountRegisterBtn,
// continueAsGuestBtn, // continueAsGuestBtn,
@ -1859,9 +1887,10 @@ print('User response $response');
}, },
child: Scaffold( child: Scaffold(
backgroundColor: isDarkTheme ? Color(0xFF000000) : Colors.white, backgroundColor: isDarkTheme ? Color(0xFF000000) : Colors.white,
body: SafeArea(child: isInit.value body: SafeArea(
? scaffoldBody child: isInit.value
: const CircularProgressIndicator()), ? scaffoldBody
: const CircularProgressIndicator()),
), ),
); );
} }

View File

@ -1,365 +1,365 @@
import 'package:external_repos/external_repos.dart'; // import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart'; // import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; // import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart'; // import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; // import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:the_validator/the_validator.dart'; // import 'package:the_validator/the_validator.dart';
//
import 'package:uae_stat/config/my_theme.dart'; // import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/auth_use_case.dart'; // import 'package:uae_stat/domain/use_cases/auth_use_case.dart';
import 'package:uae_stat/domain/use_cases/language.dart'; // import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart'; // import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/misc_icon_asset_path.dart'; // import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/misc_icon_asset_path.dart';
import 'package:uae_stat/presentation/components/dialogs.dart'; // import 'package:uae_stat/presentation/components/dialogs.dart';
import 'package:uae_stat/presentation/components/lang_toggle.dart'; // import 'package:uae_stat/presentation/components/lang_toggle.dart';
import 'package:uae_stat/presentation/components/my_bottom_nav_bar.dart'; // import 'package:uae_stat/presentation/components/my_bottom_nav_bar.dart';
import 'package:uae_stat/presentation/components/space.dart'; // import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/components/themed_text_field.dart'; // import 'package:uae_stat/presentation/components/themed_text_field.dart';
//
class RegisterRoute extends HookConsumerWidget { // class RegisterRoute extends HookConsumerWidget {
const RegisterRoute({super.key}); // const RegisterRoute({super.key});
//
static final formKey = GlobalKey<FormState>(); // static final formKey = GlobalKey<FormState>();
//
@override // @override
Widget build(BuildContext context, WidgetRef ref) { // Widget build(BuildContext context, WidgetRef ref) {
final nameCtl = useTextEditingController(); // final nameCtl = useTextEditingController();
final emailCtl = useTextEditingController(); // final emailCtl = useTextEditingController();
final pwCtl = useTextEditingController(); // final pwCtl = useTextEditingController();
final cpwCtl = useTextEditingController(); // final cpwCtl = useTextEditingController();
final registerBtn = SizedBox( // final registerBtn = SizedBox(
width: double.infinity, // width: double.infinity,
child: ElevatedButton( // child: ElevatedButton(
onPressed: () async { // onPressed: () async {
final isValid = formKey.currentState!.validate(); // final isValid = formKey.currentState!.validate();
if (!isValid) return; // if (!isValid) return;
await context.loaderWithErrorDialog( // await context.loaderWithErrorDialog(
() => ref // () => ref
.read( // .read(
authUseCaseProvider.notifier, // authUseCaseProvider.notifier,
) // )
.register( // .register(
name: nameCtl.text, // name: nameCtl.text,
email: emailCtl.text, // email: emailCtl.text,
pw: pwCtl.text, // pw: pwCtl.text,
), // ),
errorDialogBuilder: ( // errorDialogBuilder: (
error, [ // error, [
StackTrace? stackTrace, // StackTrace? stackTrace,
]) { // ]) {
if (error == RegisterError.emailAlreadyInUse) { // if (error == RegisterError.emailAlreadyInUse) {
return context.simpleDialog( // return context.simpleDialog(
title: context.translate( // title: context.translate(
'Email already exists', // 'Email already exists',
'البريد الالكتروني موجود بالفعل', // 'البريد الالكتروني موجود بالفعل',
), // ),
content: context.translate( // content: context.translate(
'Choose another email address or use forgot my password.', // 'Choose another email address or use forgot my password.',
'اختر عنوان بريد إلكتروني آخر أو استخدم نسيت كلمة المرور الخاصة بي.', // 'اختر عنوان بريد إلكتروني آخر أو استخدم نسيت كلمة المرور الخاصة بي.',
), // ),
extraAction: ElevatedButton( // extraAction: ElevatedButton(
onPressed: () async { // onPressed: () async {
Navigator.of( // Navigator.of(
context, // context,
rootNavigator: true, // rootNavigator: true,
).pop(); // ).pop();
await context.loaderWithErrorDialog( // await context.loaderWithErrorDialog(
() => ref // () => ref
.read(authUseCaseProvider.notifier) // .read(authUseCaseProvider.notifier)
.requestPwReset(emailCtl.text), // .requestPwReset(emailCtl.text),
); // );
if (!context.mounted) return; // if (!context.mounted) return;
context.simpleDialog( // context.simpleDialog(
title: 'Success', // title: 'Success',
content: // content:
'An email has been sent to ${emailCtl.text} with further details.', // 'An email has been sent to ${emailCtl.text} with further details.',
); // );
}, // },
child: Text( // child: Text(
context.translate( // context.translate(
'Reset Password for ${emailCtl.text}', // 'Reset Password for ${emailCtl.text}',
'إعادة تعيين كلمة المرور ل ${emailCtl.text}', // 'إعادة تعيين كلمة المرور ل ${emailCtl.text}',
), // ),
), // ),
), // ),
); // );
} // }
return context.simpleDialog( // return context.simpleDialog(
error: error, // error: error,
stackTrace: stackTrace, // stackTrace: stackTrace,
); // );
}, // },
); // );
if (!context.mounted) return; // if (!context.mounted) return;
await context.simpleDialog( // await context.simpleDialog(
title: context.translate( // title: context.translate(
'Registration succeeded', // 'Registration succeeded',
'نجح التسجيل', // 'نجح التسجيل',
), // ),
content: context.translate( // content: context.translate(
'Check your inbox for an email verification link before logging in.', // 'Check your inbox for an email verification link before logging in.',
'تحقق من صندوق الوارد الخاص بك للحصول على رابط التحقق من البريد الإلكتروني قبل تسجيل الدخول.', // 'تحقق من صندوق الوارد الخاص بك للحصول على رابط التحقق من البريد الإلكتروني قبل تسجيل الدخول.',
), // ),
); // );
if (!context.mounted) return; // if (!context.mounted) return;
context.go('/${context.language}/login'); // context.go('/${context.language}/login');
}, // },
style: ButtonStyle( // style: ButtonStyle(
shape: WidgetStatePropertyAll( // shape: WidgetStatePropertyAll(
RoundedRectangleBorder( // RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), // borderRadius: BorderRadius.circular(10),
), // ),
), // ),
padding: const WidgetStatePropertyAll( // padding: const WidgetStatePropertyAll(
EdgeInsets.symmetric(vertical: 10.5), // EdgeInsets.symmetric(vertical: 10.5),
), // ),
textStyle: WidgetStatePropertyAll( // textStyle: WidgetStatePropertyAll(
TextStyle( // TextStyle(
fontFamily: context.translate( // fontFamily: context.translate(
'Roboto', // 'Roboto',
'NotoKufi', // 'NotoKufi',
), // ),
fontSize: 16, // fontSize: 16,
fontWeight: FontWeight.w600, // fontWeight: FontWeight.w600,
), // ),
), // ),
backgroundColor: WidgetStatePropertyAll( // backgroundColor: WidgetStatePropertyAll(
MyTheme.topicColor(IndicatorTopic.social), // MyTheme.topicColor(IndicatorTopic.social),
), // ),
foregroundColor: const WidgetStatePropertyAll( // foregroundColor: const WidgetStatePropertyAll(
Colors.white, // Colors.white,
), // ),
), // ),
child: Row( // child: Row(
mainAxisAlignment: MainAxisAlignment.center, // mainAxisAlignment: MainAxisAlignment.center,
children: [ // children: [
Text( // Text(
context.translate( // context.translate(
'Register', // 'Register',
'يسجل', // 'يسجل',
), // ),
), // ),
6.horizontalSpace, // 6.horizontalSpace,
const Icon(Icons.chevron_right_outlined), // const Icon(Icons.chevron_right_outlined),
], // ],
), // ),
), // ),
); // );
final pwField = ThemedFormField( // final pwField = ThemedFormField(
validator: (text) { // validator: (text) {
if (text!.length < 10) { // if (text!.length < 10) {
return 'The password must be at least 10 characters'; // return 'The password must be at least 10 characters';
} // }
return FieldValidator.password(minLength: 10)(text); // return FieldValidator.password(minLength: 10)(text);
}, // },
hintText: context.translate( // hintText: context.translate(
'Password', // 'Password',
'كلمة المرور', // 'كلمة المرور',
), // ),
imgPath: MiscIconAssetPath.personLight, // imgPath: MiscIconAssetPath.personLight,
controller: pwCtl, // controller: pwCtl,
isObscurable: true, // isObscurable: true,
); // );
final cpwField = ThemedFormField( // final cpwField = ThemedFormField(
validator: (text) => // validator: (text) =>
text != pwCtl.text ? 'The passwords do not match' : null, // text != pwCtl.text ? 'The passwords do not match' : null,
hintText: context.translate( // hintText: context.translate(
'Confirm Password', // 'Confirm Password',
'كلمة المرور', // 'كلمة المرور',
), // ),
imgPath: MiscIconAssetPath.personLight, // imgPath: MiscIconAssetPath.personLight,
controller: cpwCtl, // controller: cpwCtl,
isObscurable: true, // isObscurable: true,
); // );
final emailField = ThemedFormField( // final emailField = ThemedFormField(
hintText: context.translate( // hintText: context.translate(
'Email', // 'Email',
'اسم المستخدم', // 'اسم المستخدم',
), // ),
validator: FieldValidator.email(), // validator: FieldValidator.email(),
imgPath: MiscIconAssetPath.personLight, // imgPath: MiscIconAssetPath.personLight,
controller: emailCtl, // controller: emailCtl,
); // );
final nameField = ThemedFormField( // final nameField = ThemedFormField(
hintText: context.translate( // hintText: context.translate(
'Name', // 'Name',
'اسم', // 'اسم',
), // ),
validator: (value) => // validator: (value) =>
value!.isEmpty ? 'Name must be at least one character' : null, // value!.isEmpty ? 'Name must be at least one character' : null,
imgPath: MiscIconAssetPath.personLight, // imgPath: MiscIconAssetPath.personLight,
controller: nameCtl, // controller: nameCtl,
); // );
final form = Form( // final form = Form(
key: formKey, // key: formKey,
child: Column( // child: Column(
children: [ // children: [
nameField, // nameField,
15.verticalSpace, // 15.verticalSpace,
emailField, // emailField,
15.verticalSpace, // 15.verticalSpace,
pwField, // pwField,
15.verticalSpace, // 15.verticalSpace,
cpwField, // cpwField,
10.verticalSpace, // 10.verticalSpace,
registerBtn, // registerBtn,
], // ],
), // ),
); // );
final helloAndPleaseLoginTexts = Column( // final helloAndPleaseLoginTexts = Column(
children: [ // children: [
Text( // Text(
context.translate( // context.translate(
'Welcome to UAE Stats!', // 'Welcome to UAE Stats!',
'مرحبا بكم في إحصائيات دولة الإمارات العربية المتحدة!', // 'مرحبا بكم في إحصائيات دولة الإمارات العربية المتحدة!',
), // ),
textAlign: TextAlign.center, // textAlign: TextAlign.center,
style: TextStyle( // style: TextStyle(
fontFamily: context.translate( // fontFamily: context.translate(
'Roboto', // 'Roboto',
'NotoKufi', // 'NotoKufi',
), // ),
fontSize: 40, // fontSize: 40,
fontWeight: FontWeight.w300, // fontWeight: FontWeight.w300,
), // ),
), // ),
10.verticalSpace, // 10.verticalSpace,
Text( // Text(
context.translate( // context.translate(
'Register as a new user', // 'Register as a new user',
'سجل كمستخدم جديد', // 'سجل كمستخدم جديد',
), // ),
textAlign: TextAlign.center, // textAlign: TextAlign.center,
style: TextStyle( // style: TextStyle(
fontFamily: context.translate( // fontFamily: context.translate(
'Roboto', // 'Roboto',
'NotoKufi', // 'NotoKufi',
), // ),
fontSize: 18, // fontSize: 18,
color: const Color(0xff898C81), // color: const Color(0xff898C81),
fontWeight: FontWeight.bold, // fontWeight: FontWeight.bold,
), // ),
), // ),
], // ],
); // );
final alreadyHaveAnAccountLoginBtn = TextButton( // final alreadyHaveAnAccountLoginBtn = TextButton(
onPressed: () => context.go('/${context.language}/login'), // onPressed: () => context.go('/${context.language}/login'),
child: Text.rich( // child: Text.rich(
textAlign: TextAlign.center, // textAlign: TextAlign.center,
TextSpan( // TextSpan(
style: TextStyle( // style: TextStyle(
fontFamily: context.translate( // fontFamily: context.translate(
'Roboto', // 'Roboto',
'NotoKufi', // 'NotoKufi',
), // ),
fontSize: 18, // fontSize: 18,
fontWeight: FontWeight.bold, // fontWeight: FontWeight.bold,
), // ),
children: [ // children: [
TextSpan( // TextSpan(
text: context.translate( // text: context.translate(
'Already have an account?', // 'Already have an account?',
'هل لديك حساب؟', // 'هل لديك حساب؟',
), // ),
style: const TextStyle( // style: const TextStyle(
color: Color(0xff898C81), // color: Color(0xff898C81),
), // ),
), // ),
const TextSpan( // const TextSpan(
text: ' ', // text: ' ',
), // ),
TextSpan( // TextSpan(
text: context.translate( // text: context.translate(
'Login', // 'Login',
'تسجيل الدخول', // 'تسجيل الدخول',
), // ),
style: TextStyle( // style: TextStyle(
color: MyTheme.topicColor(IndicatorTopic.economy).shade600, // color: MyTheme.topicColor(IndicatorTopic.economy).shade600,
), // ),
), // ),
], // ],
), // ),
), // ),
); // );
final continueAsGuestBtn = SizedBox( // final continueAsGuestBtn = SizedBox(
width: double.infinity, // width: double.infinity,
child: ElevatedButton( // child: ElevatedButton(
onPressed: () => context // onPressed: () => context
.go('/${context.language}/${BottomNavBarItem.home.routePath}'), // .go('/${context.language}/${BottomNavBarItem.home.routePath}'),
style: ButtonStyle( // style: ButtonStyle(
shape: WidgetStatePropertyAll( // shape: WidgetStatePropertyAll(
RoundedRectangleBorder( // RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), // borderRadius: BorderRadius.circular(10),
), // ),
), // ),
padding: const WidgetStatePropertyAll( // padding: const WidgetStatePropertyAll(
EdgeInsets.symmetric(vertical: 10.5), // EdgeInsets.symmetric(vertical: 10.5),
), // ),
textStyle: WidgetStatePropertyAll( // textStyle: WidgetStatePropertyAll(
TextStyle( // TextStyle(
fontFamily: context.translate( // fontFamily: context.translate(
'Roboto', // 'Roboto',
'NotoKufi', // 'NotoKufi',
), // ),
fontSize: 16, // fontSize: 16,
fontWeight: FontWeight.w600, // fontWeight: FontWeight.w600,
), // ),
), // ),
backgroundColor: WidgetStatePropertyAll( // backgroundColor: WidgetStatePropertyAll(
MyTheme.topicColor(IndicatorTopic.environment), // MyTheme.topicColor(IndicatorTopic.environment),
), // ),
foregroundColor: const WidgetStatePropertyAll( // foregroundColor: const WidgetStatePropertyAll(
Colors.white, // Colors.white,
), // ),
), // ),
child: Row( // child: Row(
mainAxisAlignment: MainAxisAlignment.center, // mainAxisAlignment: MainAxisAlignment.center,
children: [ // children: [
Text( // Text(
context.translate( // context.translate(
'Continue as Guest', // 'Continue as Guest',
'استمر كضيف', // 'استمر كضيف',
), // ),
), // ),
6.horizontalSpace, // 6.horizontalSpace,
const Icon(Icons.chevron_right_outlined), // const Icon(Icons.chevron_right_outlined),
], // ],
), // ),
), // ),
); // );
final fcscBanner = Image.asset( // final fcscBanner = Image.asset(
BannerAssetPath.fcscLogoLight, // BannerAssetPath.fcscLogoLight,
height: 56, // height: 56,
); // );
final screenWidth = MediaQuery.of(context).size.width; // final screenWidth = MediaQuery.of(context).size.width;
final listViewHorizontalPadding = // final listViewHorizontalPadding =
screenWidth < 700 ? 30 : 30 + (screenWidth - 700) / 2; // screenWidth < 700 ? 30 : 30 + (screenWidth - 700) / 2;
final scaffoldBody = ListView( // final scaffoldBody = ListView(
padding: EdgeInsets.symmetric( // padding: EdgeInsets.symmetric(
horizontal: listViewHorizontalPadding.toDouble(), // horizontal: listViewHorizontalPadding.toDouble(),
), // ),
children: [ // children: [
36.verticalSpace, // 36.verticalSpace,
const Align( // const Align(
alignment: AlignmentDirectional.topEnd, // alignment: AlignmentDirectional.topEnd,
child: LangToggle(), // child: LangToggle(),
), // ),
16.verticalSpace, // 16.verticalSpace,
helloAndPleaseLoginTexts, // helloAndPleaseLoginTexts,
42.verticalSpace, // 42.verticalSpace,
form, // form,
20.verticalSpace, // 20.verticalSpace,
alreadyHaveAnAccountLoginBtn, // alreadyHaveAnAccountLoginBtn,
36.verticalSpace, // 36.verticalSpace,
continueAsGuestBtn, // continueAsGuestBtn,
72.verticalSpace, // 72.verticalSpace,
fcscBanner, // fcscBanner,
], // ],
); // );
final bgScaffold = Scaffold( // final bgScaffold = Scaffold(
backgroundColor: Colors.white, // backgroundColor: Colors.white,
body: SafeArea(child: scaffoldBody), // body: SafeArea(child: scaffoldBody),
); // );
return bgScaffold; // return bgScaffold;
} // }
} // }

View File

@ -401,14 +401,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.11" version: "0.7.11"
desktop_webview_window:
dependency: transitive
description:
name: desktop_webview_window
sha256: "57cf20d81689d5cbb1adfd0017e96b669398a669d927906073b0e42fc64111c0"
url: "https://pub.dev"
source: hosted
version: "0.2.3"
device_info_plus: device_info_plus:
dependency: "direct main" dependency: "direct main"
description: description:
@ -759,22 +751,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
flutter_web_auth_2:
dependency: "direct main"
description:
name: flutter_web_auth_2
sha256: "3c14babeaa066c371f3a743f204dd0d348b7d42ffa6fae7a9847a521aff33696"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
flutter_web_auth_2_platform_interface:
dependency: transitive
description:
name: flutter_web_auth_2_platform_interface
sha256: c63a472c8070998e4e422f6b34a17070e60782ac442107c70000dd1bed645f4d
url: "https://pub.dev"
source: hosted
version: "4.1.0"
flutter_web_plugins: flutter_web_plugins:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -1104,26 +1080,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.0.1" version: "10.0.9"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_flutter_testing name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.10" version: "3.0.9"
leak_tracker_testing: leak_tracker_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_testing name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.2" version: "3.0.1"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -1635,10 +1611,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.6" version: "0.7.4"
the_validator: the_validator:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1779,10 +1755,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.1.4"
video_player: video_player:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1872,13 +1848,13 @@ packages:
source: hosted source: hosted
version: "4.13.0" version: "4.13.0"
webview_flutter_android: webview_flutter_android:
dependency: "direct main" dependency: transitive
description: description:
name: webview_flutter_android name: webview_flutter_android
sha256: e5201c620eb2637dca88a756961fae4a7191bb30b4f2271e08b746405ffdf3fd sha256: "9a25f6b4313978ba1c2cda03a242eea17848174912cfb4d2d8ee84a556f248e3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.10.5" version: "4.10.1"
webview_flutter_platform_interface: webview_flutter_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -1888,13 +1864,13 @@ packages:
source: hosted source: hosted
version: "2.14.0" version: "2.14.0"
webview_flutter_wkwebview: webview_flutter_wkwebview:
dependency: "direct main" dependency: transitive
description: description:
name: webview_flutter_wkwebview name: webview_flutter_wkwebview
sha256: fea63576b3b7e02b2df8b78ba92b48ed66caec2bb041e9a0b1cbd586d5d80bfd sha256: fb46db8216131a3e55bcf44040ca808423539bc6732e7ed34fb6d8044e3d512f
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.23.1" version: "3.23.0"
win32: win32:
dependency: transitive dependency: transitive
description: description:
@ -1911,14 +1887,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.0" version: "2.1.0"
window_to_front:
dependency: transitive
description:
name: window_to_front
sha256: "7aef379752b7190c10479e12b5fd7c0b9d92adc96817d9e96c59937929512aee"
url: "https://pub.dev"
source: hosted
version: "0.0.3"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -1944,5 +1912,5 @@ packages:
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
sdks: sdks:
dart: ">=3.9.0 <4.0.0" dart: ">=3.7.0 <4.0.0"
flutter: ">=3.35.0" flutter: ">=3.29.0"