【问题标题】:Flutter Firebase Authentication, Routing and Widget TreeFlutter Firebase 身份验证、路由和小部件树
【发布时间】:2021-05-19 04:54:16
【问题描述】:

我是 Flutter 的新手,在实现 FireBase 身份验证时对小部件树结构有疑问。

下面是我现在的小部件树。

我使用 Provider 来访问 clind 小部件中的 FirebaseAuthentication 对象,并使用 StreamBuilder 来识别身份验证状态更改。

我的问题是在哪里放置路由。我不确定拥有两个 MaterialApp 是否正确。将路由放在 HomePage 类中感觉不对,而且还会多次重新加载应用程序

部分代码如下。

class LandingPage extends StatelessWidget {
  const LandingPage();

  @override
  Widget build(BuildContext context) {
  final firebaseAuth = Provider.of<FirebaseAuth>(context);
  final SecureStorage secureStorage = SecureStorage();
  void checkAndLoginWithSavedCredentials() {
    if (kIsWeb) {
    } else if (Platform.isAndroid || Platform.isIOS) {
      print(secureStorage.readSecureData('email'));
    }
}
return StreamBuilder<User>(
    stream: firebaseAuth.authStateChanges(),
    builder: (context, AsyncSnapshot<User> snapshot) {
      checkAndLoginWithSavedCredentials();
      print(snapshot.connectionState.toString());
      if (snapshot.connectionState == ConnectionState.active) {
        //final bool signedIn = snapshot.hasData;
        User user = snapshot.data;
        return user == null ? LoginScreen() : HomeScreen();
        //return signedIn ? DashBoard() : FirstView();
      } else {
        return LoginScreen();
      }
    });
 }
}

class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
  initialRoute: '/home',
  routes: {
    '/': (context) => LandingPage(),
    '/home': (context) => HomeScreen(),
    '/notifications': (context) => NotificationList(),
    '/fitness': (context) => FitnessScreen(),
    '/settings': (context) => SettingsScreen(),
  },
  debugShowCheckedModeBanner: false,
 );
 }
}

更新: 根据建议,我将代码更改如下。

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Provider<FirebaseAuth>(
  create: (context) => FirebaseAuth.instance,
  child: MaterialApp(
    initialRoute: '/',
    routes: {
      '/': (context) => LandingPage(),
      '/home': (context) => HomeScreen(),
      '/notifications': (context) => NotificationList(),
      '/fitness': (context) => FitnessScreen(),
      '/settings': (context) => SettingsScreen(),
    },
    debugShowCheckedModeBanner: false,
   ), 
  );
 }
}

class LandingPage extends StatelessWidget {
const LandingPage();

@override
Widget build(BuildContext context) {
    final firebaseAuth = Provider.of<FirebaseAuth>(context);
}

return StreamBuilder<User>(
    stream: firebaseAuth.authStateChanges(),
    builder: (context, AsyncSnapshot<User> snapshot) {
      print(snapshot.connectionState.toString());
      if (snapshot.connectionState == ConnectionState.active) {
        User user = snapshot.data;
        print(user == null);
        if (user == null) {
          print('loading Login Screen ...');
          return LoginScreen();
        } else {
          print('loading Home Screen ...');
          return HomeScreen();
        }
      } else {
        return LoginScreen();
      }
    });
  }
}

现在应用不会多次重新加载,但是当在小部件树下发生 SignOut 时,它不会更改路由。 LandingPage 确实返回了 LoginPage 小部件,但没有发生路由转换,并且应用程序状态与发生 SignOut 的小部件相同。

我可以在 streamBuilder 中进行强制路由转换,但我认为这不是实现它的方式。

【问题讨论】:

    标签: firebase flutter flutter-layout


    【解决方案1】:

    您可以将整个应用程序放在MaterialApp 下,您的其他屏幕(例如LandingPageHome 等)不必是 MaterialApp,而可以只是Scaffold

    【讨论】:

    • 谢谢。但是随后通过路由将发生在顶层,我需要主页只能通过状态更改才能访问
    • 主页只有在状态改变后才能访问。您的路由逻辑将确保这一点。
    【解决方案2】:

    路由应该在 main.dart 的 My app() 中定义

    在 MyApp() 中使用 Stream builder(..home: 像这样:

    请注意,您应该将登录页面和其他页面替换为命名路由,例如 '/login'

    home: StreamBuilder<User>(
        stream: firebaseAuth.authStateChanges(),
        builder: (context, AsyncSnapshot<User> snapshot) {
          checkAndLoginWithSavedCredentials();
          print(snapshot.connectionState.toString());
          if (snapshot.connectionState == ConnectionState.active) {
            //final bool signedIn = snapshot.hasData;
            User user = snapshot.data;
            return user == null ? LoginScreen() : HomeScreen();
            //return signedIn ? DashBoard() : FirstView();
          } else {
            return LoginScreen();
          }
        })
    

    【讨论】:

    • 谢谢。如果我们在 MyApp() 主页中使用路由:我猜不应该使用,不是吗?在这种情况下,您是否引用了一个中间类来处理 StreamBuilder,它将作为 intialRoute 加载?
    • 您介意给我一个示例 MyApp() 类吗?我可能弄错了。
    【解决方案3】:

    我能够通过将“退出”选项移至主屏幕本身来解决此问题。当注销发生在小部件树下时,它不会执行路由。即使 LandingPage 小部件此时返回了 LoginPage,也没有路由更改来运行 LandingPage 类的 Build 方法。

    所以我的解决方案是

    • 删除 /home 和 /login(根据建议)路由,让 authStateChanges() 控制 LandingPage 类返回的小部件。
    • 将 SignOut 选项移至 HomeScreen 本身

    另一种解决方案是在按下退出按钮后执行 Navigator.pop()。但这会在渲染 LoginScreen 之前使 HomeScreen 飞溅一秒。因此我不喜欢它。如果您的 SignOut 调用位于小部件树下,不确定是否有更好的方法。

    【讨论】:

      猜你喜欢
      • 2017-09-24
      • 1970-01-01
      • 1970-01-01
      • 2019-01-05
      • 2021-11-07
      • 2021-12-09
      • 2018-02-04
      • 2018-07-07
      • 2022-01-11
      相关资源
      最近更新 更多