skip to Main Content

Currently I am facing a problem, I am using AsyncNotifier for my login page and there I am saving token in Auth provider and user information in User provider, but when I try to retrieve user data in profile screen it shows null, it seems state not persist between screens … What am I doing wrong?

My User provider:

@Riverpod(keepAlive: true)
class User extends _$User {
  @override
  Future<UserModel?> build() async => null;

  Future<void> setUser(UserModel user) async {
    state = AsyncData(user);
  }

  void signOut() {
    state = const AsyncData(null);
  }
}

My Auth provider:

@riverpod
class Auth extends _$Auth {
  @override
  Future<AuthModel?> build() async => null;
  
  Future<void> login(String username, String password) async {
    var dio = getDio();
    var response = await dio.post(
      '/login',
      data: {
        'username': username,
        'password': password,
      },
    );

    state = AsyncData(AuthModel.fromJson(response.data));

    ref
        .read(userProvider.notifier)
        .setUser(UserModel.fromJson(response.data['user']));
  }
 
}

User profile screen:

class ProfileScreen extends ConsumerWidget {
  const ProfileScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    var user = ref.watch(userProvider);
 
    return Column(
        children: [
          Text("${user.value?.fullName}"), // null
          Text("${user.value?.login}") // null
        ]
);
}
}

Login page:

class LoginScreen extends ConsumerStatefulWidget {
  const LoginScreen({super.key});

  @override
  ConsumerState<LoginScreen> createState() => _LoginScreenState();
}


class _LoginScreenState extends ConsumerState<LoginScreen> {
  final TextEditingController login = TextEditingController();
  final TextEditingController password = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(
          controller: login,
        ),
        TextField(
          controller: password,
        ),
        ElevatedButton(
            onPressed: () {
                  ref
                  .read(authProvider.notifier)
                  .login(
                    login.text,
                    password.text,
                  )
                  .then(
                    (_) => Navigator.pushNamed(context, "profile"),
                  );
            },
            child: Text("Login")),
      ],
    );
  }
}

2

Answers


  1. Chosen as BEST ANSWER

    It was because I tried to manipulate userProvider before it was initialized. I fixed this by adding ref.watch(userProvider) in LoginScreen.

    You may have a question:

    It will rebuild this screen for every change, isn't this a bad practice ?

    Answer:
    Since changes in providers like auth and user usually affect your entire app you probably want to trigger a rebuild anyway. Therefore, I watch them at the top of the widget tree.


  2. You just need to add add await keyword before calling your future provider.

    await ref
                    .read(authProvider.notifier)
                    .login(
                      login.text,
                      password.text,
                    )
                    .then(
                        (_) => Navigator.pushNamed(context, "profile"));
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search