Flutter - 如何将用户数据传递到所有视图

时间:2017-10-28 13:22:50

标签: dart firebase-authentication flutter

我是新手和移动应用程序开发的新手,并且在我的应用程序中如何传递用户数据时也很挣扎。

我已经尝试了几件事,但似乎都没有,我确信我应该遵循最佳实践模式。

因为它使示例更容易,我使用firebase进行身份验证。 我目前有一个单独的登录路径。一旦我登录,我想在大多数视图中使用用户模型来检查显示内容的权限,在抽屉中显示用户信息等等。

Firebase有await firebaseAuth.currentUser();最佳做法是在您可能需要用户的任何地方拨打电话吗?如果是的话,这个电话的最佳位置在哪里?

flutter codelab显示了在允许写入之前验证用户的一个很好的示例。但是,如果页面需要检查auth以确定要构建的内容,则异步调用无法进入build方法。

INITSTATE

我尝试过的一种方法是覆盖initState并启动调用以获取用户。当未来完成时,我致电setState并更新用户。

    FirebaseUser user;

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

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

这很有效,但似乎需要它的每个小部件的很多仪式。屏幕在没有用户的情况下加载时也会闪烁,然后在未来完成时与用户进行更新。

将用户传递给构造函数

这也有效,但是有很多模板可以让用户通过可能需要访问它们的所有路由,视图和状态。此外,我们不能在转换路由时执行popAndPushNamed,因为我们无法将变量传递给它。我们必须改变类似的路线:

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

继承的小部件

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

本文展示了使用InheritedWidget的一个很好的模式。当我将继承的小部件放在MaterialApp级别时,当auth状态改变时,子节点不会更新(我确定我做错了)

  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似乎也是一个不错的选择,但似乎每条路线都有很多工作要做。在下面的部分示例中,_authenticateUser()在完成时获取用户和设置状态。

  @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();
        }
      },
    );
  }

我很欣赏有关最佳实践模式的建议或用于示例的资源链接。

5 个答案:

答案 0 :(得分:15)

我建议进一步调查继承的小部件;下面的代码显示了如何使用它们异步更新数据:

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

答案 1 :(得分:0)

由于这个问题我遇到了另一个问题you can check it out here 所以我提出的解决方案有点不整洁,我创建了一个单独的Instance dart页面并将其导入到每个页面。

 GoogleSignInAccount Guser = googleSignIn.currentUser;
 FirebaseUser Fuser;

我在登录时将用户存储在那里并检查每个StateWidget是否为null

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

这是我在我当前的应用程序上清理过的旧代码,但我现在还没有使用该代码。只需签出null用户并再次登录

我也为大多数Firebase实例做了这件事,因为我的应用上有超过3个页面而且继承的小部件工作太多了

答案 2 :(得分:0)

我更喜欢使用Flutter get_it与定位器一起使用服务。

如果愿意,可以使用缓存的数据创建UserService:

class UserService {
  final Firestore _db = Firestore.instance;
  final String _collectionName = 'users';
  CollectionReference _ref;

  User _cachedUser; //<----- Cached Here

  UserService() {
    this._ref = _db.collection(_collectionName);
  }

  User getCachedUser() {
    return _cachedUser;
  }

  Future<User> getUser(String id) async {
    DocumentSnapshot doc = await _ref.document(id).get();

    if (!doc.exists) {
      log("UserService.getUser(): Empty companyID ($id)");
      return null;
    }

    _cachedUser = User.fromDocument(doc.data, doc.documentID);
    return _cachedUser;
  }
}

然后创建一个定位器

GetIt locator = GetIt.instance;

void setupLocator() {
  locator.registerLazySingleton(() => new UserService());
}

并在main()中实例化

void main() {
  setupLocator();
  new Routes();
}

就是这样!您可以使用以下任何一种方法调用Service + cachedData:

.....
UserService _userService = locator<UserService>();

@override
void initState() {
  super.initState();
  _user = _userService.getCachedUser();
}

答案 3 :(得分:0)

您可以使用GetX程序包检查用户是否已登录,获取用户数据并在整个应用程序中对其进行访问

答案 4 :(得分:-1)

对于我懒惰的方法, 我只是创建新文件,例如 userdata.dart ,然后在其上放置任何变量,例如 dynamic Profile = null

userdata.dart

内部
//only put this or anything u want.
dynamic Profile = null;

startingpage.dart

//import that file
import '../userdata.dart';

class startingpage extends ...{
...
//set data to store..
   Profile = 'user profile';
...
}

使用数据只需声明并使用 anotherpage.dart

//import that file
import '../userdata.dart';

class anotherpage extends...{
...
}

class .. State ...{
...
//set the data to variable
   dynamic userdata = Profile;
   print('this is my lazy pass data' + userdata.toString());
...
}