在 initState 中获取 InheritedWidget 参数

Posted

技术标签:

【中文标题】在 initState 中获取 InheritedWidget 参数【英文标题】:Get InheritedWidget parameter in initState 【发布时间】:2019-12-01 16:34:57 【问题描述】:

我需要一些帮助来了解如何从继承的小部件中获取数据。

我通常使用

直接从构建方法中从我的小部件中获取参数
  @override
  Widget build(BuildContext context)     
   //THIS METHOD
   var data = StateContainer.of(context).data;

   return Container(child:Text("$data.parameter"));
  

但由于还没有 buildContext,因此无法从 initState 调用此方法。

我需要在 initState 方法中具有该参数(我在其中调用从服务器获取的数据,并且我需要将该数据传递给我的函数),那么,我应该怎么做呢?

@override
void initState() 
 otherData = fetchData(data);
 super.initState();

我尝试使用 didChangeDipendencies(),但每次重建视图(从屏幕弹出等)时都会调用它,所以它不是我想要使用的,也不是 FutureBuilder 小部件。

有什么建议吗?

【问题讨论】:

【参考方案1】:

首先,请注意您可能确实想要使用didChangeDependencies。但是你不能在没有任何检查的情况下就在那里打电话。您需要先将其包装在 if 中。

典型的didChangeDependencies 实现应该类似于:

Foo foo;

@override
void didChangeDependencies() 
  super.didChangeDependencies();
  final foo = Foo.of(context);
  if (this.foo != foo) 
    this.foo = foo;
    foo.doSomething();
  

使用这样的代码,doSomethingfoo 更改时执行。


或者,如果您很懒惰并且确定您的对象永远不会改变,那么还有另一种解决方案。

获取InheritedWidget,常用的方法是:

BuildContext context;
InheritedWidget foo = context.inheritFromWidgetOfExactType(Foo);

而在initState内部不能调用的正是这个方法。

但是还有另一种方法可以做同样的事情:

BuildContext context;
InheritedWidget foo = context.ancestorInheritedElementForWidgetOfExactType(Foo)?.widget;

转折是: - 这个方法可以在initState里面调用 - 它不会处理值改变的场景。

所以如果你的值永远不会改变,你可以使用它来代替。

【讨论】:

【参考方案2】:

1、如果你只需要InheritedWidget作为Widget参数的Provider。 您可以在 initState 上使用如下:

@override
void initState() 
    super.initState();
    var data = context.ancestorInheritedElementForWidgetOfExactType(type)?.widget;

2、当InheritedWidget的数据发生变化时,如果需要监听器重新渲染widget。我建议您将 StatefulWidget 内部人员包装为 StatelessWidgetStatefulWidget的参数是从StatelessWidget传过来的,当InheritedWidget改变数据时,会通知StatelessWidget,在StatefulWidget 我们将在 didChangeDependencies 上获得更改,您可以刷新数据。 这是代码指南:

class WrapperDemoWidget extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    DemoData data = StateContainer.of(context).data;
    return Container();
  


class ImplementWidget extends StatefulWidget 

  DemoData data;

  ImplementWidget(this.data);

  @override
  _ImplementWidgetState createState() => _ImplementWidgetState();


class _ImplementWidgetState extends State<ImplementWidget> 

  @override
  void initState() 
    super.initState();
   //TODO Do sth with widget.data
  

  @override
  void didChangeDependencies() 
    super.didChangeDependencies();
    //TODO Do change with widget.data
  

  @override
  Widget build(BuildContext context) 
    return Container();
  

【讨论】:

【参考方案3】:

我更喜欢didChangeDependencies 的解决方案,因为Future.delayed 解决方案有点hack,看起来不专业且不健康。但是,它开箱即用。

这是我更喜欢的解决方案:

class _MyAppState extends State<MyApp> 

    bool isDataLoaded = false;

    @override
  void didChangeDependencies() 
    if (!isDataLoaded) 
            otherData = fetchData(data).then((_)
                this.isDataLoaded = true;
            );
    
    super.didChangeDependencies();
  
...

【讨论】:

虽然比 Future.delayed 更好,但布尔值错过了生命周期的要点,即处理数据更改的场景。【参考方案4】:

您还可以在 initState 中获取上下文,尝试使用持续时间为零的未来。你可以找到一些例子here

void initState() 
    super.initState();
      Future.delayed(Duration.zero,() 
        //use context here
      showDialog(context: context, builder: (context) => AlertDialog(
          content: Column(
            children: <Widget>[
              Text('@todo')
            ],
          ),
          actions: <Widget>[
            FlatButton(onPressed: ()
              Navigator.pop(context);
            , child: Text('OK')),
          ],
        ));
      );
  

我使用它来使用继承的小部件制作加载屏幕并避免一些全局变量

【讨论】:

以上是关于在 initState 中获取 InheritedWidget 参数的主要内容,如果未能解决你的问题,请参考以下文章

在构建之前使用 initState 从函数中获取值并插入到变量中

如何在 initState 方法中访问颤振块?

Flutter:异步任务在“initState()”中无法按预期工作

将参数传递给 initState

在颤振中在 initstate() 之前调用了dependOnInheritedElement()

如何在flutter中调用initState中的异步方法