1
votes

Authentification Flutter Firebase: délai au démarrage

J'utilise Provider et le flux FirebaseAuth.instance.onAuthStateChanged dans l'application pour décider où rediriger au démarrage, mais bien que l'utilisateur soit déjà connecté (à partir d'un démarrage précédent), l'application démarre sur l'écran de connexion et presque 1 seconde plus tard redirige à la page d'accueil, à partir de laquelle il aurait dû commencer dès le premier instant. Cela se produit même en mode avion.

Je voudrais savoir s'il existe une approche pour résoudre cela, même s'il n'est pas possible d'afficher l'écran d'accueil à la fois, je ne sais pas comment faire la différence entre l'utilisateur non connecté (null-> écran de connexion) et le chargement user (null-> écran de chargement).

Certains des codes:

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final FirebaseAuth _auth = FirebaseAuth.instance;
  final DatabaseService db = DatabaseService();
  @override
  Widget build(BuildContext context) {
    return StreamProvider<FirebaseUser>.value(
      value: _auth.onAuthStateChanged,
      child: Consumer<FirebaseUser>(
        builder: (context, firebaseUser, child) {
          return MultiProvider(
            providers: [
              if (firebaseUser != null)
                ChangeNotifierProvider(create: (ctx) => CollectionState(firebaseUser)),
              StreamProvider<List<Collection>>.value(value: db.streamCollections(firebaseUser)),
            ],
            child: MaterialApp(
              title: 'My App',
              routes: {
                '/': (ctx) => LandingPage(),
                '/login': (ctx) => LoginPage(),
                '/emailSignIn': (ctx) => EmailSignInPage(),
                '/emailSignUp': (ctx) => EmailSignUpPage(),
                '/emailUnverified': (ctx) => EmailUnverifiedPage(),
                '/home': (ctx) => HomePage(),
                '/settings': (ctx) => Settings(),
              },
            ),
          );
        },
      ),
    );
  }
}

class LandingPage extends StatelessWidget {
  final DatabaseService _db = DatabaseService();
  @override
  Widget build(BuildContext context) {
    final user = Provider.of<FirebaseUser>(context);
    final userCondition =
        user == null ? 'null' : user.isEmailVerified ? 'verifiedUser' : 'unverifiedUser';

    switch (userCondition) {
      case 'null':
        return LoginPage();
        break;
      case 'unverifiedUser':
        return EmailUnverifiedPage();
        break;
      case 'verifiedUser':
        return HomePage();
        break;
    }
  }
}

Le code est un peu simplifié, j'utilise plutôt un service pour l'instance d'authentification, rien que ça.


2 commentaires

SharedPreferrence


Merci pour votre réponse meditat. Pouvez-vous expliquer un peu plus?


3 Réponses :


0
votes

Mais bien que l'utilisateur soit déjà connecté (à partir d'un démarrage précédent), l'application démarre sur l'écran de connexion et près d'une seconde plus tard, redirige vers la page d'accueil, à partir de laquelle elle aurait dû démarrer dès le premier moment.

Dans ce type de situation, la meilleure pratique consiste à utiliser un wrapper qui décidera de la navigation en fonction du flux FirebaseAuth.instance.onAuthStateChanged

Explication:

Comme vous le savez peut-être, FirebaseAuth.instance.onAuthStateChanged qui donne un FirebaseUser lorsque nous sommes connectés et donne null lorsque ce n'est pas le cas. Donc, sur la base de ce flux, je retourne le widget. Si le flux donne un FirebaseUser et n'est pas null ce qui signifie clairement que l'utilisateur est connecté, retournez la page d' HomePage() sinon si l'utilisateur n'est pas connecté, retournez simplement LoginPage()

Vous n'avez donc pas à vous soucier des utilisateurs qui sont connectés, accédez à la page de LoginPage puis après une deuxième redirection vers la page d' HomePage . StreamBuilder sera reconstruit à chaque fois qu'il y a un changement dans le flux ( FirebaseAuth.instance.onAuthStateChanged ne changera que lors de la connexion ou de la déconnexion). Si un utilisateur se déconnecte, StreamBuilder se reconstruira et retournera LoginPage afin que vous n'ayez pas besoin de naviguer manuellement vers LoginPage .

void main() {
  runApp(MaterialApp(
    debugShowCheckedModeBanner: false,
    home: Home(),
  ));
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
        stream: FirebaseAuth.instance.onAuthStateChanged,
        builder: (_, snapshot) {
          if (snapshot.data is FirebaseUser && snapshot.data != null) {
            return HomePage();
          }
          return LoginPage();
        });
  }
}

2 commentaires

Merci pour votre réponse. J'ai essayé votre code, mais le problème persiste, pour les utilisateurs connectés, il affiche toujours la page de connexion pendant environ une seconde, puis passe à la page d'accueil.


Si vous le pouvez, je veux voir tous vos codes sur Github.



1
votes

Je sais que je suis très en retard, mais j'ai eu le même problème pendant des semaines et je l'ai finalement compris.

@ChinkySight a raison lorsqu'il dit qu'il est préférable d'utiliser un StreamBuilder , principalement parce que vous avez accès à la propriété connectionState.

La raison pour laquelle un décalage existe est que la connexion au flux n'est pas complètement établie. Ainsi, pendant ConnectionState.waiting, retournez un widget comme un écran de démarrage ou juste un conteneur.

return StreamBuilder(
      stream: FirebaseAuth.instance.onAuthStateChanged,
      builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
        Widget widget;

        if (snapshot.connectionState == ConnectionState.waiting) {
          return Container();
        }

        switch (snapshot.hasData) {
          case (true):
            widget = HomePage();
            break;
          case (false):
            widget = LoginPage();
        }
        return Stack(
          children: <Widget>[
            Scaffold(
              backgroundColor: Colors.grey.shade200,
            ),
            AnimatedSwitcher(
              duration: Duration(milliseconds: 700),
              child: FadeTransition(
                  opacity: animation,
                  child: widget,
                ),
               );
              },
            )
          ],
        );
      },
    );

Vous pouvez même donner à vos déclarations de retour des animations fantaisistes avec le sélecteur animé

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
        stream: FirebaseAuth.instance.onAuthStateChanged,
        builder: (_, snapshot) {
          // Added this line
          if (snapshot.connectionState == ConnectionState.waiting) {
            return Container();
          }
          if (snapshot.data is FirebaseUser && snapshot.data != null) {
            return HomePage();
          }
          return LoginPage();
        });
  }
}


2 commentaires

Le sélecteur animé ne fonctionne pas, donc le code a été modifié pour: return AnimatedSwitcher (durée: durée (millisecondes: 300), enfant: widget,);


C'est une erreur. Son enfant est censé être FadeTransition. Permettez-moi de modifier le code. EDIT: édité !!



1
votes

Cela fonctionne pour FlutterFire .

Firebase Auth vous permet de vous abonner en temps réel à cet état via un Stream. Une fois appelé, le flux fournit un événement immédiat de l'état d'authentification actuel de l'utilisateur, puis fournit des événements ultérieurs chaque fois que l'état d'authentification change. Pour vous abonner à ces modifications, appelez la méthode authStateChanges () sur votre instance FirebaseAuth:

import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:flutter/material.dart';
import 'menu.dart';
import 'login.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:firebase_core/firebase_core.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitUp]);
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'TestApp',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: 
        StreamBuilder<auth.User>(
          stream: auth.FirebaseAuth.instance.authStateChanges(),
          builder: (BuildContext context, AsyncSnapshot<auth.User> snapshot) { 
            if(snapshot.hasData) {
              print("data exists");
              return HomePage();
            }
            else {
              return LoginPage();
            }
          },
        )
    );
  }
}


1 commentaires

C'est beau