Управление состоянием провайдера флаттера, концепция выхода из системы

Я пытаюсь реализовать индивидуальное решение для выхода из системы для своего приложения, где независимо от того, где в данный момент находится пользователь, после нажатия Logout button приложение вернется к Login page.

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

Для простоты я сократил количество элементов до минимума. Вот как мог бы выглядеть мой класс Profile:

class Profile with ChangeNotifier {
  bool _isAuthentificated = false;
  bool get isAuthentificated => _isAuthentificated;
  set isAuthentificated(bool newVal) {
    _isAuthentificated = newVal;
    notifyListeners();
  }
}

Теперь под Main я зарегистрировал этого провайдера следующим образом:

void main() => runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(
            create: (_) => Profile(),
          )
        ],
        child: MyApp(),
      ),
    );

И, наконец, MyApp компонент:

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return Consumer<Profile>(
      builder: (context, profile, _) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            brightness: Brightness.light,
            primaryColor: Color.fromARGB(255, 0, 121, 107),
            accentColor: Color.fromARGB(255, 255, 87, 34),
          ),
          home: buildBasePage(context, profile),
        );
      },
    );
  }

  Widget buildBasePage(BuildContext context, Profile currentProfile) {
    return !currentProfile.isAuthentificated
        ? LoginComponent()
        : MyHomePage(title: 'Flutter Demo Home Page test');
  }
}

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

Что происходит, когда я, например, MyHomePage, и я нажимаю Logout() метод, который выглядит следующим образом:

  void _logout() {
    Provider.of<Profile>(context, listen: false).isAuthentificated = false;
  }

Я ожидал, что при изменении свойства начальный компонент MyApp отреагирует и сгенерирует LoginPage; что не так. Я пробовал перейти с Consumer на Provider.of<Profile>(context, listen: false), но с тем же результатом.

Что мне нужно сделать, чтобы эта концепция заработала? Это вообще правильно?

Я имею в виду, что я мог бы точно обновить свой класс Profile, добавив следующий метод:

  logout(BuildContext context) {
    _isAuthentificated = false;

    Navigator.push(
        context, MaterialPageRoute(builder: (context) => LoginComponent()));
  }

А затем просто позвоните Provider.of<Profile>(context, listen: false).logout(), но я думал, что пакет Provider был разработан для этого ... или я что-то упускаю?

Любая помощь в этом вопросе будет более чем признательна.


person Robert J.    schedule 07.02.2020    source источник
comment
В целом, я не думаю, что буду использовать эту идею. Невозможно управлять анимацией переходов между страницами. Это просто внезапное переключение.   -  person Ted Henry    schedule 08.02.2020


Ответы (2)


Я не знаю, почему у тебя это не сработало. Вот полный пример, который я построил на основе вашего описания. Оно работает!

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class Profile with ChangeNotifier {
  bool _isAuthentificated = false;

  bool get isAuthentificated {
    return this._isAuthentificated;
  }

  set isAuthentificated(bool newVal) {
    this._isAuthentificated = newVal;
    this.notifyListeners();
  }
}

void main() {
  return runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider<Profile>(
          create: (final BuildContext context) {
            return Profile();
          },
        )
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(final BuildContext context) {
    return Consumer<Profile>(
      builder: (final BuildContext context, final Profile profile, final Widget child) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(primarySwatch: Colors.blue),
          home: profile.isAuthentificated ? MyHomePage() : MyLoginPage(),
        );
      },
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(final BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Home [Auth Protected]")),
      body: Center(
        child: RaisedButton(
          child: const Text("Logout"),
          onPressed: () {
            final Profile profile = Provider.of<Profile>(context, listen: false);
            profile.isAuthentificated = false;
          },
        ),
      ),
    );
  }
}

class MyLoginPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Login")),
      body: Center(
        child: RaisedButton(
          child: const Text("Login"),
          onPressed: () {
            final Profile profile = Provider.of<Profile>(context, listen: false);
            profile.isAuthentificated = true;
          },
        ),
      ),
    );
  }
}
person Ted Henry    schedule 07.02.2020
comment
Какую версию пакета провайдера вы использовали? Я использовал 4.0+ - person Robert J.; 08.02.2020
comment
Я использовал самую последнюю. ^ 4.0.3 - person Ted Henry; 08.02.2020
comment
Интересно; когда я копирую и вставляю ваше решение в свой код, теперь оно работает как шарм! В любом случае я отмечаю ваше решение как выполненное. Спасибо за помощь - person Robert J.; 08.02.2020

Вам не нужно передавать listen:false, вместо этого просто вызовите

Provider.of<Profile>(context).logout()

Итак, ваш класс профиля будет выглядеть так

      class Profile with ChangeNotifier {
  bool isAuthentificated = false;


  logout() {
    isAuthentificated = false;
    notifyListeners();
  }
}
person Darish    schedule 07.02.2020
comment
Но моя проблема в том, что с текущей реализацией ничего не происходит. Что мне нужно изменить, чтобы пакет Provider работал? - person Robert J.; 07.02.2020
comment
он должен работать в соответствии с документацией пакета поставщика, в противном случае откройте проблему на github для пакета поставщика. - person Darish; 07.02.2020