0

I am unsure if this question has been asked before, but I was unable to find an answer to it.

I have a stateful widget for my signin page, which uses the flutter_toast package for error handling (incorrect password, email already in use, etc.). When using flutter_toast, in order to make a custom toast, I have to use the initState function of the signin page. I also have another class, in a separate file, in which I am trying to move all of my Firebase Auth services into (this is where I am needing to call the showToast function, which is declared in the SigninState class). I have been having trouble figuring out how to get the showToast function to be defined in the AuthService class, so that I will be able to call it there.

Here is my Signin stateful widget class:

class Signin extends StatefulWidget {
  const Signin({Key? key}) : super(key: key);

  @override
  _SigninState createState() => _SigninState();
}

class _SigninState extends State<Signin> {
  FToast? fToast;
  AuthService authService = AuthService();

  @override
  void initState() {
    super.initState();
    fToast = FToast();
    fToast!.init(context);
  }
...
// build function{
//   in here, I call "authService.signInAction()", which needs to call "showToast()"
// }
...
}
showToast(String msg, context) {
    bool smallPhone = false;
    double width = MediaQuery.of(context).size.width;
    if (width <= 370) smallPhone = true;
    Widget toast = Container(
      padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(25.0),
        color: Colors.redAccent,
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          const SizedBox(
            width: 10.0,
          ),
          Text(
            msg,
            style:
                TextStyle(fontSize: smallPhone ? 13 : 15, color: Colors.white),
          ),
        ],
      ),
    );

    fToast!.showToast(
      child: toast,
      gravity: ToastGravity.CENTER,
      toastDuration: const Duration(seconds: 2),
    );
  }
...
...
} // closing tag for _SigninState class

Here is my AuthService class:

class AuthService {
  FirebaseAuth auth = FirebaseAuth.instance;
  Signin signin = const Signin();

  UserModel _userFromFirebaseUser(User user) {
    return UserModel(id: user.uid);
  }

  Stream<UserModel> get user {
    return auth.authStateChanges().map((user) => _userFromFirebaseUser(user!));
  }

  Future signInAction(
      String email, String password, BuildContext context) async {
    try {
      User user = (await auth.signInWithEmailAndPassword(
          email: email, password: password)) as User;
      _userFromFirebaseUser(user);
      Navigator.pushReplacement(
        context,
        MaterialPageRoute(
            builder: (context) => const MyHomePage(title: "Twitter")),
      );
    } on FirebaseAuthException catch (e) {
      if (e.code == 'wrong-password') {
        signin.showToast("The password provided is incorrect.", context); // <-- This does not work
      } else if (e.code == 'user-not-found') {
        signin.showToast("An account with that email does not exist.", context); // <-- This does not work
      }
    } catch (e) {
      signin.showToast("An unexpected error has occurred.", context); // <-- This does not work
    }
  }
}

This is the error that I am getting:

The method 'showToast' isn't defined for the type 'Signin'.
Try correcting the name to the name of an existing method, or defining a method named 'showToast'
noahfoe
  • 27
  • 6
  • I don't think it's possible to do what you want to do the way you are trying to do it, why not (re)throw the error and catch it on the state class and call show toast from there? – h8moss Dec 21 '21 at 15:29
  • you could also move both the initialization call and the signInAction method into the auth service class and just remember to call the initialization before the first call of `signiinAction` – h8moss Dec 21 '21 at 15:33
  • Why don't you declare this function in separate class and just call them by creating object of that class? – Diwyansh Dec 21 '21 at 15:42

2 Answers2

1

You can use callback but i don't think this is an elegant solution.

signInAction(
      String email, String password, {required void Function(String message) onError}) async {
    try {
      User user = (await auth.signInWithEmailAndPassword(
          email: email, password: password)) as User;
      _userFromFirebaseUser(user);
      Navigator.pushReplacement(
        context,
        MaterialPageRoute(
            builder: (context) => const MyHomePage(title: "Twitter")),
      );
    } on FirebaseAuthException catch (e) {
      if (e.code == 'wrong-password') {
        onError("The password provided is incorrect.");
      } else if (e.code == 'user-not-found') {
        onError("An account with that email does not exist.");
      }
    } catch (e) {
      onError("An unexpected error has occurred."); 
    }
  }

and then you can call

authService.signInAction(email, password, 
   onError:(message){
       showToast(message, context);
   }
)
Qori
  • 194
  • 2
  • 9
  • This approach seems like it should work, but for some reason, I was not able to get it to work. Thanks for the reply though! – noahfoe Dec 21 '21 at 17:08
1

it may possible but you can easy different make it instead of your approach. set a static String message = "";

and..
on FirebaseAuthException catch (e) {
      if (e.code == 'wrong-password') {
        message = "The password provided is incorrect.";
      }

finally you can use it in Auth button..

showToast() {
    bool smallPhone = false;
    double width = MediaQuery.of(context).size.width;
    if (width <= 370) smallPhone = true;
    Widget toast = Container(
      padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(25.0),
        color: Colors.redAccent,
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          const SizedBox(
            width: 10.0,
          ),
          Text(
            AuthService.message, 
            style:
                TextStyle(fontSize: smallPhone ? 13 : 15, color: Colors.white),
          ),
        ],
      ),
    );

maybe it will help

Zekai Demir
  • 143
  • 1
  • 10
  • 1
    this approach did not work directly, but it helped me get to a solution that worked. Instead of making the message static, I made it late. Then in the onPressed event of the signin button, I call signInAction, as well as check if the message from authService is not null, if it is not null, I show toast by passing authService.message as the msg. – noahfoe Dec 21 '21 at 17:08