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

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

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

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

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

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

Ответ 1

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

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

Ответ 2

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

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

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

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

Ответ 3

Я предпочитаю использовать Сервисы с Локатором, используя 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();
}

Ответ 4

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

внутри userdata.dart

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

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