firebase 如何清除Flutter应用中的Widget堆栈,以便在注销时无法返回上一页?

iklwldmw  于 2023-04-22  发布在  Flutter
关注(0)|答案(2)|浏览(179)

当我注销应用时,我可以按后退按钮返回到注销前的最后一个页面。然而,奇怪的是,只有当我在登录时导航到主页以外的另一个页面时,才会出现此意外功能。
我使用Firebase来管理我的用户身份验证,我设置了一个类来管理身份验证,如下所示:

class AuthService {

  // creating a member of class that represents an instance of firebase authentication
  final FirebaseAuth _auth = FirebaseAuth.instance;

  // create user object based on firebase User class
  AppUser? _userFromFirebaseUser(User user) {
    // ternary operator
    return user != null ? AppUser(uid: user.uid) : null;
  }

  // auth change user stream
  Stream<AppUser> get user {
    // mapping firebase User to User 
    return _auth.authStateChanges().map((User? user) => _userFromFirebaseUser(user!)!);
  }

  }
  // sign in with email & password
  Future signIn(String email, String password) async {
    try {
      UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password);
      User? firebaseUser = result.user;
      return _userFromFirebaseUser(firebaseUser!);
    } catch(e) {
      print(e.toString());
      return null;
    }
  }

  // register with email & password
  Future registerWithEmailAndPassword(String email, String password) async {
    try {
      UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
      User? firebaseUser = result.user;
      return _userFromFirebaseUser(firebaseUser!);
    } catch(e) {
      print(e.toString());
      return null;
    }
  }

  // sign out
  Future signOutFunc() async {
    try {
      _auth.signOut();
    } catch(e) {
      print('Failed to sign out');
      print(e);

    }
  }
}

这个类不断地监听用户身份验证的变化,并根据用户是否登录显示登录或注册页面。

class Wrapper extends StatelessWidget {
  const Wrapper({super.key});

  @override
  Widget build(BuildContext context) {

    final user = Provider.of<AppUser?>(context);
    print(user);
    // return either HomePage or Athenticate Widget
    // listening to auth changes from stream
    if (user == null) {
      return Authenticate();
    } else {
      return MyHomePage();
    }
  }
}

当用户注销时,此小部件将用户引导到注册页面。

class Authenticate extends StatefulWidget {
  const Authenticate({super.key});

  @override
  State<Authenticate> createState() => _AuthenticateState();
}

class _AuthenticateState extends State<Authenticate> {
  @override
  Widget build(BuildContext context) {

      return RegisterPage();
    }
    
  }

这是用户登录后我通常在应用中管理导航的方式:

class RouteGenerator {
  // static function to generate route
  static Route<dynamic> generateRoute(RouteSettings settings) {
    // getting arguments passed in while callling Navigator.pushNamed()
    final args = settings.arguments as Map<String, dynamic>?;

    // checking if the name of the route is the home route
    switch (settings.name) {
      case '/':
        return MaterialPageRoute(builder: (_) => MyHomePage());
      case 'ReceiptExplorer':
        return MaterialPageRoute(builder: (_) => ReceiptExplorer());
      case 'SignInPage':
        return MaterialPageRoute(builder: (_) => SignInPage());
      case 'PicturePreviewPage':
        final imagePath = args!['imagePath'] as String;
        return MaterialPageRoute(
            builder: (_) => PicturePreviewPage(imagePath: imagePath));
      case 'RegisterPage':
          return MaterialPageRoute(builder: (_) => RegisterPage());

      default:
        return _errorRoute();
    }
  }
}

Route<dynamic> _errorRoute() {
  return MaterialPageRoute(builder: (_) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Error"),
        ),
        body: Center(
          child: Text('ERROR'),
        ));
  });
}

这里是我的main.dart,我使用流提供程序向其子部件提供AppUser(一个自定义类,它抽象了检索firebase User时返回的所有不必要的信息)对象流。

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

// ignore: use_key_in_widget_constructors
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return StreamProvider<AppUser?>.value
    ( initialData: null,
    catchError: (User, AppUser) => null,
      value: AuthService().user,
      child: const MaterialApp(
        debugShowCheckedModeBanner: false,
        home: Wrapper(),
        initialRoute: "/",
        onGenerateRoute: RouteGenerator.generateRoute,
      ),
    );
  }
}

我尝试在Authenticate Widget中返回SignInPage而不是RegisterPage,但这并没有改变任何东西。我还使用以下代码更新了Authenticate Widget,尝试用新页面替换Widget堆栈的顶部,但这也没有改变任何东西。

class Authenticate extends StatefulWidget {
  const Authenticate({Key? key});

  @override
  State<Authenticate> createState() => _AuthenticateState();
}

class _AuthenticateState extends State<Authenticate> {

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addPostFrameCallback((_) {
      Navigator.of(context).pushReplacementNamed('SignInPage');
    });
  }

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: CircularProgressIndicator(),
      ),
    );
  }
}

我看到一些其他的帖子,人们用途:

Navigator.of(context).popUntil(ModalRoute.withName('/')); // pops the stack back to the home page

Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false)

...但我不确定如何实现这些,因为我的主页(/)路由是我的应用程序的主页,而不是登录或注册页面,所以这些方法只会在我注销应用程序时返回应用程序的主页,这是不可取的。
总而言之,我想知道为什么当我退出应用程序时屏幕上甚至会显示一个后退按钮,它会将我带到注册页面,以及为什么当我按下此后退按钮时,它会将我带到我登录时的图片预览页面。
下面是一个视频,可以更轻松地演示我的问题是什么:https://youtu.be/Kl4KOwRgHS0

sxpgvts3

sxpgvts31#

从注册页面中删除后退按钮。

Scaffold(
  appBar: AppBar(automaticallyImplyLeading: false,),
);

如果你需要显示它(如果你想从另一个页面导航到注册页面,通过使用Navigator.push(...)),然后使用(leading:)参数或AppBar手动添加它。

4xrmg8kj

4xrmg8kj2#

我扩展了AuthenticateState小部件的initState函数,并使用了Navigator.pushNamedAndRemoveUntil(),它解决了我的问题。

class _AuthenticateState extends State<Authenticate> {

  @override
  // initState is called when the Authenticate widget is inserted into the  widget tree
  void initState() {
    super.initState();
    // the addPostFrameCallback method runs at the end of the current frame, which is
    // right when the widget is added to the widget tree so the RegisterPage is shown instantly,
    // after the Authenticate widget is called
    WidgetsBinding.instance.addPostFrameCallback((_) {
      // removing all routes in the stack except RegisterPage
      Navigator.pushNamedAndRemoveUntil(context, 'SignInPage', (route) => false);
    });
  }

  @override
  Widget build(BuildContext context) {
    // the Authenticate widget isn't being shown on the screen for any
    // noticeable length of time since the addPostFrameCallback
    // method runs on the first frame. So we return an empty Container
    // just as 'something' to return from the function 
    return Container();
  }
}

相关问题