Provider中的侦听器如何在Flutter中工作? [英] How does listeners in Provider work in Flutter?

查看:379
本文介绍了Provider中的侦听器如何在Flutter中工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用提供程序包来处理来自不同位置的数据。对于这个问题,我创建了一个示例项目,其中包括一个欢迎屏幕,其中显示一个登录按钮,单击后将重定向到登录屏幕,在该屏幕上将有一个登录文本字段,但现在,我放置了一个按钮,用于单击调用登录功能,该登录功能会通知侦听器。我还有一个注销函数和布尔值,可以跟踪已登录状态。



这些函数和值位于一个名为 auth的文件中。 lib文件夹中provider文件夹中的provider.dart

  import' package:flutter / foundation.dart'; 

类,带有ChangeNotifier的验证{
var _loggedIn = false;

bool获取登录=> _登录;

void loginUser(){
_loggedIn = true;
打印(已登录);
notifyListeners();
}

void logout(){
_loggedIn = false;
打印(已注销);
notifyListeners();
}
}

我在main.dart中也提供了提供程序使用消费者:

  class MyApp扩展了StatelessWidget {
@override
Widget build(BuildContext context){
返回ChangeNotifierProvider< Auth>(
创建:(_)=> Auth(),
子级:MaterialApp(
标题:'Flutter Demo',
主题: ThemeData(
primarySwatch:Colors.blue,
visualDensity:VisualDensity.adaptivePlatformDensity,
),
home:Consumer< Auth>(
builder:(_,auth, __){
print('main.dart登录:$ {auth.loggedIn}');
if(auth.loggedIn)返回HomeScreen();
返回WelcomeScreen();
},
),
路由:{
LoginScreen.routeName:(ctx)=> LoginScreen(),
HomeScreen.routeName:(ctx)=> HomeScreen(),
},
),
);
}
}

在上面的文件中,我只是检查是否< c $ c> loggedIn 是否正确。如果未显示,则显示欢迎屏幕,以便用户可以登录,但是如果用户登录,则直接显示主屏幕。还已在消费者的构建器中添加了打印语句,以便在收到有关身份验证数据的任何更改的通知时打印状态。该操作将在应用程序启动时运行一次,而不再运行。 p>

  RaisedButton(
child:Text('Click to Login'),
onPressed:(){
Provider.of< Auth>(上下文,听:false).loginUser();
},
),

现在单击登录按钮时,它将 loggedIn 布尔值设置为true,并通知侦听器应为Consumer,但应为Consumer。 dart从未收到过通知。它不会触发更改,有一种条件可以检查是否已登录,然后显示主屏幕,但屏幕完全不更改。必须手动使用 Navigator 转到主屏幕,并且在主屏幕上有一个注销按钮,该按钮只需设置 loggedIn 为假,一旦发生,它应该通知消费者,但又不会通知消费者。



现在让我想到一个问题:Provider中的侦听器如何工作?它何时通知侦听器更改?它是否不会通知更改是否发生在一个向下的级别上,例如上例中的Consumer在main.dart中,而更改发生了2个级别在 main.dart>下?欢迎屏幕> LoginScreen 。如何让 main.dart 中的使用者知道其使用的基础数据已更改?



如果您想快速开始使用以上示例,请在此处是存储库链接。

解决方案

我浏览了您的github代码。



使用pushReplacementNamed进入登录屏幕


最初-> main.dart-> welcomeScreen->(在click-loginbtn)-> LoginScreen


这里,



1)消费者没有在听(因为您最初的main.dart屏幕已被登录页面替换,并且当前不在要收听的页面上)



2)没有导航器可以返回(因为页面不是在堆栈中替换的)



使用pushNamed进入登录屏幕


最初-> main.dart-> welcomeScreen->(单击时登录)-> LoginScreen


此处,



1)消费者正在监听(因为最初的main.dart屏幕没有被替换并且当前存在于页面上。但是您没有看到更改,因为您已经用堆栈中的登录屏幕覆盖了您的初始页面)



2)有导航器可以返回HomeScreen(现在您可以使用后退箭头返回以查看更改)



< blockquote>

结论如果该页面被其他页面替换,则该页面将无法收听更改的提供者值。它应该在屏幕上监听。


可以使用的模式



main.dart(home参数)

  home:消费者< ; Auth>(
生成器:(_,auth,__){
print('main.dart登录:$ {auth.loggedIn}');
if(auth.loggedIn) return HomeScreen();
return WelcomeScreen();
},

WelcomeScreen(登录代码)

  RaisedButton(
子级:Text( '登录'),
onPressed:()=> Navigator.of(context).pushReplacementNamed(LoginScreen.routeName),

LoginScreen(登录按钮)

  RaisedButton(
child:Text('Click to Login'),
onPressed:(){
Provider.of< Auth>(上下文,听:假).loginUser();
Navigator.of(context).pushReplacementNamed(’/’);
},

HomeScreen(注销btn)

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


I am using Provider package to work with data from different locations. For this question, I have created a sample project which consists of a Welcome Screen which shows a Login button, upon click, it redirects to Login Screen where one would have login text field but for now, I have placed a button which on click calls the login function and that login function notifies the listeners. I also have a logout function and boolean value which keeps track of the logged in status.

These functions and values reside in a file called auth.provider.dart in providers directory in lib folder.

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

I am providing the provider in main.dart and also using 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(),
        },
      ),
    );
  }
}

In the above file, I simply check whether loggedIn is true or not. If it is not then show the welcome screen so the user can login but if the user is logged in then directly show the home screen. Have also put a print statement in Consumer's builder so it prints the status whenever it gets notified of any changes in auth data. This runs once when the app starts and never again.

There is not much in the login screen, just a scaffold and a login button which calls the login function.

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

Now on login button click, it sets the loggedIn boolean to true and notifies the listener which should be Consumer but Consumer in main.dart is never notified of it. It does not trigger the change, there is a condition which checks if logged in is true then show home screen but screen does not change at all. One has to manually use Navigator to move to home screen and on home screen there is a logout button which simply sets loggedIn to false, once that happens it should notify the Consumer but it once again doesn't.

Which now brings me to the question: How does listeners in Provider work? When does it notify the listeners for changes? Will it not notify if the changes occur more than a level down like in the above example, Consumer is in main.dart and the changes occur 2 levels down main.dart > WelcomeScreen > LoginScreen. How to let the Consumer in main.dart know that the underlying data it is using has changed?

If you want to quickly get started with the above example, here is the repository link.

解决方案

I went through your github code.

With pushReplacementNamed to go to login screen

Initially->main.dart->welcomeScreen->(on-clicking-loginbtn)->LoginScreen

Here,

1)Consumer is not listening(because your initial main.dart screen is replaced by login page and doesnt exist on a page currently to listen)

2)There is no navigator to go back(because page is replaced not in a stack)

With pushNamed to go to login screen

Initially->main.dart->welcomeScreen->(on-clicking-loginbtn)->LoginScreen

Here,

1)Consumer is listening(because your initial main.dart screen is not replaced and exist on a page currently.But you are not seeing the changes because you have covered your main intial page with login screen above in the stack )

2)There is navigator to go back to HomeScreen(Now you can go back to see changes using back arrow)

Conclusion Page will not able to listen to changed Provider Value if the page is replaced by other page. It should be there in a screen listening.

Pattern that can be used

main.dart(home argument)

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

WelcomeScreen(login code)

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

LoginScreen(login button)

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

HomeScreen(signout btn)

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

这篇关于Provider中的侦听器如何在Flutter中工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆