Handle authentication with Riverpod

5.8k views Asked by At

I'm trying to get hold of Riverpod but I'm having some issues.

I created a form for signing in users and I'm trying to get the main view change when the user is logged in. My issue is, when I set change the state and navigate back to the app root, the new state doesn't seem to be available. But if I save a file or hot reload the app, the new state is available.

What I'm I doing wrong?

My main.dart file

import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:practical_riverpod/features/authenticated/authentication_state.dart';

import 'package:practical_riverpod/features/signup/signup_screen.dart';

void main() {
  runApp(ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, ScopedReader watch) {
    final auth = watch(authProvider).state;

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            Text(auth == null ? "not connected" : auth.firstname),
            RaisedButton(
              child: Text("gooooo"),
              onPressed: () {
                Navigator.of(context).push(
                  MaterialPageRoute(
                    builder: (context) => SignupScreen(),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

I have a file with the state notifier inside:

import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:practical_riverpod/features/models/user_model.dart';

final authProvider = StateNotifierProvider((ref) {
  return AuthNotifier(null);
});

class AuthNotifier extends StateNotifier<UserModel> {
  AuthNotifier(UserModel state) : super(state);

  setCurrentUser(UserModel user) {
    state = user;
  }

  void clearUser() {
    state = null;
  }
}

the signup screen

import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:practical_riverpod/features/authenticated/authentication_state.dart';
import 'package:practical_riverpod/features/models/user_model.dart';

class SignupScreen extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final _firstnameController =
        useTextEditingController.fromValue(TextEditingValue.empty);

    final _lastname =
        useTextEditingController.fromValue(TextEditingValue.empty);
    final GlobalKey<FormState> _key = GlobalKey<FormState>();

    final auth = context.read(authProvider);

    return Scaffold(
      body: Column(
        children: [
          SizedBox(
            height: 190,
          ),
          Container(
            padding: EdgeInsets.all(20),
            child: Column(
              children: [
                Text("Signup"),
                Form(
                  key: _key,
                  child: Column(
                    children: [
                      TextFormField(
                        decoration: InputDecoration(hintText: "Your email"),
                        controller: _firstnameController,
                        validator: (value) => _firstnameController.text.isEmpty
                            ? "Can't be empty"
                            : null,
                      ),
                      TextFormField(
                        obscureText: true,
                        onSaved: (newValue) => print(newValue),
                        validator: (value) =>
                            _lastname.text.isEmpty ? "Can't be empty" : null,
                        decoration: InputDecoration(
                          hintText: "Password",
                        ),
                        controller: _lastname,
                      ),
                      RaisedButton(
                        child: Text("Create"),
                        onPressed: () {
                          if (_key.currentState.validate()) {
                            auth.setCurrentUser(
                              UserModel(
                                  firstname: _firstnameController.text,
                                  lastname: "_password.text"),
                            );
                            Navigator.of(context).pop();
                          }
                        },
                      )
                    ],
                  ),
                )
              ],
            ),
          )
        ],
      ),
    );
  }
}

And finally my userModel:

class UserModel {
  final String firstname;
  final String lastname;

  UserModel({this.firstname, this.lastname});
}
1

There are 1 answers

2
Alex Hartford On BEST ANSWER

[Riverpod >= 0.14.0] Change:

final auth = watch(authProvider).state;

to:

final auth = watch(authProvider);

[Riverpod < 0.14.0] Change:

final auth = watch(authProvider).state;

to:

final auth = watch(authProvider.state);

I would recommend adding a logger to your Riverpod ProviderScope - it can really help when debugging issues like this!