Anonymous
Почему я не получаю автозаполнение otp?
Сообщение
Anonymous » 25 окт 2024, 14:18
В настоящее время я реализую автозаполнение SMS для получения одноразового пароля от Firebase, но пока безуспешно. В своей настройке я использую как Bloc, так и sms_autofill. Несмотря на использование различных подходов, OTP не заполняется автоматически, как ожидалось. Вот код, с которым я работаю:
Код: Выделить всё
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:pin_code_fields/pin_code_fields.dart';
import 'package:provider/provider.dart';
import 'package:responsive_sizer/responsive_sizer.dart';
import 'package:sms_autofill/sms_autofill.dart';
import 'package:timer_count_down/timer_count_down.dart';
import '../../application/auth/verify_otp/verify_otp_cubit.dart';
import '../../domain/core/configs/app_config.dart';
import '../../domain/core/configs/injection.dart';
import '../../domain/core/constants/asset_constants.dart';
import '../../domain/core/constants/string_constants.dart';
import '../../domain/core/services/analytics_service/analytics_service.dart';
import '../../domain/core/services/navigation_services/navigation_service.dart';
import '../../domain/core/services/navigation_services/routers/route_name.dart';
import '../../domain/core/services/storage_service/dynamic_link_storage_service.dart';
import '../../domain/core/utils/dynamic_link.dart';
import '../../infrastructure/core/enum/profile_state.enum.dart';
import '../widgets/rounded_arrow_button.dart';
import '../widgets/snackbar_alert.dart';
import 'login_with_phone_screen.dart';
class VerifyOtpScreen extends StatelessWidget {
final String verificationCode;
final String dialCode;
final String phoneNumber;
const VerifyOtpScreen({
super.key,
required this.verificationCode,
required this.dialCode,
required this.phoneNumber,
});
@override
Widget build(BuildContext context) {
final AppConfig appConfig = AppConfig.of(context)!;
return BlocProvider(
create: (context) => VerifyOtpCubit(VerifyOtpState.initial(
dialCode: dialCode,
phoneNumber: phoneNumber,
verificationCode: verificationCode,
apiBaseUrl: appConfig.serverUrl,
)),
child: const VerifyOtpScreenConsumer(),
);
}
}
class VerifyOtpScreenConsumer extends StatefulWidget {
const VerifyOtpScreenConsumer({super.key});
@override
State createState() =>
_VerifyOtpScreenConsumerState();
}
class _VerifyOtpScreenConsumerState extends State {
@override
void initState() {
super.initState();
_listenSmsCode();
}
@override
void dispose() {
SmsAutoFill().unregisterListener();
super.dispose();
}
_listenSmsCode() async {
await SmsAutoFill().listenForCode();
final String? appSignature = await SmsAutoFill().getAppSignature;
print('App Signature: $appSignature');
// Listen to the code stream
await SmsAutoFill().code.listen((code) {
print('Received SMS code: $code');
if (code != null && code.isNotEmpty) {
final RegExp regExp = RegExp(r'\d{6}');
final match = regExp.firstMatch(code);
if (match != null) {
final otp = match.group(0);
print('Extracted OTP: $otp');
// Update the OTP field with the extracted code
// You can call a method here to update the UI or state
_updateOtpField(otp!);
}
}
});
}
void _updateOtpField(String otp) {
context.read().updateOtp(otp);
}
@override
Widget build(BuildContext context) {
final ThemeData themeData = Theme.of(context);
return BlocConsumer(
listener: (context, state) {
if (state.backToAuth) {
navigator().goBack();
context.read().emitFromAnywhere(
state: state.copyWith(backToAuth: false),
);
}
// verify otp -> result - success
if (state.isOTPVerificationSuccessful &&
!state.isOTPVerificationFailed) {
// nav to home
print('navigating to home');
final user = state.user!;
Provider.of(context, listen: false)
.updateAfterAuthChange(
isAuthorized: true,
user: state.user,
);
DynamicLinkStorageService.getStoredDynamicLink().then((value) {
try {
Map pathSegments = value ?? {};
final profileState = user.fullName.isNotEmpty &&
user.dob.isNotEmpty &&
user.gender.isNotEmpty &&
user.tag != null
? ProfileStateEnum.completed
: user.fullName.isNotEmpty &&
user.dob.isNotEmpty &&
user.gender.isNotEmpty
? ProfileStateEnum.gender
: user.fullName.isNotEmpty && user.dob.isNotEmpty
? ProfileStateEnum.birthday
: user.fullName.isNotEmpty
? ProfileStateEnum.basic
: ProfileStateEnum.started;
final route = (profileState == ProfileStateEnum.completed)
? (value != null)
? DynamicLinkUtil.getDynamicRoute(
pathSegments['category']!, pathSegments['id']!)
: UserRoutes.mainNavRoute
: profileState == ProfileStateEnum.gender
? AuthRoutes.usernameRoute
: profileState == ProfileStateEnum.birthday
? AuthRoutes.genderRoute
: profileState == ProfileStateEnum.basic
? AuthRoutes.birthdayRoute
: AuthRoutes.basicInfoRoute;
AnalyticsService().setUserId(state.user!.id.toString());
AnalyticsService()
.setUserProperties(name: 'user_id', val: user.id.toString());
AnalyticsService().setUserProperties(
name: 'phone_no', val: user.phoneNumber.toString());
if (profileState == ProfileStateEnum.started) {
AnalyticsService().logEvent(eventName: 'sign_up', paras: {});
} else {
AnalyticsService().logEvent(eventName: 'login', paras: {});
}
print('route $route');
Future.delayed(const Duration(milliseconds: 100))
.then((value) async {
navigator()
.navigateTo(route, isClearStack: true, queryParams: {
'id': pathSegments['id'] ?? '0',
});
});
} catch (e) {
print('error $e');
}
});
context.read().emitFromAnywhere(
state: state.copyWith(isOTPVerificationSuccessful: false),
);
}
// verify otp -> result - failed
if (!state.isOTPVerificationSuccessful &&
state.isOTPVerificationFailed) {
CustomScaffoldMessenger.clearSnackBars(context);
CustomScaffoldMessenger.showSnackBar(
context,
message: state.errorMessage,
);
context.read().emitFromAnywhere(
state: state.copyWith(isOTPVerificationFailed: false),
);
}
// send otp -> result - success
if (state.isOTPSentSuccessful && !state.isOTPSentFailed) {
CustomScaffoldMessenger.clearSnackBars(context);
CustomScaffoldMessenger.showSnackBar(
context,
message: state.errorMessage,
);
context.read().emitFromAnywhere(
state: state.copyWith(isOTPSentSuccessful: false),
);
}
// send otp -> result - failed
if (!state.isOTPSentSuccessful && state.isOTPSentFailed) {
CustomScaffoldMessenger.clearSnackBars(context);
CustomScaffoldMessenger.showSnackBar(
context,
message: state.errorMessage,
);
context.read().emitFromAnywhere(
state: state.copyWith(isOTPSentFailed: false),
);
}
},
builder: (context, state) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(
children: [
OnboardingGradient(),
SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 2.h, vertical: 2.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 11.h,
),
Text(
LoginScreenConstants.verifyNumberHeader,
style: themeData.textTheme.bodyLarge!.copyWith(
fontSize: 24.sp,
fontWeight: FontWeight.w600,
color: themeData.colorScheme.background,
),
),
SizedBox(
height: 1.h,
),
Row(
children: [
Text(
'${LoginScreenConstants.verifyNumberDescription} ${state.dialCode} ${state.phoneNumber}',
style: themeData.textTheme.bodySmall!
.copyWith(fontWeight: FontWeight.w400),
),
SizedBox(
width: 2.w,
),
GestureDetector(
onTap: () {
context.read().onGoBack();
},
child: SvgPicture.asset(
AssetConstants.editIcon,
),
)
],
),
SizedBox(
height: 5.h,
),
PinCodeTextField(
controller: state.otpController,
appContext: context,
length: 6,
showCursor: false,
obscureText: false,
animationType: AnimationType.fade,
enableActiveFill: true,
enablePinAutofill: false,
hintCharacter: '-',
keyboardType: TextInputType.number,
hintStyle: themeData.textTheme.bodyLarge!.copyWith(
fontSize: 22.sp,
fontWeight: FontWeight.bold,
color: themeData.colorScheme.background,
),
textStyle: themeData.textTheme.bodyLarge!.copyWith(
fontSize: 20.sp,
fontWeight: FontWeight.w500,
color: themeData.scaffoldBackgroundColor,
),
pinTheme: PinTheme(
inactiveColor: themeData
.colorScheme.primaryContainer
.withOpacity(0.4),
borderWidth: 0,
inactiveBorderWidth: 0,
activeBorderWidth: 0,
shape: PinCodeFieldShape.box,
borderRadius: BorderRadius.circular(40),
fieldHeight: 8.h,
fieldWidth: 13.5.w,
selectedFillColor: themeData
.colorScheme.primaryContainer
.withOpacity(0.5),
inactiveFillColor: themeData
.colorScheme.primaryContainer
.withOpacity(0.5),
activeFillColor: themeData.colorScheme.background,
selectedBorderWidth: 0.6,
selectedColor: themeData.colorScheme.primary),
onChanged: (value) {
context.read().onOtpChange();
},
),
SizedBox(
height: 1.5.h,
),
state.showResendButton
? InkWell(
onTap: () {
context.read().resendOtp();
},
child: Text(
LoginScreenConstants.resendNow,
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
fontWeight: FontWeight.w600,
fontSize: 16.sp,
color: Theme.of(context)
.colorScheme
.background,
decoration: TextDecoration.underline,
decorationColor: Theme.of(context)
.colorScheme
.background,
),
),
)
: Countdown(
seconds: AppConstants.otpTimer,
build: (_, double time) {
return Text(
'${LoginScreenConstants.resendAfter}${time.toInt()}${AppConstants.seconds}',
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
decoration: TextDecoration.underline,
fontWeight: FontWeight.w600,
fontSize: 16.sp,
decorationColor: Theme.of(context)
.colorScheme
.onSecondary,
color: Theme.of(context)
.colorScheme
.onSecondary,
),
);
},
interval: const Duration(seconds: 1),
onFinished: () {
context
.read()
.coundownFinished();
},
),
const Spacer(),
Align(
alignment: Alignment.centerRight,
child: state.isLoading
? const CircularProgressIndicator()
: RoundedArrowButton(
height: 6.h,
width: 6.h,
contentIcon: AssetConstants.longArrowRight,
isEnabled: state.isVerifyEnabled,
onTap: () {
// if (!state.isLoginEnabled) {
context.read().verifyOtp();
// }
},
),
)
],
),
),
),
],
),
);
},
);
}
}
Не могли бы вы дать несколько советов о том, почему автозаполнение SMS может не работать или необходимо ли выполнить дополнительные действия для обеспечения функциональности?
Подробнее здесь:
https://stackoverflow.com/questions/791 ... autofilled
1729855124
Anonymous
В настоящее время я реализую автозаполнение SMS для получения одноразового пароля от Firebase, но пока безуспешно. В своей настройке я использую как Bloc, так и sms_autofill. Несмотря на использование различных подходов, OTP не заполняется автоматически, как ожидалось. Вот код, с которым я работаю: [code]import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; import 'package:pin_code_fields/pin_code_fields.dart'; import 'package:provider/provider.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:sms_autofill/sms_autofill.dart'; import 'package:timer_count_down/timer_count_down.dart'; import '../../application/auth/verify_otp/verify_otp_cubit.dart'; import '../../domain/core/configs/app_config.dart'; import '../../domain/core/configs/injection.dart'; import '../../domain/core/constants/asset_constants.dart'; import '../../domain/core/constants/string_constants.dart'; import '../../domain/core/services/analytics_service/analytics_service.dart'; import '../../domain/core/services/navigation_services/navigation_service.dart'; import '../../domain/core/services/navigation_services/routers/route_name.dart'; import '../../domain/core/services/storage_service/dynamic_link_storage_service.dart'; import '../../domain/core/utils/dynamic_link.dart'; import '../../infrastructure/core/enum/profile_state.enum.dart'; import '../widgets/rounded_arrow_button.dart'; import '../widgets/snackbar_alert.dart'; import 'login_with_phone_screen.dart'; class VerifyOtpScreen extends StatelessWidget { final String verificationCode; final String dialCode; final String phoneNumber; const VerifyOtpScreen({ super.key, required this.verificationCode, required this.dialCode, required this.phoneNumber, }); @override Widget build(BuildContext context) { final AppConfig appConfig = AppConfig.of(context)!; return BlocProvider( create: (context) => VerifyOtpCubit(VerifyOtpState.initial( dialCode: dialCode, phoneNumber: phoneNumber, verificationCode: verificationCode, apiBaseUrl: appConfig.serverUrl, )), child: const VerifyOtpScreenConsumer(), ); } } class VerifyOtpScreenConsumer extends StatefulWidget { const VerifyOtpScreenConsumer({super.key}); @override State createState() => _VerifyOtpScreenConsumerState(); } class _VerifyOtpScreenConsumerState extends State { @override void initState() { super.initState(); _listenSmsCode(); } @override void dispose() { SmsAutoFill().unregisterListener(); super.dispose(); } _listenSmsCode() async { await SmsAutoFill().listenForCode(); final String? appSignature = await SmsAutoFill().getAppSignature; print('App Signature: $appSignature'); // Listen to the code stream await SmsAutoFill().code.listen((code) { print('Received SMS code: $code'); if (code != null && code.isNotEmpty) { final RegExp regExp = RegExp(r'\d{6}'); final match = regExp.firstMatch(code); if (match != null) { final otp = match.group(0); print('Extracted OTP: $otp'); // Update the OTP field with the extracted code // You can call a method here to update the UI or state _updateOtpField(otp!); } } }); } void _updateOtpField(String otp) { context.read().updateOtp(otp); } @override Widget build(BuildContext context) { final ThemeData themeData = Theme.of(context); return BlocConsumer( listener: (context, state) { if (state.backToAuth) { navigator().goBack(); context.read().emitFromAnywhere( state: state.copyWith(backToAuth: false), ); } // verify otp -> result - success if (state.isOTPVerificationSuccessful && !state.isOTPVerificationFailed) { // nav to home print('navigating to home'); final user = state.user!; Provider.of(context, listen: false) .updateAfterAuthChange( isAuthorized: true, user: state.user, ); DynamicLinkStorageService.getStoredDynamicLink().then((value) { try { Map pathSegments = value ?? {}; final profileState = user.fullName.isNotEmpty && user.dob.isNotEmpty && user.gender.isNotEmpty && user.tag != null ? ProfileStateEnum.completed : user.fullName.isNotEmpty && user.dob.isNotEmpty && user.gender.isNotEmpty ? ProfileStateEnum.gender : user.fullName.isNotEmpty && user.dob.isNotEmpty ? ProfileStateEnum.birthday : user.fullName.isNotEmpty ? ProfileStateEnum.basic : ProfileStateEnum.started; final route = (profileState == ProfileStateEnum.completed) ? (value != null) ? DynamicLinkUtil.getDynamicRoute( pathSegments['category']!, pathSegments['id']!) : UserRoutes.mainNavRoute : profileState == ProfileStateEnum.gender ? AuthRoutes.usernameRoute : profileState == ProfileStateEnum.birthday ? AuthRoutes.genderRoute : profileState == ProfileStateEnum.basic ? AuthRoutes.birthdayRoute : AuthRoutes.basicInfoRoute; AnalyticsService().setUserId(state.user!.id.toString()); AnalyticsService() .setUserProperties(name: 'user_id', val: user.id.toString()); AnalyticsService().setUserProperties( name: 'phone_no', val: user.phoneNumber.toString()); if (profileState == ProfileStateEnum.started) { AnalyticsService().logEvent(eventName: 'sign_up', paras: {}); } else { AnalyticsService().logEvent(eventName: 'login', paras: {}); } print('route $route'); Future.delayed(const Duration(milliseconds: 100)) .then((value) async { navigator() .navigateTo(route, isClearStack: true, queryParams: { 'id': pathSegments['id'] ?? '0', }); }); } catch (e) { print('error $e'); } }); context.read().emitFromAnywhere( state: state.copyWith(isOTPVerificationSuccessful: false), ); } // verify otp -> result - failed if (!state.isOTPVerificationSuccessful && state.isOTPVerificationFailed) { CustomScaffoldMessenger.clearSnackBars(context); CustomScaffoldMessenger.showSnackBar( context, message: state.errorMessage, ); context.read().emitFromAnywhere( state: state.copyWith(isOTPVerificationFailed: false), ); } // send otp -> result - success if (state.isOTPSentSuccessful && !state.isOTPSentFailed) { CustomScaffoldMessenger.clearSnackBars(context); CustomScaffoldMessenger.showSnackBar( context, message: state.errorMessage, ); context.read().emitFromAnywhere( state: state.copyWith(isOTPSentSuccessful: false), ); } // send otp -> result - failed if (!state.isOTPSentSuccessful && state.isOTPSentFailed) { CustomScaffoldMessenger.clearSnackBars(context); CustomScaffoldMessenger.showSnackBar( context, message: state.errorMessage, ); context.read().emitFromAnywhere( state: state.copyWith(isOTPSentFailed: false), ); } }, builder: (context, state) { return Scaffold( resizeToAvoidBottomInset: false, body: Stack( children: [ OnboardingGradient(), SafeArea( child: Padding( padding: EdgeInsets.symmetric(horizontal: 2.h, vertical: 2.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 11.h, ), Text( LoginScreenConstants.verifyNumberHeader, style: themeData.textTheme.bodyLarge!.copyWith( fontSize: 24.sp, fontWeight: FontWeight.w600, color: themeData.colorScheme.background, ), ), SizedBox( height: 1.h, ), Row( children: [ Text( '${LoginScreenConstants.verifyNumberDescription} ${state.dialCode} ${state.phoneNumber}', style: themeData.textTheme.bodySmall! .copyWith(fontWeight: FontWeight.w400), ), SizedBox( width: 2.w, ), GestureDetector( onTap: () { context.read().onGoBack(); }, child: SvgPicture.asset( AssetConstants.editIcon, ), ) ], ), SizedBox( height: 5.h, ), PinCodeTextField( controller: state.otpController, appContext: context, length: 6, showCursor: false, obscureText: false, animationType: AnimationType.fade, enableActiveFill: true, enablePinAutofill: false, hintCharacter: '-', keyboardType: TextInputType.number, hintStyle: themeData.textTheme.bodyLarge!.copyWith( fontSize: 22.sp, fontWeight: FontWeight.bold, color: themeData.colorScheme.background, ), textStyle: themeData.textTheme.bodyLarge!.copyWith( fontSize: 20.sp, fontWeight: FontWeight.w500, color: themeData.scaffoldBackgroundColor, ), pinTheme: PinTheme( inactiveColor: themeData .colorScheme.primaryContainer .withOpacity(0.4), borderWidth: 0, inactiveBorderWidth: 0, activeBorderWidth: 0, shape: PinCodeFieldShape.box, borderRadius: BorderRadius.circular(40), fieldHeight: 8.h, fieldWidth: 13.5.w, selectedFillColor: themeData .colorScheme.primaryContainer .withOpacity(0.5), inactiveFillColor: themeData .colorScheme.primaryContainer .withOpacity(0.5), activeFillColor: themeData.colorScheme.background, selectedBorderWidth: 0.6, selectedColor: themeData.colorScheme.primary), onChanged: (value) { context.read().onOtpChange(); }, ), SizedBox( height: 1.5.h, ), state.showResendButton ? InkWell( onTap: () { context.read().resendOtp(); }, child: Text( LoginScreenConstants.resendNow, style: Theme.of(context) .textTheme .bodySmall! .copyWith( fontWeight: FontWeight.w600, fontSize: 16.sp, color: Theme.of(context) .colorScheme .background, decoration: TextDecoration.underline, decorationColor: Theme.of(context) .colorScheme .background, ), ), ) : Countdown( seconds: AppConstants.otpTimer, build: (_, double time) { return Text( '${LoginScreenConstants.resendAfter}${time.toInt()}${AppConstants.seconds}', style: Theme.of(context) .textTheme .bodySmall! .copyWith( decoration: TextDecoration.underline, fontWeight: FontWeight.w600, fontSize: 16.sp, decorationColor: Theme.of(context) .colorScheme .onSecondary, color: Theme.of(context) .colorScheme .onSecondary, ), ); }, interval: const Duration(seconds: 1), onFinished: () { context .read() .coundownFinished(); }, ), const Spacer(), Align( alignment: Alignment.centerRight, child: state.isLoading ? const CircularProgressIndicator() : RoundedArrowButton( height: 6.h, width: 6.h, contentIcon: AssetConstants.longArrowRight, isEnabled: state.isVerifyEnabled, onTap: () { // if (!state.isLoginEnabled) { context.read().verifyOtp(); // } }, ), ) ], ), ), ), ], ), ); }, ); } } [/code] Не могли бы вы дать несколько советов о том, почему автозаполнение SMS может не работать или необходимо ли выполнить дополнительные действия для обеспечения функциональности? Подробнее здесь: [url]https://stackoverflow.com/questions/79125438/why-im-not-getting-the-otp-autofilled[/url]