Flutter - Как передать данные пользователя во все представления

Я новичок в мире флаттера и разработки мобильных приложений и борюсь с тем, как передавать пользовательские данные в моем приложении.

Я пробовал несколько вещей, но ни один из них не кажется отличным, и я уверен, что есть лучшие образцы практики, которым я должен следовать.

Поскольку это упрощает примеры, я использую firebase для аутентификации. В настоящее время у меня есть отдельный маршрут для входа в систему. После входа в систему мне нужна модель User в большинстве представлений для проверки разрешений на то, что показывать, отображения информации о пользователе в ящике и т. Д.

У Firebase есть await firebaseAuth.currentUser();. Лучше ли вызывать это везде, где вам может понадобиться пользователь? и если да, то где лучше всего сделать этот звонок?

flutter codelab показывает отличный пример аутентификации пользователей перед разрешением записи. Однако, если странице необходимо проверить аутентификацию, чтобы определить, что строить, асинхронный вызов не может перейти в метод 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, дочерние элементы не обновляются при изменении состояния аутентификации (я уверен, что делаю это неправильно)

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

Буду признателен за любые советы по шаблонам передовой практики или ссылки на ресурсы для использования в качестве примеров.


person Aaron    schedule 28.10.2017    source источник
comment
Есть много разных способов. Но InheritedWidget - интересная функция Flutter для распространения данных. Вы можете найти пример InheritedWidget здесь, gitlab.com/brianegan/scoped_model. Также вы можете рассмотреть возможность использования Redux pub.dartlang.org/search?q=redux   -  person German Saprykin    schedule 29.10.2017


Ответы (5)


Я бы рекомендовал продолжить изучение унаследованных виджетов; в приведенном ниже коде показано, как использовать их с асинхронно обновляемыми данными:

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,
    );
  }
}
person Matt S.    schedule 14.11.2017
comment
Ой, интересно, спасибо за пример! Мои последние попытки заключались в том, чтобы заключить Scaffold в InheritedWidget, чтобы информация была доступна в drawer, appBar и body. Я играл с FutureBuilder вместе с унаследованными виджетами для загрузки контента. - person Aaron; 19.11.2017
comment
Вы пробовали использовать onAuthStateChanges с StreamBuilder? - person Tsortanidis Christos; 23.02.2018
comment
У меня проблема с InheritedWidget - его нельзя использовать с initState(). Перемещение его в didChangeDependencies() помогает, но затем он вызывается несколько раз, даже если виджет больше не активен. В моем случае InheritedWidget содержит оболочку API, поэтому это решение не работает. - person divan; 16.03.2019

Я предпочитаю использовать службы с локатором, используя 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();
}
person Makah    schedule 14.10.2019
comment
как использовать этот подход, когда пользователь повторно откроет приложение, _cachedUser будет уничтожен? - person Merym; 08.07.2020

Я столкнулся с другой проблемой из-за этой проблемы вы можете проверить это здесь Итак, решение, которое я придумал, немного неаккуратное. Я создал отдельную страницу экземпляра dart и импортировал ее на каждую страницу.

 GoogleSignInAccount Guser = googleSignIn.currentUser;
 FirebaseUser Fuser;

Я сохранил пользователя там при входе в систему и проверил каждый StateWidget, если он был нулевым

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

Это мой старый код, который я очистил в своем текущем приложении, но сейчас у меня нет этого кода под рукой. Просто проверьте наличие нулевого пользователя и снова войдите в систему

Я сделал это и для большинства экземпляров Firebase, потому что в моем приложении более трех страниц, а унаследованные виджеты - это слишком много работы.

person PrimeNexes    schedule 07.03.2018

Вы можете использовать пакет GetX, чтобы проверить, вошел ли пользователь в систему, получить данные пользователя и сделать их доступными для всего вашего приложения.

person yulkin    schedule 24.09.2020

Для моего ленивого метода я просто создаю новый файл, например userdata.dart, а затем помещаю в него любую переменную, например, например dynamic Profile = null

внутри userdata.dart

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

в startpage.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());
...
}
person newbe    schedule 10.04.2019
comment
в нем вы можете привести пример предпочтения доли. - person Md Omor Faruqe; 12.04.2021