I'm facing issues with the phone authentication process in my Flutter app. Despite following the phone auth documentation, configuring SHA-1 and SHA-256 fingerprints, and enabling phone auth in Firebase, I'm not receiving the SMS message containing the OTP.
Here is my code :
-> Login screen
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:google_maps/bussiness_logic/cubit/phone_auth_cubit/phone_auth_cubit.dart';
import 'package:google_maps/constants/strings.dart';
import 'package:google_maps/functions.dart';
import 'package:google_maps/presentation/widgets/intro_text.dart';
import 'package:google_maps/presentation/widgets/custom_button.dart';
import 'package:google_maps/presentation/widgets/phone_form_field.dart';
class LoginScreenBody extends StatelessWidget {
final GlobalKey<FormState> _phoneFormKey = GlobalKey<FormState>();
TextEditingController phoneFieldController = TextEditingController();
LoginScreenBody({
super.key,
});
PhoneAuthCubit phoneAuthCubit = PhoneAuthCubit();
@override
Widget build(BuildContext context) {
return BlocProvider<PhoneAuthCubit>(
create: (context) => phoneAuthCubit,
child: BlocListener<PhoneAuthCubit, PhoneAuthState>(
listenWhen: (previous, current) => previous != current,
listener: (context, state) {
if (state is LoadingState) {
showProgressIndicator(context);
} else if (state is ErrorState) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(state.errorMsg),
backgroundColor: Colors.black,
duration: const Duration(seconds: 15),
));
} else if (state is PhoneNumberSubmitedState) {
Navigator.pop(context);
GoRouter.of(context)
.push(Strings.kOtpView, extra: phoneFieldController.text);
} else if (state is ExitLoadingState) {
Navigator.pop(context);
}
},
child: Form(
key: _phoneFormKey,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const IntroText(),
const SizedBox(
height: 90,
),
PhoneFormField(
phoneFieldController: phoneFieldController,
),
const SizedBox(
height: 60,
),
CustomButton(
buttonTextValue: "Next",
onPressedAction: () {
phoneAuthCubit.tapNextButton(
context,
_phoneFormKey,
phoneFieldController,
);
},
),
],
),
),
),
),
);
}
}
-> OTP screen
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:google_maps/bussiness_logic/cubit/phone_auth_cubit/phone_auth_cubit.dart';
import 'package:google_maps/constants/strings.dart';
import 'package:google_maps/functions.dart';
import 'package:google_maps/presentation/widgets/custom_button.dart';
import 'package:google_maps/presentation/widgets/otp_texts.dart';
import 'package:google_maps/presentation/widgets/pin_code_digits.dart';
class OtpScreenBody extends StatelessWidget {
OtpScreenBody({super.key, required this.phoneNumber});
PhoneAuthCubit phoneAuthCubit = PhoneAuthCubit();
final String phoneNumber;
late String otpCode;
@override
Widget build(BuildContext context) {
return BlocProvider<PhoneAuthCubit>(
create: (context) => phoneAuthCubit,
child: BlocListener<PhoneAuthCubit, PhoneAuthState>(
listenWhen: (previous, current) => previous != current,
listener: (context, state) {
if (state is LoadingState) {
showProgressIndicator(context);
} else if (state is ErrorState) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(state.errorMsg),
backgroundColor: Colors.black,
duration: const Duration(seconds: 5),
));
} else if (state is OtpVerifiedState) {
GoRouter.of(context).pushReplacementNamed(Strings.kMapScreen);
}
},
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
OtpTexts(
phoneNumber: phoneNumber,
),
const SizedBox(
height: 88,
),
PinCodeDigits(phoneAuthCubit: phoneAuthCubit),
const SizedBox(
height: 60,
),
CustomButton(
buttonTextValue: 'Submit',
onPressedAction: () {
phoneAuthCubit.tapSubmitButton();
},
)
]),
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:google_maps/constants/my_colors.dart';
class OtpTexts extends StatelessWidget {
const OtpTexts({super.key, required this.phoneNumber});
final String phoneNumber;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Verify your phone numberr',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
const SizedBox(
height: 20,
),
Container(
margin: const EdgeInsets.symmetric(horizontal: 2),
child: RichText(
text: TextSpan(
text: 'Enter your 6 digits code numbers sent to you at ',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Colors.black,
),
children: <TextSpan>[
TextSpan(
text: '+2$phoneNumber',
style:
const TextStyle(color: MyColors.blue, fontSize: 16))
]),
),
),
],
);
}
}
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
part 'phone_auth_state.dart';
class PhoneAuthCubit extends Cubit<PhoneAuthState> {
String verificationId = '';
late String otpCode;
final TextEditingController fieldOne = TextEditingController();
final TextEditingController fieldTwo = TextEditingController();
final TextEditingController fieldThree = TextEditingController();
final TextEditingController fieldFour = TextEditingController();
final TextEditingController fieldFive = TextEditingController();
final TextEditingController fieldSex = TextEditingController();
PhoneAuthCubit() : super(PhoneAuthInitial());
Future<void> submitPhoneNumber(String phoneNumber) async {
print(phoneNumber);
emit(LoadingState());
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: '+2$phoneNumber',
timeout: const Duration(seconds: 14),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout,
);
}
void verificationCompleted(PhoneAuthCredential credential) async {
print('verificationCompleted');
await signIn(credential);
}
void verificationFailed(FirebaseAuthException error) {
print('verificationFailed : ${error.toString()}');
emit(ErrorState(errorMsg: error.toString()));
}
void codeSent(String verificationId, int? resendToken) {
print('codeSent');
this.verificationId = verificationId;
emit(PhoneNumberSubmitedState());
}
void codeAutoRetrievalTimeout(String verificationId) {
print('codeAutoRetrievalTimeout');
}
Future<void> submitOTP(String otpCode) async {
PhoneAuthCredential credential = PhoneAuthProvider.credential(
verificationId: this.verificationId, smsCode: otpCode);
await signIn(credential);
}
Future<void> signIn(PhoneAuthCredential credential) async {
try {
await FirebaseAuth.instance.signInWithCredential(credential);
emit(OtpVerifiedState());
} catch (error) {
emit(ErrorState(errorMsg: error.toString()));
}
}
Future<void> logOut() async {
await FirebaseAuth.instance.signOut();
}
User getLoggedInUser() {
User firebaseUser = FirebaseAuth.instance.currentUser!;
return firebaseUser;
}
Future<void> tapNextButton(
BuildContext context,
GlobalKey<FormState> phoneFormKey,
TextEditingController phoneController) async {
emit(LoadingState());
await Future.delayed(const Duration(seconds: 1));
if (phoneFormKey.currentState!.validate()) {
phoneFormKey.currentState!.save();
submitPhoneNumber(phoneController.text);
emit(
PhoneNumberSubmitedState()); // fire bloc listener to handle that state
} else {
// entered unvalid phone number
emit(ExitLoadingState());
}
}
Future<void> tapSubmitButton() async {
emit(LoadingState());
if (fieldOne.text.isEmpty ||
fieldTwo.text.isEmpty ||
fieldThree.text.isEmpty ||
fieldFour.text.isEmpty ||
fieldFive.text.isEmpty ||
fieldSex.text.isEmpty) {
emit(ErrorState(errorMsg: "Please fill in all fields"));
return;
}
otpCode = fieldOne.text +
fieldTwo.text +
fieldThree.text +
fieldFour.text +
fieldFive.text +
fieldSex.text;
submitOTP(otpCode);
}
}`
loginBody --> where i enter the phone number .
log output:
Connecting to VM Service at ws://127.0.0.1:63570/wbNXq9n5oQ0=/ws
W/MIUIScout ANR( 5906): AnrScout only want msg within 20s, so stop here
D/MIUIScout ANR( 5906): get period history msg: (Current message:null)
D/MIUIScout ANR( 5906): get period history msg:execute time\>50ms (msgIndex=4seq=4 plan=١٣:٥٦:٥٤.١٦٥ late=53ms wall=118ms running=0ms h=android.app.ActivityThread$H w=159)
D/MIUIScout ANR( 5906): get period history msg:In recent 20s, total historyMsgCount=343
I/ple.google_map( 5906): Thread\[6,tid=5915,WaitingInMainSignalCatcherLoop,Thread\*=0xb400007cba832000,peer=0x12cc1768,"Signal Catcher"\]: reacting to signal 3
I/ple.google_map( 5906):
I/ple.google_map( 5906): Wrote stack traces to tombstoned
2
W/InputMethodManager( 5906): Ignoring showSoftInput() as view=io.flutter.embedding.android.FlutterView{26421c9 VFE...... .F....ID 0,0-1080,2270 #1} is not served.
D/DecorView\[\]( 5906): onWindowFocusChanged hasWindowFocus true
D/MIUIInput( 5906): \[KeyEvent\] ViewRootImpl KeyEvent { action=ACTION_UP, keyCode=KEYCODE_VOLUME_DOWN, scanCode=114, metaState=0, flags=0x8, repeatCount=0, eventTime=26582407, downTime=26582275, deviceId=2, source=0x101, displayId=-1 }
W/MIUIInput( 5906): AnrScout input event latency is 7119
W/MirrorManager( 5906): this model don't Support
D/InputMethodManager( 5906): showSoftInput() view=io.flutter.embedding.android.FlutterView{26421c9 VFE...... .F...... 0,0-1080,2270 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 5906): show(ime(), fromIme=true)
D/InputMethodManager( 5906): showSoftInput() view=io.flutter.embedding.android.FlutterView{26421c9 VFE...... .F...... 0,0-1080,2270 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 5906): show(ime(), fromIme=true)
D/InputMethodManager( 5906): showSoftInput() view=io.flutter.embedding.android.FlutterView{26421c9 VFE...... .F...... 0,0-1080,2270 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 5906): show(ime(), fromIme=true)
D/InputMethodManager( 5906): showSoftInput() view=io.flutter.embedding.android.FlutterView{26421c9 VFE...... .F...... 0,0-1080,2270 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 5906): show(ime(), fromIme=true)
D/InputMethodManager( 5906): showSoftInput() view=io.flutter.embedding.android.FlutterView{26421c9 VFE...... .F...... 0,0-1080,2270 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 5906): show(ime(), fromIme=true)
D/InputMethodManager( 5906): showSoftInput() view=io.flutter.embedding.android.FlutterView{26421c9 VFE...... .F...... 0,0-1080,2270 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 5906): show(ime(), fromIme=true)
D/DecorView\[\]( 5906): onWindowFocusChanged hasWindowFocus false
D/DecorView\[\]( 5906): onWindowFocusChanged hasWindowFocus true
D/InputMethodManager( 5906): showSoftInput() view=io.flutter.embedding.android.FlutterView{26421c9 VFE...... .F...... 0,0-1080,2270 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 5906): show(ime(), fromIme=true)
D/InputMethodManager( 5906): showSoftInput() view=io.flutter.embedding.android.FlutterView{26421c9 VFE...... .F...... 0,0-1080,2270 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 5906): show(ime(), fromIme=true)
D/InputMethodManager( 5906): showSoftInput() view=io.flutter.embedding.android.FlutterView{26421c9 VFE...... .F...... 0,0-1080,2270 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 5906): show(ime(), fromIme=true)
D/InputMethodManager( 5906): showSoftInput() view=io.flutter.embedding.android.FlutterView{26421c9 VFE...... .F...... 0,0-1080,2270 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 5906): show(ime(), fromIme=true)
D/InputMethodManager( 5906): showSoftInput() view=io.flutter.embedding.android.FlutterView{26421c9 VFE...... .F...... 0,0-1080,2270 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 5906): show(ime(), fromIme=true)
D/InputMethodManager( 5906): showSoftInput() view=io.flutter.embedding.android.FlutterView{26421c9 VFE...... .F...... 0,0-1080,2270 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 5906): show(ime(), fromIme=true)
I/flutter ( 5906): 01005355825
I/zzb ( 5906): ForceRecaptchaFlow from phoneAuthOptions = false, ForceRecaptchaFlow from firebaseSettings = false
W/System ( 5906): Ignoring header X-Firebase-Locale because its value was null.
2
D/InputMethodManager( 5906): showSoftInput() view=io.flutter.embedding.android.FlutterView{26421c9 VFE...... .F...... 0,0-1080,2270 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 5906): show(ime(), fromIme=true)
I/AssistStructure( 5906): Flattened final assist data: 392 bytes, containing 1 windows, 3 views
D/InsetsController( 5906): show(ime(), fromIme=true)
I/PlayCore( 5906): UID: \[10261\] PID: \[5906\] IntegrityService : requestIntegrityToken(IntegrityTokenRequest{nonce=7bXOSNhNlUgCWuHzy9mDBhQA9o0vfL5Z918aSxdmUDs, cloudProjectNumber=551503664846})
I/PlayCore( 5906): UID: \[10261\] PID: \[5906\] IntegrityService : Initiate binding to the service.
I/PlayCore( 5906): UID: \[10261\] PID: \[5906\] IntegrityService : ServiceConnectionImpl.onServiceConnected(ComponentInfo{com.android.vending/com.google.android.finsky.integrityservice.IntegrityService})
I/PlayCore( 5906): UID: \[10261\] PID: \[5906\] IntegrityService : linkToDeath
I/PlayCore( 5906): UID: \[10261\] PID: \[5906\] OnRequestIntegrityTokenCallback : onRequestIntegrityToken
I/PlayCore( 5906): UID: \[10261\] PID: \[5906\] IntegrityService : Unbind from service.
W/System ( 5906): Ignoring header X-Firebase-Locale because its value was null.
E/FirebaseAuth( 5906): \[SmsRetrieverHelper\] SMS verification code request failed: unknown status code: 18002 Invalid PlayIntegrity token; app not Recognized by Play Store.
D/FirebaseAuth( 5906): Re-triggering phone verification with Recaptcha flow forced for phone number +201005355825
I/zzb ( 5906): ForceRecaptchaFlow from phoneAuthOptions = true, ForceRecaptchaFlow from firebaseSettings = false
D/IS_CTS_MODE( 5906): false
D/MULTI_WINDOW_SWITCH_ENABLED( 5906): false
D/DecorView\[\]( 5906): getWindowModeFromSystem windowmode is 1
W/Activity( 5906): PerfMonitor: Slow Operation: Activity com.example.google_maps/com.google.firebase.auth.internal.RecaptchaActivity onResume took 666ms
W/System ( 5906): Ignoring header X-Firebase-Locale because its value was null.
W/Looper ( 5906): PerfMonitor looperActivity : package=com.example.google_maps/com.google.firebase.auth.internal.RecaptchaActivity time=0ms latency=687ms running=0ms procState=-1 historyMsgCount=2 (msgIndex=2 wall=685ms seq=767 late=2ms h=android.app.ActivityThread$H w=159)
D/DecorView\[\]( 5906): onWindowFocusChanged hasWindowFocus false
D/DecorView\[\]( 5906): onWindowFocusChanged hasWindowFocus true
E/FirebaseAuth( 5906): \[GetAuthDomainTask\] Error getting project config. Failed with INVALID_CERT_HASH 400
E/zzb ( 5906): Failed to get reCAPTCHA token with error \[There was an error while trying to get your package certificate hash.\]- calling backend without app verification
W/System ( 5906): Ignoring header X-Firebase-Locale because its value was null.
D/DecorView\[\]( 5906): onWindowFocusChanged hasWindowFocus false
D/DecorView\[\]( 5906): onWindowFocusChanged hasWindowFocus true
E/FirebaseAuth( 5906): \[SmsRetrieverHelper\] SMS verification code request failed: unknown status code: 17093 null
I/flutter ( 5906): verificationFailed : \[firebase_auth/missing-client-identifier\] This request is missing a valid app identifier, meaning that Play Integrity checks, and reCAPTCHA checks were unsuccessful. Please try again, or check the logcat for more details.
D/DecorView\[\]( 5906): onWindowFocusChanged hasWindowFocus false
E/BLASTBufferQueue( 5906): \[ViewRootImpl\[MainActivity\]#0\](f:0,a:2) Applying pending transactions on dtor 1
W/ple.google_map( 5906): Cleared Reference was only reachable from finalizer (only reported once)
W/JavaBinder( 5906): BinderProxy is being destroyed but the application did not call unlinkToDeath to unlink all of its death recipients beforehand. Releasing leaked death recipient: com.google.android.play.integrity.internal.n
I/BpBinder( 5906): onLastStrongRef automatically unlinking death recipients: \<uncached descriptor\>
W/System ( 5906): A resource failed to call end