Навигация, когда Flutter FutureProvider решает свое будущее - PullRequest
0 голосов
/ 31 октября 2019

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

FutureProvider получает свое значение от SharedPreferences. Главный экран по умолчанию - это просто логотип со спиннером.

Если идентификатор пользователя принимает значение NULL, приложение должно перейти к экрану входа в систему, в противном случае оно должно вызвать метод, который начнет загрузку данных, а затем по завершении перейдет кглавная страница.

Можно ли этого достичь с помощью FutureProvider?

Я добавляю его в компоновку страницы, чтобы виджет страницы подписывался на поставщика:

  Widget build(BuildContext context) {
    userInfo = Provider.of<UserInfo>(context);
    print('Building with $userInfo'); 
    return PageWithLoadingIndicator();
....

Я добавил его в didChangeDependencies, чтобы отреагировать на изменение:

  @override
  void didChangeDependencies() {
    print('Deps changed: $userInfo');
    super.didChangeDependencies();
//    userInfo = Provider.of<UserInfo>(context); // Already building, can't do this.
//    print('And now: $userInfo');
    if (userInfo == null) return;
    if (userInfo.userId != null) {
      startUp(); // Run when user is logged in
    } else {
      tryLogin(); // Navigate to Login 
    }
  }

И поскольку я не могу использовать Provider.of в initState, я добавил PostFrameCallback

  void postFrame(BuildContext context) {
    print('PostFrame...');
    userInfo = Provider.of<UserInfo>(context);
  }

Main очень просто -сейчас он просто настраивает MultiProvider с одним FutureProvider.

class MyApp extends StatelessWidget {

  Future<UserInfo> getUserInfo() async {
    String token = await UserPrefs.token;
    return UserInfo.fromToken(
      token,
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'App One',
      theme: ThemeData(
        primarySwatch: Colors.purple,
      ),
      home: MultiProvider(
        providers: [FutureProvider<UserInfo>(builder: (_) => getUserInfo())],
        child: LoadingScreen(),
      ),
    );
  }
}

Проблема в том, что, как сейчас, я вижу из операторов печати, что didChangeDependencies вызывается дважды, но значение userInfoвсегда равен null, хотя build () в конечном итоге получает экземпляр UserInfo, что видно из оператора print в методе build ().

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

1 Ответ

0 голосов
/ 12 ноября 2019

Я решил, что это концептуально неправильный подход.

В моем случае я хотел использовать FutureProvider для получения результатов от функции Async, которая создает объект "Config" с использованием SharedPreferences. Затем FutureProvider позволит остальной части приложения получить доступ к настройкам конфигурации пользователя, полученным из sharepreferences.

Мне все еще кажется, что это правильный подход. Но есть проблемы с этим с точки зрения потока приложения.

Преимущественно, что значения из общих настроек включают в себя зарегистрированный токен сеанса пользователя и имя пользователя.

Приложение запускается с отображением экрана загрузки. с круговой шкалой прогресса. Затем приложение считывает общие настройки и подключается к сети, чтобы убедиться, что сеанс действителен. Если сеанс отсутствует или если он недействителен, приложение переходит к «wizzard» для входа в систему, который запрашивает имя пользователя, затем на следующей странице для ввода пароля и затем на следующей странице для двухфакторного входа в систему. После этого он переходит на целевую страницу. Если на странице загрузки обнаружен допустимый сеанс, мастер входа в систему пропускается.

Дело в том, что две вещи - состояние приложения и поток приложения - тангенциально различны. Поток приложения может привести к тому, что изменения будут сохранены в состоянии приложения, но состояние приложения не должно концептуально влиять на поток приложения, по крайней мере, не таким образом. .push () из функции сборки FutureProvider действительна, даже если контекст доступен. Я могу ошибаться в этом, но я чувствовал, что плавный подход более трепетен.

 @override
 void initState() {
   super.initState();
   _loadSharedPrefs().then((_) {
     if(this.session.isValid()) _navToLandingPage();
     else _navToLoginStepOne();
     }
   }

Я открыт для лучших предложений / указаний

...