when I try to login it doesn't redirect me to my authenticated page(the state doesn't update) but it stays as it is, it redirects me only after I hot restart the app. I don't know what is wrong with my code.I control the user =null in landing page but landingPage cant return home page.
this is Landing Page
import '/material.dart';
import '/provider.dart';
import '/login-directory/home_page.dart';
import '/login-directory/sign_in_page.dart';
import '/model/user_model.dart' as usr;
import '/viewmodel/viewusermodel.dart';
class LandingPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
final _userModel = Provider.of<UserModel>(context,listen: false);
if (_userModel.state == ViewState.Idle) {
if (_userModel.user == null) {
return SignInPage();//user==null
} else {
return HomePage(user: _userModel.user as usr.User);
}
}
else {
return Scaffold(
body: Center(child: CircularProgressIndicator(),),
);
}
}
}
this is User Repository:
import '/locator.dart';
import '/model/user_model.dart';
import '/services/auth_base.dart';
import '/services/fake_auth_service.dart';
import '/services/firebase_auth_service.dart';
enum AppMode{DEBUG,RELEASE}
class UserRepository implements AuthBase {
FirebaseAuthService _firebaseAuthService = locator<FirebaseAuthService>();
final FakeAuthService _fakeAuthService = locator<FakeAuthService>();
AppMode appMode = AppMode.RELEASE;
#override
Future<User?> currentUser() async {
if (appMode == AppMode.DEBUG) {
return await _fakeAuthService.currentUser();
}
else {
return await _firebaseAuthService.currentUser();
}
}
#override
Future<bool> logOut() async {
if (appMode == AppMode.DEBUG) {
return await _fakeAuthService.logOut();
}
else {
return await _firebaseAuthService.logOut();
}
}
#override
Future<User?> singInAnonymously() async {
if (appMode == AppMode.DEBUG) {
return await _fakeAuthService.singInAnonymously();
}
else {
return await _firebaseAuthService.singInAnonymously();
}
}
#override
Future<User?> signInWithGoogle() async {
if (appMode == AppMode.DEBUG) {
return await _fakeAuthService.singInAnonymously();
}
else {
return await _firebaseAuthService.signInWithGoogle();
}
}
}
Authbase :
abstract class AuthBase{
Future<User?> currentUser();
Future<User?> singInAnonymously();
Future<bool> logOut();
Future<User?> signInWithGoogle();
}
ViewUserModel:
import 'package:flutter/material.dart';
import 'package:/locator.dart';
import 'package:/model/user_model.dart' as usr;
import 'package:/repository/user_repository.dart';
import 'package:/services/auth_base.dart';
enum ViewState{Idle,Busy}
class UserModel with ChangeNotifier implements AuthBase {
ViewState _state = ViewState.Idle;
final UserRepository _userRepository = locator<UserRepository>();
usr.User? _user;
usr.User? get user =>_user;
ViewState get state => _state;
set state(ViewState value) {
_state = value;
notifyListeners(); //
}
UserModel(){
currentUser();
}
#override
Future<usr.User?> currentUser() async {
try {
state = ViewState
.Busy; //kullanıcı verisi cekilirken ekran verisini guncelliyorum
_user = (await _userRepository.currentUser())!;
return _user;
} catch (e) {
debugPrint("ViewModel currentUser is error $e" );
return null;
} finally {
state = ViewState.Idle;
}
}
#override
Future<bool> logOut() async {
try {
state = ViewState.Busy;
bool result =await _userRepository.logOut();
_user= null;
return result;
} catch (e) {
debugPrint("ViewModel currentUser is error $e" );
return false;
} finally {
state = ViewState.Idle;
}
}
#override
Future<usr.User?> singInAnonymously() async {
try {
state = ViewState
.Busy;
_user = (await _userRepository.singInAnonymously())!;
return _user;
} catch (e) {
debugPrint("ViewModel currentUser is error $e");
return null;
} finally {
state = ViewState.Idle;
}
}
#override
Future<usr.User?> signInWithGoogle() async {
try {
state = ViewState
.Busy;
_user = (await _userRepository.signInWithGoogle())!;
return _user;
} catch (e) {
debugPrint("ViewModel currentUser is error $e");
return null;
} finally {
state = ViewState.Idle;
}
}
}
GetIt locator:
GetIt locator=GetIt.instance;
void setupLocator(){
locator.registerLazySingleton(() => FirebaseAuthService());
locator.registerLazySingleton(() => UserRepository());
}
this is main.dart:
import '/viewmodel/viewusermodel.dart';
void main() async{
WidgetsFlutterBinding.ensureInitialized();;
await Firebase.initializeApp();
setupLocator();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title:'Title',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch:Colors.purple,
),
home:ChangeNotifierProvider(
create:(context)=>UserModel(),
child: LandingPage()),
);
}
}
I have a problem, my database has data, but I can't list this data in the application, can you see where the problem is?
Here the database query is being implemented
#override
Stream<Either<TodoFailures, List<Todo>>> watchAll() async* {
//yield left(const InsufficientPermissions());
// users/{user ID}/notes/{todo ID}
final userDoc = await firestore.userDocument();
yield* userDoc.todoCollection
.snapshots()
.map((snapshot) => right<TodoFailures, List<Todo>>(snapshot.docs
.map((doc) => TodoModel.fromFirestore(doc).toDomain()).toList()))
.handleError((e) {
if (e is FirebaseException) {
if (e.code.contains('permission-denied') || e.code.contains("PERMISSION_DENIED")) {
return left(InsufficientPermisssons());
} else {
return left(UnexpectedFailure());
}
} else {
// ? check for the unauthenticated error
// ! log.e(e.toString()); // we can log unexpected exceptions
return left(UnexpectedFailure());
}
});
}
Below is where I capture the integrated query through the BloC
#injectable
class ObserverBloc extends Bloc<ObserverEvent, ObserverState> {
final TodoRepository todoRepository;
StreamSubscription<Either<TodoFailures, List<Todo>>>? todoStreamSubscription;
ObserverBloc({required this.todoRepository}) : super(ObserverInitial()) {
on<ObserverEvent>((event, emit) async {
emit(ObserverLoading());
await todoStreamSubscription?.cancel();
todoStreamSubscription = todoRepository
.watchAll()
.listen((failureOrTodos) => add(TodosUpdatedEvent(failureOrTodos: failureOrTodos)));
});
on<TodosUpdatedEvent>((event, emit) {
event.failureOrTodos.fold((failures) => emit(ObserverFailure(todoFailure: failures)),
(todos) => emit(ObserverSuccess(todos: todos)));
});
}
#override
Future<void> close() async {
await todoStreamSubscription?.cancel();
return super.close();
}
}
Even containing data in the database it comes empty, I need help to know where the problem is.
my test is throwing an exception because there is a StateNotifierProvider inside which is not overridden. for a regular Provider, i can override it using providerContainer, but for the state of a stateNotifierProvider, I don't know how to do it. I tried my best but I reached the limit of my best. I already saw this and this but it didn't help.
Appreciate much if someone could help me out of this. Thanks
My service File
class ReportService {
final Ref ref;
ReportService({
required this.ref,
});
Future<void> testReport() async {
//* How can i override this provider ?
final connection = ref.read(connectivityServiceProvider);
if (connection) {
try {
await ref.read(reportRepositoryProvider).testFunction();
} on FirebaseException catch (e, st) {
ref.read(errorLoggerProvider).logError(e, st);
throw Exception(e.message);
}
} else {
throw Exception('Check internet connection...');
}
}
}
final reportServiceProvider = Provider<ReportService>((ref) => ReportService(
ref: ref,
));
My test file
void main() {
WidgetsFlutterBinding.ensureInitialized();
final reportRepository = MockReportRepository();
ReportService makeReportService() {
final container = ProviderContainer(overrides: [
reportRepositoryProvider.overrideWithValue(reportRepository),
]);
addTearDown(container.dispose);
return container.read(reportServiceProvider);
}
test('test test', () async {
//How to stub the connectivityServiceProvider here ?
when(reportRepository.testFunction)
.thenAnswer((invocation) => Future.value());
final service = makeReportService();
await service.testReport();
verify(reportRepository.testFunction).called(1);
});
My StateNotifierProvider
class ConnectivityService extends StateNotifier<bool> {
ConnectivityService() : super(false);
}
final connectivityServiceProvider =
StateNotifierProvider<ConnectivityService, bool>(
(ref) => ConnectivityService());
Hello im trying to mock firebase messaging to get token but when i try to test i get some error,can someone help me to solve this error. This error occur only in testing and not in my emulator or mobile phone. Here is my setupFirebaseAuthMocks. Thank you
my test
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
}
void main() {
setupFirebaseAuthMocks();
late ProviderContainer container;
group('AuthenticationControllerTest -', () {
setUpAll(() async {
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
registerThirdPartyServices();
});
tearDown(() {
unregisterThirdPartyServices();
//container.dispose();
});
});
Error
MissingPluginException(No implementation found for method Messaging#getToken on channel plugins.flutter.io/firebase_messaging)
here is the method im trying to call
Future<Result<Failure, bool>> registerUserFirebaseToken() async {
try {
log.i('Registering Firebase');
final fireBaseMessaging = FirebaseMessaging.instance;
final token = await fireBaseMessaging.getToken();
log.v('Firebase token: $token');
await api.post(
link: '${env.getValue(kAuthUrl)}users/auth/firebase',
body: {'token': token},
hasHeader: true,
);
return const Success(true);
} catch (e) {
return Error(Failure(message: 'Firebase registration went wrong, Please try again!', content: e.toString()));
}
}
For those having the same issue, there is an example of a Mock on the official firebase messaging Github
Depending on your Mockito's version, you may have to update this code a little bit.
Here is the Mock file I'm using with Mockito v5.3.2
// ignore_for_file: require_trailing_commas
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart';
import 'package:firebase_messaging_platform_interface/firebase_messaging_platform_interface.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
typedef Callback = Function(MethodCall call);
final MockFirebaseMessaging kMockMessagingPlatform = MockFirebaseMessaging();
Future<T> neverEndingFuture<T>() async {
// ignore: literal_only_boolean_expressions
while (true) {
await Future.delayed(const Duration(minutes: 5));
}
}
void setupFirebaseMessagingMocks() {
TestWidgetsFlutterBinding.ensureInitialized();
setupFirebaseCoreMocks();
// Mock Platform Interface Methods
// ignore: invalid_use_of_protected_member
when(kMockMessagingPlatform.delegateFor(app: anyNamed('app')))
.thenReturn(kMockMessagingPlatform);
// ignore: invalid_use_of_protected_member
when(kMockMessagingPlatform.setInitialValues(
isAutoInitEnabled: anyNamed('isAutoInitEnabled'),
)).thenReturn(kMockMessagingPlatform);
}
// Platform Interface Mock Classes
// FirebaseMessagingPlatform Mock
class MockFirebaseMessaging extends Mock
with MockPlatformInterfaceMixin
implements FirebaseMessagingPlatform {
MockFirebaseMessaging() {
TestFirebaseMessagingPlatform();
}
#override
bool get isAutoInitEnabled {
return super.noSuchMethod(Invocation.getter(#isAutoInitEnabled),
returnValue: true, returnValueForMissingStub: true) as bool;
}
#override
FirebaseMessagingPlatform delegateFor({FirebaseApp? app}) {
return super.noSuchMethod(
Invocation.method(#delegateFor, [], {#app: app}),
returnValue: TestFirebaseMessagingPlatform(),
returnValueForMissingStub: TestFirebaseMessagingPlatform(),
) as FirebaseMessagingPlatform;
}
#override
FirebaseMessagingPlatform setInitialValues({bool? isAutoInitEnabled}) {
return super.noSuchMethod(
Invocation.method(
#setInitialValues, [], {#isAutoInitEnabled: isAutoInitEnabled}),
returnValue: TestFirebaseMessagingPlatform(),
returnValueForMissingStub: TestFirebaseMessagingPlatform(),
) as FirebaseMessagingPlatform;
}
#override
Future<RemoteMessage?> getInitialMessage() {
return super.noSuchMethod(Invocation.method(#getInitialMessage, []),
returnValue: neverEndingFuture<RemoteMessage>(),
returnValueForMissingStub: neverEndingFuture<RemoteMessage>())
as Future<RemoteMessage?>;
}
#override
Future<void> deleteToken() {
return super.noSuchMethod(Invocation.method(#deleteToken, []),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
#override
Future<String?> getAPNSToken() {
return super.noSuchMethod(Invocation.method(#getAPNSToken, []),
returnValue: Future<String>.value(''),
returnValueForMissingStub: Future<String>.value('')) as Future<String?>;
}
#override
Future<String> getToken({String? vapidKey}) {
return super.noSuchMethod(
Invocation.method(#getToken, [], {#vapidKey: vapidKey}),
returnValue: Future<String>.value(''),
returnValueForMissingStub: Future<String>.value('')) as Future<String>;
}
#override
Future<void> setAutoInitEnabled(bool? enabled) {
return super.noSuchMethod(Invocation.method(#setAutoInitEnabled, [enabled]),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
#override
Stream<String> get onTokenRefresh {
return super.noSuchMethod(
Invocation.getter(#onTokenRefresh),
returnValue: const Stream<String>.empty(),
returnValueForMissingStub: const Stream<String>.empty(),
) as Stream<String>;
}
#override
Future<NotificationSettings> requestPermission(
{bool? alert = true,
bool? announcement = false,
bool? badge = true,
bool? carPlay = false,
bool? criticalAlert = false,
bool? provisional = false,
bool? sound = true}) {
return super.noSuchMethod(
Invocation.method(#requestPermission, [], {
#alert: alert,
#announcement: announcement,
#badge: badge,
#carPlay: carPlay,
#criticalAlert: criticalAlert,
#provisional: provisional,
#sound: sound
}),
returnValue: neverEndingFuture<NotificationSettings>(),
returnValueForMissingStub:
neverEndingFuture<NotificationSettings>())
as Future<NotificationSettings>;
}
#override
Future<void> subscribeToTopic(String? topic) {
return super.noSuchMethod(Invocation.method(#subscribeToTopic, [topic]),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
#override
Future<void> unsubscribeFromTopic(String? topic) {
return super.noSuchMethod(Invocation.method(#unsubscribeFromTopic, [topic]),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
}
class TestFirebaseMessagingPlatform extends FirebaseMessagingPlatform {
TestFirebaseMessagingPlatform() : super();
}
and here is the unit test itself
void main() {
setupFirebaseMessagingMocks();
setUpAll(() async {
await Firebase.initializeApp();
FirebaseMessagingPlatform.instance = kMockMessagingPlatform;
});
test('An example of test', () {
//...
when(kMockMessagingPlatform.getToken(vapidKey: anyNamed('vapidKey')))
.thenAnswer(
(_) => Future.value('DEVICE_ID'),
);
//...
});
}
Hi I'm new to flutter and dart. I'm following a lesson on internet which is practicing to use bloc to control states. First lesson is after showing appStart animation, turn to a login page.
the lesson was using 'mapEventToState':
class AuthenticationBloc extends Bloc<AuthenticationEvent, AuthenticationState> {
final UserRepository? _userRepository;
AuthenticationBloc({UserRepository? userRepository})
: assert(userRepository != null),
_userRepository = userRepository, super(Uninitialized());
#override
Stream<AuthenticationState> mapEventToState(
AuthenticationEvent event,
) async* {
if (event is AppStarted) {
yield* _mapAppStartedToState();
} else if (event is LoggedIn) {
yield* _mapLoggedInToState();
} else if (event is LoggedOut) {
yield* _mapLoggedOutToState();
}
Stream<AuthenticationState> _mapAppStartedToState() async* {
log('_mapAppStartedToState is running.');
try {
final bool? isSigned = await _userRepository?.isSignedIn();
if (isSigned != null) {
if (isSigned) {
final String? name = await _userRepository?.getUser();
yield Authenticated(name);
}
else {
yield Unauthenticated();
}
}
} catch (_) {
yield Unauthenticated();
}
}
Stream<AuthenticationState> _mapLoggedInToState() async* {
log('_mapLoggedInToState is running.');
yield Authenticated(await _userRepository?.getUser());
}
Stream<AuthenticationState> _mapLoggedOutToState() async* {
log('_mapLoggedOutToState is running.');
yield Unauthenticated();
_userRepository?.signOut();
}
}
turns out 'mapEventToState' was removed.
According to this page(https://github.com/felangel/bloc/issues/2526), I try to use on< event > instead:
#override
AuthenticationBloc({UserRepository? userRepository})
: assert(userRepository != null, 'userRepository == null'),
_userRepository = userRepository,
super(Uninitialized()) {
log('AuthenticationBloc is running.');
on<AppStarted>(_appStarted);
on<LoggedIn>(_loggedIn);
on<LoggedOut>(_loggedOut);
}
Stream<AuthenticationState> _appStarted(AuthenticationEvent event, Emitter<AuthenticationState> emit) async* {
log('_appStarted is running.');
yield* _mapAppStartedToState();
}
But it didn't work. Even log('_appStarted is running.'); didn't show at console.
I tried to change type and aync*. It would show console log if _appStarted isn't aync.
void _appStarted(AuthenticationEvent event, Emitter<AuthenticationState> emit) {
log('_appStarted is running.');
// yield* _mapAppStartedToState();
}
However, it can't yield to stream as _appStarted isn't aync. Makes me confused.
Please let me know if I got some misunderstand about bloc and stream. Happy to see any solution or advise.
You no longer need your one function per event, because you already have it:
void _appStarted(AuthenticationEvent event, Emitter<AuthenticationState> emit) {
log('_appStarted is running.');
try {
final bool? isSigned = await _userRepository?.isSignedIn();
if (isSigned != null) {
if (isSigned) {
final String? name = await _userRepository?.getUser();
emit(Authenticated(name));
}
else {
emit(Unauthenticated());
}
}
} catch (_) {
emit(Unauthenticated());
}
}
If you want to delegate this to another function, just remove the stream return value and pass the emitter.