Как слушатели в Provider работают во Flutter? - PullRequest
0 голосов
/ 29 мая 2020

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

Эти функции и значения находятся в файле с именем auth.provider.dart в каталоге поставщиков в папке lib.

import 'package:flutter/foundation.dart';

class Auth with ChangeNotifier {
  var _loggedIn = false;

  bool get loggedIn => _loggedIn;

  void loginUser() {
    _loggedIn = true;
    print('Logged in');
    notifyListeners();
  }

  void logout() {
    _loggedIn = false;
    print('Logged out');
    notifyListeners();
  }
}

Я предоставляю поставщика в main.dart, а также использую Consumer:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<Auth>(
      create: (_) => Auth(),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: Consumer<Auth>(
          builder: (_, auth, __) {
            print('main.dart logged in: ${auth.loggedIn}');
            if(auth.loggedIn) return HomeScreen();
            return WelcomeScreen();
          },
        ),
        routes: {
          LoginScreen.routeName: (ctx) => LoginScreen(),
          HomeScreen.routeName: (ctx) => HomeScreen(),
        },
      ),
    );
  }
}

В приведенном выше файле я просто проверяю, является ли loggedIn истинным или нет. Если это не так, покажите экран приветствия, чтобы пользователь мог войти в систему, но если пользователь вошел в систему, тогда сразу покажите главный экран. Также поместите оператор печати в конструктор Consumer, чтобы он печатал статус всякий раз, когда он получает уведомление о любых изменениях в данных аутентификации. Это выполняется один раз при запуске приложения и никогда больше.

На экране входа в систему не так много всего, только скаффолд и кнопка входа в систему, которая вызывает функцию входа.

RaisedButton(
    child: Text('Click to Login'),
    onPressed: () {
      Provider.of<Auth>(context, listen: false).loginUser();
    },
),

Теперь по нажатие кнопки входа в систему устанавливает для логического loggedIn значение true и уведомляет слушателя, который должен быть Consumer, но Consumer в main.dart никогда не уведомляется об этом. Это не вызывает изменения, существует условие, которое проверяет, является ли вход в систему истинным, затем показывает главный экран, но экран вообще не меняется. Чтобы перейти на главный экран, нужно вручную использовать Navigator, а на главном экране есть кнопка выхода из системы, которая просто устанавливает для loggedIn значение false, как только это произойдет, он должен уведомить Потребителя, но это снова не так.

Это подводит меня к вопросу: как работают слушатели в Provider? Когда он уведомляет слушателей об изменениях? Не будет ли он уведомлять, если изменения происходят более чем на один уровень ниже, как в приведенном выше примере, Consumer находится в main.dart, а изменения происходят на 2 уровня ниже main.dart > WelcomeScreen > LoginScreen. Как сообщить Потребителю в main.dart, что базовые данные, которые он использует, изменились?

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

1 Ответ

0 голосов
/ 29 мая 2020

Я просмотрел ваш код на github.

С pushReplacementNamed на go на экран входа в систему

Первоначально-> main.dart-> welcomeScreen- > (on-click-loginbtn) -> LoginScreen

Здесь

1) Потребитель не слушает (потому что ваш начальный экран main.dart заменен страницей входа и не существует на странице, которую нужно прослушать в данный момент)

2) Нет навигатора для go назад (потому что страница заменяется не в стеке)

С pushNamed на go to экран входа в систему

Первоначально-> main.dart-> welcomeScreen -> (on-click-loginbtn) -> LoginScreen

Здесь

1) Потребитель слушает (потому что ваш начальный экран main.dart не заменен и существует на странице в настоящее время. Но вы не видите изменений, потому что вы закрыли свою основную начальную страницу экраном входа в стек выше)

2) Есть навигатор на go обратно на HomeScreen (Теперь вы можете go назад, чтобы увидеть изменения используя стрелку назад)

Заключение Страница не сможет прослушивать измененное значение поставщика, если страница заменена другой страницей. Он должен присутствовать при прослушивании экрана.

Шаблон, который можно использовать

main.dart (аргумент home)

home: Consumer<Auth>(
          builder: (_, auth, __) {
            print('main.dart logged in: ${auth.loggedIn}');
            if(auth.loggedIn) return HomeScreen();
            return WelcomeScreen();
          },
        )

Экран приветствия (код входа)

 RaisedButton(
              child: Text('Login'),
              onPressed: () => Navigator.of(context).pushReplacementNamed(LoginScreen.routeName),
            )

Экран входа в систему (кнопка входа)

RaisedButton(
          child: Text('Click to Login'),
          onPressed: () {
            Provider.of<Auth>(context, listen: false).loginUser();
            Navigator.of(context).pushReplacementNamed('/');
          },
        )

HomeScreen (btn выхода)

RaisedButton(
          child: Text('Logout'),
         onPressed: () {
            Provider.of<Auth>(context, listen: false).logout();
            Navigator.of(context).pushReplacementNamed('/');
          }
...