【问题标题】:Flutter calling child class function from parent classFlutter 从父类调用子类函数
【发布时间】:2019-05-10 14:14:38
【问题描述】:

代码

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          leading: IconButton(
            icon: Icon(Icons.help),
            onPressed: () {
              // how can I call methodA from here?
            },
          ),
        ),
        body: HomePage(),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }

  void methodA() {}
}

问题: 我想从IconButtononPressed() 打电话给methodA()。我该怎么做,我知道如何从子类调用父类的方法,但是这个用例是不同的。

【问题讨论】:

  • 您可以将 GlobalKey 传递给HomePage(...) 并使用此密钥访问HomePage 的状态。最好使用一些事件传播机制(BloC 和 redux 在这里有所帮助,但有很多方法可以做到这一点)
  • @GünterZöchbauer 感谢您的提示,我知道ScopedModel,这对这里也有帮助吗? IMO,这无济于事,因为它用于将数据向下传递。
  • 从未仔细观察过。您还可以使用普通的StreamController 并将其流传递给子组件,并在子组件中侦听流。在您添加到StreamController 的父级中,侦听器(子级)为每个事件调用methodA
  • @GünterZöchbauer 我使用了GlobalKey 的方法,代码现在可以工作了,你能检查一下我的方法是否正确吗?
  • 是的,没关系。

标签: dart flutter


【解决方案1】:

2021

我相信这是使用 typedef 和类似构建器的小部件的最强大的解决方案。

import 'package:flutter/material.dart';

typedef MyBuilder = void Function(BuildContext context, void Function() methodA);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  late void Function() myMethod;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          leading: IconButton(
            icon: Icon(Icons.help),
            onPressed: () {
              // how can I call methodA from here?
              myMethod.call();
            },
          ),
        ),
        body: HomePage(builder: (BuildContext context, void Function() methodA) {
          myMethod = methodA;
        },),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  final MyBuilder builder;

  const HomePage({Key? key, required this.builder}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    widget.builder.call(context, methodA);
    return Text('HomePage');
  }

  void methodA() {
    print('test');
  }
}

【讨论】:

    【解决方案2】:

    上面的答案对我不起作用,我只是替换了全局键,效果很好:

    class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
     return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          leading: IconButton(
            icon: Icon(Icons.help),
            onPressed: () {
             //call it like this:
             HomePage.globalKey.currentState.methodA();
            },
          ),
        ),
        body: HomePage(),
      ),
    );
     }
    }
    
    
    class HomePage extends StatefulWidget {
    //Add this key like this:
    static final GlobalKey<_HomePageState> globalKey = GlobalKey();
    super(key: globalKey);
    
    @override
     _HomePageState createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
     @override
      Widget build(BuildContext context) {
      return Container();
     }
    
     void methodA() {}
    }
    

    希望一切正常:)

    【讨论】:

    • 谢谢你,这真的给了我长期搜索的答案。
    • 应为班级成员。尝试将此代码放在类成员中。
    • 我很欣赏这个很棒的答案! :)
    【解决方案3】:

    我认为这种情况下的最佳实践是使用 Flutter 的程序员自己使用的方式!

    下面的代码是来自 Flutter source 的示例。正如您在 MyCustomForm 小部件中看到的那样,如果您想访问 TextField 小部件 的文本属性(在本例中是 MyCustomForm 小部件的子级) em> 你需要像这样使用它的控制器......

    class _MyCustomFormState extends State<MyCustomForm> {
      ...
      final myController = TextEditingController();
      ...
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          ...
          body: TextField(
            controller: myController,
          ),// TextField
          ...
          floatingActionButton: FloatingActionButton(
            ...
            onPressed: () {
              print(myController.text);
            },
            ...
          ),// FloatingActionButton
        );
      }
    }
    

    现在受此启发,也知道 Flutter 通过引用传递其对象参数这一事实,因此我们需要一个控制器类让我们的子类能够通过控制器对象访问子类的字段和函数(如前面示例中的 TextField 小部件)...在您的情况下,我们可以这样做:

    class HomePageController {
      void Function() methodA;
    }
    
    class MyApp extends StatelessWidget {
    
      final HomePageController myController = HomePageController();
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
              leading: IconButton(
                icon: Icon(Icons.help),
                onPressed: () {
                  myController.methodA();
                },
              ),// IconButton
            ),// AppBar
            body: HomePage(
              controller: myController,
            ),// HomePage
          ),
        );
      }
    }
    
    class HomePage extends StatefulWidget {
    
      final HomePageController controller;
    
      HomePage({ this.controller });
    
      @override
      _HomePageState createState() => _HomePageState(controller);
    }
    
    class _HomePageState extends State<HomePage> {
    
      _HomePageState(HomePageController _controller) {
        _controller.methodA = methodA;
      }
    
      @override
      Widget build(BuildContext context) {
        return Container();
      }
    
      void methodA() {}
    }
    

    从这段代码中可以看出,myController的引用通过HomePage到达_HomePageState的构造函数,然后得到引用methodA 函数...现在 methodA 可以通过 myControllerMyApp 类中访问!

    希望对您有所帮助! :))

    【讨论】:

    • 对我来说似乎是最优雅的解决方案
    • 我正在尝试使用此解决方案,但是当由于 notifyListeners() 而重建小部件树时,控制器为空。
    • @jwasher 感谢您的评论...您需要覆盖 didUpdateWidget 并重新初始化变量,就像您在构造函数中所做的那样...例如在此示例中: override void didUpdateWidget(ProfileNumbersSection oldWidget) { _controller.methodA = methodA ; super.didUpdateWidget(oldWidget); }
    • @ehsaneha 干净而聪明的解决方案!
    【解决方案4】:

    感谢Günter Zöchbauer 为我指明了正确的方向。我正在使用GlobalKey 来完成任务。但是使用 GlobalKeycare

    GlobalKey<_HomePageState> globalKey = GlobalKey();
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
              leading: IconButton(
                icon: Icon(Icons.help),
                onPressed: () {
                 globalKey.currentState.methodA();
                },
              ),
            ),
            body: HomePage(key: globalKey),
          ),
        );
      }
    }
    class HomePage extends StatefulWidget {
      HomePage({Key key}) : super(key: key);
    
      @override
      _HomePageState createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      @override
      Widget build(BuildContext context) {
        return Container();
      }
    
      void methodA() {}
    }
    

    【讨论】:

    • 有点混乱,仍在使用 GlobalKey。是好方法吗?请清除
    • @iPatel 只要你不创建很多GlobalKeys就很好
    • @Volleyball 我有同样的情况,但我在另一个类中有状态小部件。所以我需要从 State 类中调用该函数。这是一种好的做法吗?
    【解决方案5】:

    实际上,您不能这样做,因为_HomePageState 是私有的。删除 HomePageState 前面的 _ 符号,这样它就会公开,您可以使用 HomePageState().methodA(); 调用 methodA()

    【讨论】:

    • 首先,我可以在不删除_的情况下执行_HomePageState().methodA(),其次,实例化HomePageState不是调用methodA()的好方法,因为我将创建一个新状态.
    • 我个人更喜欢 Alex 的解决方案。我不明白为什么它应该被否决。在返回 MaterialApp 之前,您可以在 build 方法中创建 HomePage 实例。然后你可以在body:中传递它,并在onPressed回调中引用它。
    • @FJJ 据我所知,您不应该创建 2 个状态实例。对于这样的小作品,永远不要!!!
    • 我同意 Alex Adrianov 的观点,当您使用 '_' 时它会变为私有
    • @John 当您使用“_”时,它是“文件私有”。这意味着您可以从同一文件中的另一个类访问它。但不是来自其他文件。
    猜你喜欢
    • 1970-01-01
    • 2020-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多