Flutter: доступ к поставщику SharedPreferences / ChangeNotifier в классе Stream

Я осмотрелся в StackoverFlow и не смог найти решение этой проблемы. Сценарий: у меня есть провайдер Flutter SharedPreferences с классом ChangeNotifier, который будет обновляться с учетом информации о текущем вошедшем в систему пользователе.

Упрощенное содержание:

class SharedPreferences {
  final String userId;
  final String userName;

  SharedPreferences({
    @required this.userId,
    @required this.userName,
  });
}
class SharedPreferencesData with ChangeNotifier {
  var _sharedPreferencesData = SharedPreferences(
    userId: 'testUserId',
    userName: 'testUserName',
  );}

И файл database.dart с классом, содержащий DataBaseServices для получения потоков FireStore из снимков:

class DatabaseService {
  final CollectionReference companiesProfilesCollection =
      Firestore.instance.collection('companiesProfiles');

  List<CompanyProfile> _companiesProfilesFromSnapshot(QuerySnapshot snapshot) {
    return snapshot.documents.map((doc) {
      return CompanyProfile(
        docId: doc.documentID,
        companyName: doc.data['companyName'] ?? '',
        maxLocationsNumber: doc.data['maxLocationsNumber'] ?? 0,
        maxUsersNumber: doc.data['maxUsersNumber'] ?? 0,
      );
    }).toList();
  }

  Stream<List<CompanyProfile>> get getCompaniesProfiles {
    return companiesProfilesCollection
        .where('userId', isEqualTo: _userIdFromProvider) 
        // My problem is above -----
        .snapshots()
        .map(_companiesProfilesFromSnapshot);
  }
}

Я не хочу получать все данные Stream, поскольку они могут быть огромными для других потоков, я просто хочу передать идентификатор пользователя в .where ('userId', isEqualTo: _userIdFromProvider).

  • Мне не удалось получить доступ к контексту в этом классе, чтобы получить данные от поставщика.

  • Не удалось отправить userId в средство получения getCompaniesProfiles, поскольку средство получения не принимает параметры

  • И если я конвертирую этот метод получения в обычный метод, я не смогу отправить ему идентификатор пользователя, так как он должен выполняться под void main () {runApp (MyApp ());} / return MultiProvider (Provider: [ и к тому времени я не могу вызвать fetch sharedPreferences с контекстом, который не содержит информацию о поставщике ...

  • Не могу понять, как получить контекст в качестве конструктора в этом классе, когда я это сделал, я получил следующие Только статические члены могут быть доступны в инициализаторах в классе DatabaseService.

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

*********** Отредактировано следующим образом: ************** Я пытаюсь реализовать тот же сценарий, вот мой код: Главный файл :

return MultiProvider(
  providers: [
    ChangeNotifierProvider<SpData>(
      create: (context) => SpData(),
    ),
    ProxyProvider<SpData, DS>(
      create: (context) => DS(),
      update: (ctx, spData, previousDS) {
        print('ChangeNotifierProxyProvider RAN');
        previousDS.dbData = spData;
        return previousDS;
      },
    ),
  ],

Файл SP:

class SP {
  final String companyId;
  SP({
    @required this.companyId,
  });
}

class SpData with ChangeNotifier {
  var _sPData = SP(
    companyId: '',
  );
  void setCompanyId(String cpID) {
    final newSharedPreferences = SP(
      companyId: cpID,
    );
    _sPData = newSharedPreferences;
    print('_spData companyId:${_sPData.companyId}');
    notifyListeners();
  }

  String get getCompanyId {
    return _sPData.companyId;
  }
}

Файл DS:

class DS with ChangeNotifier {
  SpData dbData;
  void printCompanyId() {
    var companyId = dbData.getCompanyId;
    print('companyId from DataBase: $companyId');
  }
}

SpData dbData; внутри Class DS не обновляется. Я добавил распечатки, чтобы понять, что работает, а что нет. Когда я запускаю свой код, функция печати в файле main.dart print ('ChangeNotifierProxyProvider RAN'); не запускается.

Что мне не хватает? Почему не запускается ChangeNotifierProxyProvider для обновления dbData внутри файла DS? Спасибо!


person YaGeorge    schedule 14.07.2020    source источник


Ответы (1)


Вы можете использовать ProxyProvider для этой цели.

ProxyProvider - поставщик, который строит ценность на основе других поставщиков.

Вы сказали, что у вас есть MultiProvider, поэтому я думаю, у вас есть SharedPreferencesData провайдер в этом MultiProvider, а затем DatabaseService провайдер. Что вам нужно сделать, так это использовать ProxyProvider для DatabaseService вместо обычного провайдера и основывать его на SharedPreferencesData провайдере.

Вот пример:

MultiProvider(
  providers: [
    ChangeNotifierProvider<SharedPreferencesData>(
      create: (context) => SharedPreferencesData(),
    ),
    ProxyProvider<SharedPreferencesData, DatabaseService>(
      create: (context) => DatabaseService(),
      update: (context, sharedPreferencesData, databaseService) {
        databaseService.sharedPreferencesData = sharedPreferencesData;
        return databaseService;
      },
      dispose: (context, databaseService) => databaseService.dispose(),
    ),
  ],
  child: ...

Вот что происходит в приведенном выше фрагменте кода:

  1. ProxyProvider вызывает update каждый раз, когда SharedPreferencesData изменения.
  2. DatabaseService получает свою sharedPreferencesData переменную, установленную внутри update.

Теперь, когда у вас есть доступ к sharedPreferencesData внутри экземпляра DatabaseService, вы можете легко делать то, что хотите.

person Shahin Mursalov    schedule 14.07.2020
comment
Такой подход абсолютно законен. Но у меня это не сработало, возможно, это связано с какой-то неправильной реализацией, которую я делаю, вот почему: когда sharedPreferencesData впервые передается в DatabaseService, он пуст. Я не использую notifyListeners в sharedPreferencesData из-за получаемой мной ошибки: ошибка при сборке: невозможно notifyListeners = ›setState =› перестроить во время сборки приложения. Тем не менее, мое приложение отлично работает без notifyListeners, потому что sharedPreferencesData содержит переменные, которые будут обновляться только при запуске приложения и входе в систему. Так что я снова застрял, как это исправить. - person YaGeorge; 15.07.2020
comment
Я думаю, вам следует исправить проблему с вашим notifyListeners. Эта ошибка говорит мне, что вы вызываете notifyListeners в своем build методе, что неверно. - person Shahin Mursalov; 15.07.2020
comment
Привет, Шахин, я решил проблему с notifyListeners с помощью WidgetsBinding.instance.addPostFrameCallback. Теперь sharedPreferencesData должен уведомлять слушателей и, следовательно, обновляться для уведомления провайдера прокси. Я сделал следующее: - person YaGeorge; 16.07.2020
comment
ChangeNotifierProvider.value (значение: SharedPreferencesData (),), ProxyProvider ‹SharedPreferencesData, DatabaseService› (create: (context) = ›DatabaseService (), update: (context, sharedPreferencesData, databaseService) {databaseService.sharedPreferencesData = sharedPreferencesData; , // dispose: (context, databaseService) = ›databaseService.dispose (),). Отметив, что dispose дал мне: метод dispose не определен для типа DatabaseService. - person YaGeorge; 16.07.2020
comment
Подскажите, как реализовать Dispose в DatabaseService? И в DatabaseService: класс DatabaseService {SharedPreferencesData sharedPreferencesData; final sPData = SharedPreferencesData (). getSharedPreferencesData (); ....} Я добавил код печати в DatabaseService StreamProvider, но при запуске моего приложения я получаю следующее: I / flutter (6795): sPData.companyId: пусто I / flutter (6795): sPData. companyId: пусто Выполняется 2 раза, возможно, один раз при первой загрузке приложения, а второй - после уведомлений. Но sPData.companyId по-прежнему остается пустым в DatabaseService. - person YaGeorge; 16.07.2020
comment
Вы должны избавиться от своих ресурсов dispose методом, чтобы предотвратить утечку памяти. Если у вас нет ресурсов для удаления, просто создайте пустой метод dispose или просто прекратите вызывать метод dispose в провайдере прокси. - person Shahin Mursalov; 16.07.2020
comment
Что касается другой вашей проблемы, я не могу много сказать, не увидев больше вашего кода. - person Shahin Mursalov; 16.07.2020
comment
Я отредактировал свой пост, добавив очень простой код, чтобы все работало, а затем применил его к своему приложению. Если можно, просто проверьте, что еще не так. Спасибо! - person YaGeorge; 17.07.2020
comment
Дело в том, что update метод должен вызываться только в случае изменения самого SharedPreferencesData, а не одного из его атрибутов. Поэтому не назначайте явно один из его атрибутов для DatabaseService. Присвойте SharedPreferencesData себя DatabaseService в качестве атрибута и используйте для него геттер всякий раз, когда вам нужен идентификатор компании. Взгляните на метод update в моем ответе, и вы поймете. - person Shahin Mursalov; 17.07.2020
comment
Я просто не понимаю и не могу заставить это работать. в main.dart метод ChangeNotifierProxyProvider / update никогда не запускается, что бы я ни делал, независимо от того, что находится внутри, функция печати просто не запускается. Я новичок, мне действительно нужно решить эту проблему, и я уже давно пытаюсь ... Я отредактировал свой вопрос, пожалуйста, найдите очень упрощенную версию и дайте мне знать, что не так. - person YaGeorge; 21.07.2020
comment
Единственная ошибка в обновленном коде - использование ProxyProvider вместо ChangeNotifierProxyProvider. Я протестировал ваш код, и он отлично работает. Я предполагаю, что ваш update метод не вызывается, потому что вы нигде не используете Provider.of<DS>(context). Имейте в виду, что обратные вызовы create / update загружаются лениво, что означает, что они вызываются при первом чтении значения, а не при первом создании поставщика. Если вы хотите отключить эту функцию, вы можете добавить lazy: false к своим провайдерам. - person Shahin Mursalov; 22.07.2020
comment
Это именно то, что я сделал вчера, используя lazy: false, чтобы все заработало. а затем я перенастроил свой код, чтобы он работал без ленивой конфигурации. Спасибо, Шахин! ты все сделал отлично! - person YaGeorge; 22.07.2020
comment
Еще одна вещь, я использовал var dB = Provider.of<DatabaseService>(context, listen: false);. При попытке получить мой список компаний-профилей с помощью var cp= dB.getCompaniesProfiles ; тип cp будет выглядеть так: Stream ‹List ‹CompanyProfile›› вместо ‹List ‹CompanyProfile››, как я всегда получал раньше от DataBaseServices. Отметив, что я смог получить доступ к данным sharedpre с помощью dB.sPData.getSharedPreferencesData().companyId. Но как теперь получить свой поток / списки из DatabaseService? - person YaGeorge; 22.07.2020
comment
мне нужно изменить что-то в моем Провайдере Профиля Компании ?: StreamProvider<List<CompanyProfile>>.value( value: DatabaseService().getCompaniesProfiles, initialData: List(),), - person YaGeorge; 22.07.2020
comment
Обновлено до: StreamProvider<List<CompanyProfile>>.value(value: Provider.of<DatabaseService>(bCtx, listen: false).getCompaniesProfiles,initialData: List(),), Кажется, работает, но много ошибок, так как весь мой код был основан непосредственно на DataBaseService (). Это правильный подход? прежде чем я полностью конвертирую все свое приложение, чтобы слушать этот единственный ChangeNotifierProxyProvider<SharedPreferencesData, DatabaseService>? .. это означает, что я всегда должен использовать этого поставщика для доступа к экземпляру DataBaseService + SharedPreferencesData вместо прямого использования DataBaseService? - person YaGeorge; 22.07.2020