web-dev-qa-db-fra.com

Flutter - Comment transmettre des données utilisateur à toutes les vues

Je suis novice dans le développement d'applications mobiles et d'applications mobiles et je ne sais pas comment transmettre les données des utilisateurs dans mon application.

J'ai essayé plusieurs choses, mais aucune ne semble géniale et je suis sûr qu'il y a des pratiques exemplaires que je devrais suivre.

Pour faciliter les exemples, j’utilise Firebase pour l’authentification . Je dispose actuellement d’une route distincte pour la connexion. Une fois connecté, je veux que le modèle User apparaisse dans la plupart des vues pour vérifier informations utilisateur dans le tiroir, etc ...

Firebase a une await firebaseAuth.currentUser(); Est-il préférable d’appeler cet endroit où vous pourriez avoir besoin de l’utilisateur? et si oui, où est le meilleur endroit pour passer cet appel? 

Le flutter codelab est un bon exemple d'authentification des utilisateurs avant d'autoriser les écritures. Toutefois, si la page doit vérifier l'auth pour déterminer ce qu'il faut construire, l'appel async ne peut pas aller dans la méthode build.

initState

Une méthode que j'ai essayée consiste à remplacer initState et à lancer l'appel pour obtenir l'utilisateur. Lorsque le futur est terminé, j'appelle setState et mets à jour l'utilisateur. 

    FirebaseUser user;

    @override
    void initState() {
      super.initState();
      _getUserDetail();
    }

  Future<Null> _getUserDetail() async {
    User currentUser = await firebaseAuth.currentUser();
    setState(() => user = currentUser);
  }

Cela fonctionne correctement, mais cela ressemble à beaucoup de cérémonie pour chaque widget qui en a besoin. Il y a aussi un clignotement lorsque l'écran se charge sans l'utilisateur, puis est mis à jour avec l'utilisateur à la fin du futur.

Passer l'utilisateur à travers le constructeur

Cela fonctionne aussi, mais il y a beaucoup de passe-partout pour faire passer l'utilisateur à travers tous les itinéraires, vues et leurs états qui pourraient avoir besoin d'y accéder. De plus, nous ne pouvons pas simplement faire popAndPushNamed lors de la transition des routes car nous ne pouvons pas lui passer de variable. Nous devons changer les itinéraires semblables à ceci:

Navigator.Push(context, new MaterialPageRoute(
    builder: (BuildContext context) => new MyPage(user),
));

Widgets hérités

https://medium.com/@mehmetf_71205/inheriting-widgets-b7ac56dbbeb1

Cet article a montré un modèle de Nice pour utiliser InheritedWidget. Lorsque je place le widget hérité au niveau de MaterialApp, les enfants ne sont pas mis à jour lorsque l'état d'authentification a changé (je suis sûr que je ne le fais pas correctement).

  FirebaseUser user;

  Future<Null> didChangeDependency() async {
    super.didChangeDependencies();
    User currentUser = await firebaseAuth.currentUser();
    setState(() => user = currentUser);
  }

  @override
  Widget build(BuildContext context) {
    return new UserContext(
      user,
      child: new MaterialApp(
        title: 'TC Stream',
        theme: new ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: new LoginView(title: 'TC Stream Login', analytics: analytics),
        routes: routes,
      ),
    );
  }

FutureBuilder

FutureBuilder semble également être une option décente, mais semble représenter beaucoup de travail pour chaque route. Dans l'exemple partiel ci-dessous, _authenticateUser() récupère l'utilisateur et l'état de réglage à la fin. 

  @override
  Widget build(BuildContext context) {
    return new FutureBuilder<FirebaseUser>(
      future: _authenticateUser(),
      builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return _buildProgressIndicator();
        }
        if (snapshot.connectionState == ConnectionState.done) {
          return _buildPage();
        }
      },
    );
  }

J'apprécierais tout conseil sur les modèles de meilleures pratiques ou des liens vers des ressources à utiliser pour des exemples.

19
Aaron

Je recommanderais d'enquêter davantage sur les widgets hérités; le code ci-dessous montre comment les utiliser avec des données à mise à jour asynchrone:

import 'Dart:convert';

import 'package:flutter/material.Dart';
import 'package:http/http.Dart' as http;

void main() {
  runApp(new MaterialApp(
      title: 'Inherited Widgets Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new Scaffold(
          appBar: new AppBar(
            title: new Text('Inherited Widget Example'),
          ),
          body: new NamePage())));
}

// Inherited widget for managing a name
class NameInheritedWidget extends InheritedWidget {
  const NameInheritedWidget({
    Key key,
    this.name,
    Widget child}) : super(key: key, child: child);

  final String name;

  @override
  bool updateShouldNotify(NameInheritedWidget old) {
    print('In updateShouldNotify');
    return name != old.name;
  }

  static NameInheritedWidget of(BuildContext context) {
    // You could also just directly return the name here
    // as there's only one field
    return context.inheritFromWidgetOfExactType(NameInheritedWidget);
  }
}

// Stateful widget for managing name data
class NamePage extends StatefulWidget {
  @override
  _NamePageState createState() => new _NamePageState();
}

// State for managing fetching name data over HTTP
class _NamePageState extends State<NamePage> {
  String name = 'Placeholder';

  // Fetch a name asynchonously over HTTP
  _get() async {
    var res = await http.get('https://jsonplaceholder.typicode.com/users');
    var name = JSON.decode(res.body)[0]['name'];
    setState(() => this.name = name); 
  }

  @override
  void initState() {
    super.initState();
    _get();
  }

  @override
  Widget build(BuildContext context) {
    return new NameInheritedWidget(
      name: name,
      child: const IntermediateWidget()
    );
  }
}

// Intermediate widget to show how inherited widgets
// can propagate changes down the widget tree
class IntermediateWidget extends StatelessWidget {
  // Using a const constructor makes the widget cacheable
  const IntermediateWidget();

  @override
  Widget build(BuildContext context) {
    return new Center(
      child: new Padding(
        padding: new EdgeInsets.all(10.0),
        child: const NameWidget()));
  }
}

class NameWidget extends StatelessWidget {
  const NameWidget();

  @override
  Widget build(BuildContext context) {
    final inheritedWidget = NameInheritedWidget.of(context);
    return new Text(
      inheritedWidget.name,
      style: Theme.of(context).textTheme.display1,
    );
  }
}
15
Matt S.

Je suis tombé sur un autre problème à cause de ce problème vous pouvez le vérifier ici La solution que j’ai trouvée est donc un peu désordonnée, j’ai créé une page de fléchette d’instance distincte et l’a importée à chaque page.

 GoogleSignInAccount Guser = googleSignIn.currentUser;
 FirebaseUser Fuser;

J'y ai stocké l'utilisateur lors de la connexion et vérifié sur chaque StateWidget s'il était nul 

  Future<Null> _ensureLoggedIn() async {

if (Guser == null) Guser = await googleSignIn.signInSilently();
if (Fuser == null) {
  await googleSignIn.signIn();
  analytics.logLogin();
}
if (await auth.currentUser() == null) {
  GoogleSignInAuthentication credentials =
  await googleSignIn.currentUser.authentication;
  await auth.signInWithGoogle(
    idToken: credentials.idToken,
    accessToken: credentials.accessToken,
  );
}

C'est mon ancien code que j'ai nettoyé sur mon application actuelle, mais je n'ai pas ce code à portée de main. Il suffit de vérifier pour un utilisateur nul et le reconnecter

Je l'ai fait aussi pour la plupart des instances Firebase car j'ai plus de 3 pages sur mon application et Inherited Widgets était tout simplement trop dur.

0
Prime