Skip to content

registerFactory not working with bloc #151

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
xenSlayer opened this issue Jan 27, 2021 · 13 comments
Closed

registerFactory not working with bloc #151

xenSlayer opened this issue Jan 27, 2021 · 13 comments

Comments

@xenSlayer
Copy link

Been trying days to figure out why the registerFactory is not changing the state of the bloc.

main.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await di.init();
  await li.login();
  loginCheckPoint();
  runApp(PredictorApp());
}

user_login_injection_container.dart

import 'package:connectivity/connectivity.dart';
import 'package:get_it/get_it.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

import '../core/network/network_info.dart';
import '../features/predictor/data/datasources/local_data_source/fixtures_local_data_source.dart';
import '../features/predictor/data/datasources/local_data_source/prediction_local_data_source.dart';
import '../features/predictor/data/datasources/remote_data_source/fixtures_remote_data_source.dart';
import '../features/predictor/data/datasources/remote_data_source/prediction_remote_data_source.dart';
import '../features/predictor/data/datasources/token_access.dart';
import '../features/predictor/data/repositories/fixtures_repositories_impl.dart';
import '../features/predictor/data/repositories/prediction_repositories_impl.dart';
import '../features/predictor/domain/repositories/fixtures_repositories.dart';
import '../features/predictor/domain/repositories/prediction_repositories.dart';
import '../features/predictor/domain/usecases/get_fixtures.dart';
import '../features/predictor/domain/usecases/get_predictions.dart';
import '../features/predictor/presentation/bloc/nav_bar_bloc/nav_bar_bloc.dart';

GetIt sl = GetIt.instance;

Future<void> init() async {
  // Bloc
  sl.registerFactory<NavBarBloc>(
      () => NavBarBloc(getBasicFixtures: sl(), getPredictions: sl()));

  // Validate token
  sl.registerLazySingleton<ValidateToken>(() => ValidateTokenImpl());

  /// usecase
  ///
  // Fixtures
  sl.registerLazySingleton(() => GetBasicFixtures(repository: sl()));
  // Prediction
  sl.registerLazySingleton(() => GetPredictions(repository: sl()));

  /// repository
  ///
  // Fixtures
  sl.registerLazySingleton<BasicFixturesRepository>(() =>
      BasicFixturesRepositoryImpl(
          localDataSource: sl(), networkInfo: sl(), remoteDataSource: sl()));
  // Prediction
  sl.registerLazySingleton<PredictionRepository>(() => PredictionRepositoryImpl(
      localDataSource: sl(), networkInfo: sl(), remoteDataSource: sl()));

  /// Data Source
  /// Remote Date Source
  ///
  // Fixtures
  sl.registerLazySingleton<BasicFixturesRemoteDataSource>(() =>
      BasicFixturesRemoteDataSourceImpl(client: sl(), validateToken: sl()));
  // Prediction
  sl.registerLazySingleton<PredictionsRemoteDataSource>(
      () => PredictionsRemoteDataSourceImpl(client: sl(), validateToken: sl()));

  /// Local Data Source
  ///
  // Fixtures
  sl.registerLazySingleton<BasicFixturesLocalDataSource>(
      () => BasicFixturesLocalDataSourceImpl(sharedPreferences: sl()));
  // Prediction
  sl.registerLazySingleton<PredictionsLocalDataSource>(
      () => PredictionLocalDataSourceImpl(sharedPreferences: sl()));

  // External
  sl.registerLazySingleton<NetworkInfo>(() => NetworkInfoImpl(sl()));
  final sharedPreferences = await SharedPreferences.getInstance();
  sl.registerLazySingleton(() => sharedPreferences);
  sl.registerLazySingleton(() => http.Client());
  sl.registerLazySingleton(() => Connectivity());
}

user_login_injection_container.dart

import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:get_it/get_it.dart';

import '../features/predictor/data/datasources/user_login.dart';
import '../features/predictor/data/repositories/user_login_repositories_impl.dart';
import '../features/predictor/domain/repositories/user_login_repositories.dart';
import '../features/predictor/domain/usecases/user_login.dart';
import '../features/predictor/presentation/bloc/login_bloc/user_login_bloc.dart';
import 'injection_container.dart';

GetIt sm = GetIt.instance;

Future<void> login() async {
  // bloc
  sm.registerFactory<UserLoginBloc>(() => UserLoginBloc(userLogin: sm()));

  // usecase
  sm.registerLazySingleton(() => UserLogin(userLoginRepository: sm()));

  //repository
  sm.registerLazySingleton<UserLoginRepository>(
      () => UserLoginRepositoryImpl(networkInfo: sl(), remoteDataSource: sm()));

  // Data Source
  // Remote Date Source
  sm.registerLazySingleton<UserLoginRemoteDataSource>(() =>
      UserLoginRemoteDataSourceImpl(
          client: sl(), secureStorage: FlutterSecureStorage()));
}

login_bloc
user_login_bloc.dart

import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:meta/meta.dart';

import '../../../../../core/usecases/usecase.dart';
import '../../../../../core/util/constants.dart';
import '../../../domain/usecases/user_login.dart';

part 'user_login_event.dart';
part 'user_login_state.dart';

class UserLoginBloc extends Bloc<UserLoginEvent, UserLoginState> {
  final UserLogin userLogin;
  UserLoginBloc({@required this.userLogin}) : super(UserLoginInitial());

  @override
  Stream<UserLoginState> mapEventToState(
    UserLoginEvent event,
  ) async* {
    if (event is LoginEvent) {
      yield UserLogginginState();
      dynamic response = await userLogin(
          LoginParams(username: event.username, password: event.password));
      yield* response.fold((failure) async* {
        yield UserLoginFailureState();
      }, (userLoginEntity) async* {
        yield UserLoginSuccessState();
      });
    } else if (event is LogOutEvent) {
      final FlutterSecureStorage secureStorage = FlutterSecureStorage();
      secureStorage.delete(key: REFRESHKEY);
      yield UserLoginInitial();
    } else if (event is AutoLoginEvent) {
      yield UserLoginSuccessState();
    }
  }
}

user_login_event.dart

part of 'user_login_bloc.dart';

abstract class UserLoginEvent extends Equatable {
  const UserLoginEvent();

  @override
  List<Object> get props => [];
}

class LoginEvent extends UserLoginEvent {
  final String username;
  final String password;

  LoginEvent({@required this.username, @required this.password});
  @override
  List<Object> get props => [username, password];
}

class LogOutEvent extends UserLoginEvent {}

class AutoLoginEvent extends UserLoginEvent {}

user_login_state.dart

part of 'user_login_bloc.dart';

abstract class UserLoginState extends Equatable {
  const UserLoginState();

  @override
  List<Object> get props => [];
}

class UserLoginInitial extends UserLoginState {}

class UserLogginginState extends UserLoginState {}

class UserLoginFailureState extends UserLoginState {}

class UserLoginSuccessState extends UserLoginState {}

@xenSlayer
Copy link
Author

Everything is working except the bloc. Bloc is not changing its state while the app is perfectly making login request to the backend. Only problem is that the bloc state is not changing. If I use registerLazySingleton instead of registerFactory then everything works smooth. Even bloc works fine but I encountered a bug using lazySingleton.

@escamoteur
Copy link
Collaborator

Unfortunately you haven't added the part of the code where you use the UserLoginBloc.
If you use a factory, you will create different instances at every location you access UserLoginBloc. So I wouldn't be surprised when you are getting into problems.

Why can't you use a LazySingleton?

@xenSlayer
Copy link
Author

Thank you @escamoteur but I recently fixed the problem. I'll just mention how I fixed my problem so others with same issue see.

I was adding events directly as
sl<UserLoginBloc>.add(EVENT)

The problem was the events were passed to the new instance of Bloc as I was using registerFactory() in get_it.
So I initialized a variable with

var bloc = sl<UserLoginBloc>

and added events to the bloc as

bloc..add(EVENT)

which worked as expected and updated the UI.

It was problem with my code and not get_it. Sorry and thank you!

@escamoteur
Copy link
Collaborator

Btw you could always use a singleton in combination with GetIt Scopes then you don't run into this problems if you want to have only short lived BLoCs

@xenSlayer
Copy link
Author

Hello @escamoteur . I am using get_it on my app with login system. The problem I am facing is when the user log's out and another user logs in, the previously logged in user data is shown. You mentioned about new feature of scopes in get_it. But unfortunately I can find no example all over the internet [including get_it docs] on how I can use scope feature. Can you please provide an example on how to use this feature. Also update the docs since there's no point in having a feature when you don't let the users know how those features comes handy.

@escamoteur
Copy link
Collaborator

Did you check the docs of the V5.0.0 of get_it?
https://pub.dev/packages/get_it#scopes

@xenSlayer
Copy link
Author

Is there an actual example of how to work with it?

@escamoteur
Copy link
Collaborator

Don't think so, a look at the tests for it in the get_it package might help.
What is not clear to you?

@xenSlayer
Copy link
Author

This is how I register types when app starts.
main.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await di.init();
  await li.login();
  runApp(XenApp());
}

I want to unregister the AccountBloc's when user logs out registered in di.init() and re register when another user logs in.
My point is I want AccountBloc to have fresh data.

account.dart

FlatButton.icon(
        icon: Icon(Icons.logout),
        onPressed: () async {
        sm<UserLoginBloc>().add(LogOutEvent());
        Navigator.pop(context);
        },
        label: Text("LOGOUT"))

@escamoteur
Copy link
Collaborator

you don't do the login register at the beginning of your app but at the moment you do the loginpage.
Meaning before showing the login page you call pushScope() and pass the register function for your auth bloc as paramteter. When the use logs out you just pop that scope.

@xenSlayer
Copy link
Author

xenSlayer commented Feb 16, 2021

Thank you! Solved my problem. All I didn't know was when to call pushNewScope and popScope.

Pushing scope

sl.pushNewScope();
await di.init();

Popping Scope

FlatButton.icon(
                  color: Colors.red,
                  icon: Icon(Icons.logout, color: Colors.white),
                  onPressed: () async {
                    sm<UserLoginBloc>().add(LogOutEvent());
                    Navigator.pop(context);
                    sl.popScope(); // Here pop scope
                  },
                  label: Text("LOGOUT"))

It was confusing on how and where to use those push and pop scopes. Apart from that it's really simple.
Thank you !! But a simple working example would be better. 😊

@escamoteur
Copy link
Collaborator

I agree, I should add that.

@escamoteur
Copy link
Collaborator

actually you even could pop the scope from your BLoC at the end of the LogOutEvent handler. Insteat of here from the UI

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants