InheritedWidget - 在 navigator.push 之后在 null 上调用 getter

Posted

技术标签:

【中文标题】InheritedWidget - 在 navigator.push 之后在 null 上调用 getter【英文标题】:InheritedWidget - The getter was called on null after navigator.push 【发布时间】:2019-02-15 04:47:35 【问题描述】:

在导航到新小部件后尝试访问 InheritedWidget 时遇到问题。

我有这样的***小部件

class App extends StatelessWidget
  build(context)
    return MaterialApp(
        title: 'Iniciar Sesion',
        home: LoginBlocProvider(child: WelcomeScreen()),
    );
    

然后 WelcomeScreen 有一个按钮可以导航到 LoginScreen

class WelcomeScreen extends StatelessWidget 

  @override Widget build(BuildContext context)
    return Scaffold(
      body: Center(child: MyButton)
    );
  


class MyButton extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return RaisedButton(
      shape: StadiumBorder(),
      child: Text('Ingresar', style: TextStyle(color: Colors.black)),
      elevation: 5.0,
      onPressed: ()  
        Navigator.of(context).push(MaterialPageRoute(
           builder: (BuildContext context) =>LoginScreen()
        ));
      
    );
  

最后在 LoginScreen 我要访问 InheritedWidget

class LoginScreen extends StatefulWidget 
  @override
  _LoginScreenState createState() => _LoginScreenState();


class _LoginScreenState extends State<LoginScreen> 
  LoginBloc bloc;  

  @override void didChangeDependencies() 
    bloc = LoginBlocProvider.of(context);
    super.didChangeDependencies();
  

  @override Widget build(BuildContext context)
    return Scaffold(
      body:
      Stack(
        fit: StackFit.expand,
        children: <Widget>[
          Positioned(
            top: 0.0,
            child: Image.asset('assets/images/img.jpg',
              fit: BoxFit.none,
            ),
          ),
          _buildLogin(),
        ],
      ),
    );
  

已编辑:这里是 LoginBlocProvider

class LoginBlocProvider extends InheritedWidget 
  final bloc;

  LoginBlocProvider(Key key, Widget child) 
  : bloc = LoginBloc(), 
  super(key:key, child:child);

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) => true;

  static LoginBloc of(BuildContext context) 
    return (context.inheritFromWidgetOfExactType(LoginBlocProvider) as LoginBlocProvider).bloc;
  

但是,当我运行 InheritedWidget 的 .of 方法时,我得到了这个错误

I/flutter (27725): The following NoSuchMethodError was thrown building Builder:
I/flutter (27725): The getter 'bloc' was called on null.
I/flutter (27725): Receiver: null
I/flutter (27725): Tried calling: bloc

我的印象是这一切都与 Navigator.push 构建器方法中的上下文有关。 因为如果我在没有 Navigator.push 的情况下使用 LoginScreen 小部件,我可以完美地使用 InheritedWidget

发生错误是因为传递给LoginBlocProvider.of() 方法的上下文没有找到实例。

对此有什么想法吗?

【问题讨论】:

嗨@塞巴斯蒂安。 1.您的代码没有显示 LoginBlocProvider (请您分享)。 2. 我不明白为什么你会在 didChangeDependencies 中获得这个集团。我宁愿 a) 在 return Scaffold 之前从 build 方法中获取它,或者 b) 添加一个 void initState()super.initState();WidgetsBinding.instance.addPostFrameCallback((_)bloc = LoginBlocProvider.of(context ););你能尝试和/或回答吗?干杯。 @boeledi 我用 LoginBlocProvider 更新了这个问题。我尝试在 didChangeDependencies 方法中获取块,因为之后我将设置一个流侦听器。我没有在 initState() 中这样做,因为我无法访问上下文。 @boeledi 还有一件事。这个问题超出了我对继承的小部件的调用位置。例如,如果我将它放在构建函数中,我会在 null 错误时调用相同的 getter。这就是让我失望的原因。这就是为什么我认为它与 navigator.push 方法有关 @Sebastian 您可以在 State 类中的任何位置访问 context,因此您可以在 initState 中使用它。见这里:docs.flutter.io/flutter/widgets/State/context.html @KirollosMorkos 感谢您的回答,但我遇到的问题不在于访问小部件的上下文(请参阅我的最后一条评论) 【参考方案1】:

在您提供的代码中,LoginScreen 不是LoginBlocProvider 的后代,这就是它找不到祖先小部件的原因。您的代码将WelcomeScreen 路由包装在LoginBlocProvider 中,但不是整个导航器。解决方案是将您的MaterialApp 包装在LoginBlocProvider 中,然后您就可以在应用程序的任何地方访问它。

class App extends StatelessWidget 
  @override
  Widget build(context) 
    return LoginBlocProvider(
      child: MaterialApp(
        title: 'Iniciar Sesion',
        home: WelcomeScreen(),
      ),
    );
  

【讨论】:

有道理,您介意提供更多细节吗?如果我想在导航后放置一个 BlocProvider,我该如何实现? 刚刚更新了我的答案以表明这一点!当您使用 MaterialApp 时,它实际上为您捆绑了许多常用的小部件,包括 Navigator。通过将 LoginBlocProvider 放在 MaterialApp 之外,可以保证应用中的所有路由都可以访问它。 非常感谢您的回答。我试试看 没问题,很高兴我能帮上忙! 如果我想在导航后放置一个 BlocProvider,我该如何实现? @KirollosMorkos【参考方案2】:

InheritedWidget 应该包装完整的 MatrialApp 小部件(根小部件)

【讨论】:

以上是关于InheritedWidget - 在 navigator.push 之后在 null 上调用 getter的主要内容,如果未能解决你的问题,请参考以下文章

Flutter状态管理——InheritedWidget数据共享的原理分析

在 initState 中获取 InheritedWidget 参数

在 Flutter 中使用 InheritedWidget 时 ListView 失去滚动位置

带有 Scaffold 的 InheritedWidget 似乎无法正常工作

Flutter:我应该如何使用 InheritedWidget?

为啥我需要 Flutter 中的 InheritedWidget