Flutter:如何从另一个外部 Class 调用依赖于 initState 的有状态小部件状态 Class 中的函数

Flutter: How to Call a Function that is in a Stateful Widgets State Class, that depends on the initState, from another external Class

我不确定以前是否有人问过这个问题,但我找不到答案。

我的登录页面有一个状态小部件,它使用 flutter_toast 程序包进行错误处理(密码不正确、电子邮件已在使用等)。使用 flutter_toast 时,为了制作自定义 toast,我必须使用登录页面的 initState 函数。 我还有另一个 class,在一个单独的文件中,我试图将我所有的 Firebase Auth 服务移到其中(这是我需要调用 showToast 函数的地方,该函数在 SigninState class). 我一直无法弄清楚如何在 AuthService class 中定义 showToast 函数,以便我可以在那里调用它。

这是我的登录状态小部件 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

这是我的 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
    }
  }
}

这是我遇到的错误:

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'

您可以使用回调,但我认为这不是一个优雅的解决方案。

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."); 
    }
  }

然后你可以调用

authService.signInAction(email, password, 
   onError:(message){
       showToast(message, context);
   }
)

这可能是可能的,但您可以轻松地改变它而不是您的方法。 设置静态字符串消息 = "";

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

终于可以在Auth按钮中使用了..

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),
          ),
        ],
      ),
    );

也许会有帮助