riverpod
riverpod copied to clipboard
LoginController(StateNotifierProvider)+GoRouter autoSignIn firebase not working
Using a StateNotifierProvider with GoRouter to check if a User is already logged in into Firebase, the redirect feature is not working on flutter_riverpod 2.0.0-dev.9.
class LoginController extends StateNotifier<LoginState> {
LoginController(this.ref) : super(const LoginStateInitial('initial'));
final Ref ref;
Stream<User> authStateChanges() {
return ref.read(authRepositoryProvider).authStateChanges();
}
.......
void autoSignIn() async {
try {
if (state is LoginStateInitial || state is LoginStateError) {
state = LoginStateInitial('initial');
User user = await ref
.read(loginControllerProvider.notifier)
.authStateChanges()
.first;
print(user);
if (user != null) {
state = LoginStateSuccess('success', user.uid);
} else
state = LoginStateInitial('initial');
}
} catch (e) {
state = LoginStateError('error', e.toString());
}
}
..........
}
final loginControllerProvider =
StateNotifierProvider<LoginController, LoginState>((ref) {
return LoginController(ref);
});
In the LandinPage of the app (first screen) I use a simple ref.listen
to autoSignIn
class LandingPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.listen(currentUserProvider, (previous, next) {
if (next is AsyncData) {
if (next.value != null) {
LoginState loginState =
ref.read(loginControllerProvider.notifier).debugState;
if (loginState is LoginStateInitial ||
loginState is LoginStateError) {
if (next.value.uid != null) {
ref.read(loginControllerProvider.notifier).autoSignIn();
}
}
}
}
});
return Scaffold(...
final routerProvider = Provider.autoDispose<GoRouter>((ref) {
final r = RouterNotifier(ref);
return GoRouter(
initialLocation: '/',
debugLogDiagnostics: true,
refreshListenable: r,
redirect: r.redirectLogic,
routes: r.routes);
});
class RouterNotifier extends ChangeNotifier {
final Ref _ref;
RouterNotifier(this._ref) {
_ref.listen<LoginState>(
loginControllerProvider,
(_, __) => notifyListeners(),
);
}
String redirectLogic(GoRouterState state) {
final loginState = _ref.read(loginControllerProvider);
final user = _ref.read(loginControllerProvider.notifier).authStateChanges();
final areWeLoggingIn = state.location == '/auth/login' ||
state.location == '/auth/register' ||
state.location == '/';
if (loginState is LoginStateInitial && user == null) {
return areWeLoggingIn ? null : state.location;
}
if (loginState is LoginStateLoading && user == null) {
return areWeLoggingIn ? null : '/loading';
}
if (loginState is LoginStateSuccess && user != null) {
return !areWeLoggingIn ? null : '/home';
}
return null;
}
List<GoRoute> get routes => [
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return LandingPage();
}), .......
AutoSignIn works with go_router 4.4.1 and flutter_riverpod 1.0.4. Breaks with flutter_riverpod 2.0.0-dev.9
Hello! This is too broad. Could you reformulate your issue such that it does not depend on your business code, and ideally not on any package either?
We'd also need clear steps to reproduce the problem, and a description of what's happening vs what's expected.
Hello! I am listening to a StreamProvider currentUserProvider to check if the user is logged into firebase and to access its documents i need its uid. To get the uid i check a StateNotifierProvider and get the uid from LoginStateSuccess props. This logic, with the code posted above, works with flutter_riverpod 1.0.4 but not with 2.0.0-dev.9 or any other 2.0.0-xxx.
This ref.read(loginControllerProvider.notifier).autoSignIn(); never gets called in the LandingPage listener / can't retrieve state / can't access State props.
With the code from above you can reproduce the issue.
Expected: Check if the user is logged in and get uid from LoginSuccessState and redirect the user to HomePage (restricted access, needs uid for firebase documents).
Happening: Even tho the StreamProvider currentUserProvider
final Ref ref;
Stream<User> authStateChanges() {
return ref.read(authRepositoryProvider).authStateChanges();
}
says the user is logged into firebase, I can't get its uid from LoginSuccessState for further use and is not redirected to HomePage.
Sorry for this long bug report
SOLVED
class LoginController extends StateNotifier<LoginState> {
LoginController(this.ref) : super(const LoginStateInitial('initial'));
final Ref ref;
Stream<User> authStateChanges() {
return ref.read(authRepositoryProvider).authStateChanges();
}
.......
void autoSignIn() async {
try {
if (state is LoginStateInitial || state is LoginStateError) {
state = LoginStateInitial('initial');
User user = await ref
.read(loginControllerProvider.notifier)
.authStateChanges()
.first;
print(user);
if (user != null) {
state = LoginStateSuccess('success', user.uid);
} else
state = LoginStateInitial('initial');
}
} catch (e) {
state = LoginStateError('error', e.toString());
}
}
..........
}
final loginControllerProvider =
StateNotifierProvider<LoginController, LoginState>((ref) {
return LoginController(ref);
});
Changed this
User user = await ref
.read(loginControllerProvider.notifier)
.authStateChanges()
.first;
to this
User user =
await ref.read(authRepositoryProvider).authStateChanges().first;
so that the StateNotifier doesn't depend on itself.
Conclusion: Till flutter_riverpod 1.0.4 it appears that a StateNotifier (dunno about the rest) can depend on itself. On flutter_riverpod 2.0.0-dev.9 the self dependency doesn't work anymore.