一个有状态小部件中的状态方法从另一个有状态小部件中调用-Flutter

时间:2018-06-25 18:17:02

标签: android dart flutter

我正在处理一个扑朔迷离的项目,我不能把整个代码引起其超过500行的代码,因此我将尝试问我的问题,就像我使用imp一样。代码部分。

我有一个有状态的小部件,并且在扩展了类get_queryset()的有状态小部件内有一些功能

文件State<MusicPlayer>

只需使用一个简单的功能

lib\main.dart

此函数在main类的有状态窗口小部件内。

还有另一个文件class MyAppState extends State<MyApp>{ ... void printSample (){ print("Sample text"); } ...

此文件也有一个有状态的小部件,我可以做点什么,以便在这里调用函数lib\MyApplication.dart ..

printSample()

8 个答案:

答案 0 :(得分:26)

您可以通过使用小部件的来实现

myWidget.dart

    class MyWidget extends StatefulWidget {
     
      const MyWidget ({Key key}) : super(key: key);
     
      @override
      State<StatefulWidget> createState()=> MyState();
    
  
    }

   class MyState extends State<MyWidget >{
        
          
               
      Widget build(BuildContext context){ return ....}
    
      void printSample (){
         print("Sample text");
      }
        
 }

现在使用 MyWidget 时,将 GlobalKey 声明为全局密钥

  GlobalKey<MyState> _myKey = GlobalKey();

并在创建小部件时将其传递

MyWidget(
key : _myKey,
)

通过此键,您可以在状态内部调用任何公共方法

_myKey.currentState.printSample();

答案 1 :(得分:24)

要调用父函数,可以使用回调模式。在此示例中,函数(onColorSelected)被传递给子级。按下按钮时,孩子会调用该函数:

import 'package:flutter/material.dart';

class Parent extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return ParentState();
  }
}

class ParentState extends State<Parent> {
  Color selectedColor = Colors.grey;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Container(
          color: selectedColor,
          height: 200.0,
        ),
        ColorPicker(
          onColorSelect: (Color color) {
            setState(() {
              selectedColor = color;
            });
          },
        )
      ],
    );
  }
}

class ColorPicker extends StatelessWidget {
  const ColorPicker({this.onColorSelect});

  final ColorCallback onColorSelect;

  @override
  Widget build(BuildContext context) {
    return Row(
      children: <Widget>[
        RaisedButton(
          child: Text('red'),
          color: Colors.red,
          onPressed: () {
            onColorSelect(Colors.red);
          },
        ),
        RaisedButton(
          child: Text('green'),
          color: Colors.green,
          onPressed: () {
            onColorSelect(Colors.green);
          },
        ),
        RaisedButton(
          child: Text('blue'),
          color: Colors.blue,
          onPressed: () {
            onColorSelect(Colors.blue);
          },
        )
      ],
    );
  }
}

typedef ColorCallback = void Function(Color color);

内部Flutter小部件(例如按钮或表单字段)使用完全相同的模式。如果只想调用不带任何参数的函数,则可以使用VoidCallback类型来定义自己的回调类型。


如果您想通知更高级别的父母,您可以在每个层次结构级别重复此模式:

class ColorPickerWrapper extends StatelessWidget {
  const ColorPickerWrapper({this.onColorSelect});

  final ColorCallback onColorSelect;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(20.0),
      child: ColorPicker(onColorSelect: onColorSelect),
    )
  }
}

不鼓励在Flutter中从父窗口小部件调用子窗口小部件的方法。相反,Flutter鼓励您将子代的状态作为构造函数参数传递。您无需调用子方法,只需在父窗口小部件中调用setState即可更新其子方法。


一种替代方法是Flutter中的controller类(ScrollControllerAnimationController,...)。这些也作为构造函数参数传递给子级,并且它们包含控制子级状态的方法,而无需在父级上调用setState。示例:

scrollController.animateTo(200.0, duration: Duration(seconds: 1), curve: Curves.easeInOut);

然后要求孩子听这些更改以更新其内部状态。当然,您也可以实现自己的控制器类。如果需要,我建议您查看Flutter的源代码以了解其工作原理。


将来和流是传递状态的另一种选择,也可以用于调用子函数。

但是我真的不推荐它。如果您需要调用子窗口小部件的方法,则很像您的应用程序体系结构存在缺陷。 尝试将状态提升到共同祖先!

答案 2 :(得分:2)

虽然使用回调和 GlobalKey 对简单用例很好,但对于更复杂的设置,它们可能应该被视为反模式,因为它们挂钩到小部件类型并依赖于低级实现逻辑。< /p>

如果您发现自己添加了越来越多的回调/全局键,并且开始变得混乱,那么可能是时候切换到 StreamController + StreamSubscription 之类的东西了。通过这种方式,您可以将事件与特定小部件类型分离,并抽象出小部件间的通信逻辑。

注册事件控制器

在您的顶级小部件(应用级或页面级,取决于您的需要)中创建 StreamController 实例,并确保您在 dispose() 方法中释放它:

class _TopLevelPageState extends State<TopLevelPage> {
  StreamController<MyCustomEventType> eventController = StreamController<MyCustomEventType>.broadcast();

// ...
  @override
  void dispose() {
    eventController.close();
    super.dispose();
  }
}

eventController 实例作为构造函数参数传递给任何需要侦听事件和/或触发事件的子小部件。

<块引用>

MyCustomEventType 可以是一个枚举(如果您不需要传递额外的数据),也可以是一个带有您需要的任何字段的常规对象,以防您需要为事件设置额外的数据。

触发事件

现在在任何小部件中(包括您声明 StreamController 的父小部件),您都可以通过以下方式触发事件:

eventController.sink.add(MyCustomEventType.UserLoginIsComplete);

收听事件

要在您的孩子(或父小部件)中设置侦听器,请将以下代码放入 initState()


class _ChildWidgetState extends State<ChildWidget> {
  @override
  void initState() {
    super.initState();
    // NOTE: 'widget' is the ootb reference to the `ChildWidget` instance. 
    this.eventSubscription = widget.eventController.stream.asBroadcastStream().listen((event) {
      if (event == MyCustomEventType.UserLoginIsComplete) {
        print('handling LOGIN COMPLETE event ' + event.toString());
      } else {
        print('handling some other event ' + event.toString());
      }
  }

  @override
  void dispose() {
    this.parentSubscription.cancel();
    super.dispose();
  }
}
<块引用>

请注意,如果您覆盖 StreamController.done(),那么您的侦听器将不会触发,因为 done() 会替换您之前设置的任何侦听器。


注意:如果您在两个小部件之间有一对一的通信关系,那么您不需要广播事件风格——在这种情况下,您可以创建没有 .broadcast() 的控制器,即使用 { {1}} 并且要聆听而不是 StreamController<MyCustomEventType>(),您可以使用 .stream.asBroadcastStream().listen()。另见https://api.dart.dev/stable/dart-async/Stream-class.html

有关概述此方法和其他方法的答案,请参阅 Inter Widget communication

答案 3 :(得分:1)

我通过反复试验找到了另一个解决方案,但是它奏效了。

import 'main.dart' as main;

然后将此行添加到onPressed下。

main.MyAppState().printSample();

答案 4 :(得分:1)

如果要调用printSample()函数,可以使用:

class Myapp extends StatefulWidget{
...
    MyappState myAppState=new MyappState();
    @override
    MyappState createState() => myAppState;
    void printSample(){
        myAppState.printSample();
    }
}
class MyAppState extends State<MyApp>{
    void printSample (){
        print("Sample text");
    }
}

...............
Myapp _myapp = new Myapp();
myapp.printSample();
...

答案 5 :(得分:1)

您可以尝试一下,它将从Page2StatefulWidget)小部件调用Page1StatefulWidget)中定义的方法。

class Page1 extends StatefulWidget {
  @override
  _Page1State createState() => _Page1State();
}

class _Page1State extends State<Page1> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
          child: Text("Call page 2 method"),
          onPressed: () => Page2().method(),
        ),
      ),
    );
  }
}

class Page2 extends StatefulWidget {
  method() => createState().methodInPage2();

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

class _Page2State extends State<Page2> {
  methodInPage2() => print("method in page 2");

  @override
  Widget build(BuildContext context) => Container();
}

答案 6 :(得分:0)

此处HomePage是父页面,ChildPage是子页面。有一种叫做onSelectItem的方法,我们需要从子页面调用。

class HomePage extends StatefulWidget {

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

class HomePageState extends State<HomePage> {

  onSelectItem(String param) {
    print(param);
  }

  @override Widget build(BuildContext context) {

  }
}

class ChildPage extends StatefulWidget {
  final HomePageState homePageState;

  ChildPage({Key key, @required this.homePageState}) : super(key: key);

  _ChildPageState createState() => _ChildPageState();
}

class _ChildPageState extends State<ChildPage> {
  @override Widget build(BuildContext context) {

    return RaisedButton(
      onPressed: () {
        widget.homePageState.onSelectItem("test");
      },
      child: const Text(
          'Click here',
          style: TextStyle(fontSize: 20)
      ),
    );
  }
}

因此,通过使用小工具父类状态,我们可以调用父类方法。

答案 7 :(得分:0)

我刚刚找到了这个问题的最简单解决方案

显然,您可以创建一个仅包含方法的文件,并且可以直接调用

例如,我想在名为custom_show_bottom_sheet.dart的文件中创建一个showModalBottomSheet方法:

import 'package:flutter/material.dart';

customShowBottomSheet(context, Widget child){
    showModalBottomSheet(
      isScrollControlled: true,
      context: context,
      builder: (BuildContext context) {
        return Wrap(
          children: <Widget>[
            child,
          ],
        );
      });
}

您可以简单地这样称呼它:

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

class TestScreen extends StatelessWidget {
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: FloatingActionButton(
            onPressed: (){
              customShowBottomSheet( //A method is called here
                  context,
                  Container(
                    height: 200,
                    child: Text("This is a test"),
                  )
              );
            }),
      ),
    );
  }
}

希望这会有所帮助!如果我误解了您的问题,请告诉我。