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

Ama*_*tra 32 dart flutter

我有一个颤动项目,我正在努力我不能把整个代码导致它超过500行代码所以我会尝试问我的问题就像我使用imp一样简单.代码部分.

我有一个有状态的小部件,并在扩展的类下面的有状态小部件中有一些函数 State<MusicPlayer>

文件 lib\main.dart

只需要一个简单的功能

class MyAppState extends State<MyApp>{
...
void printSample (){
  print("Sample text");
}
...
Run Code Online (Sandbox Code Playgroud)

此函数位于主类中的有状态小部件内.

还有另一个文件 lib\MyApplication.dart

这个文件也有一个有状态的小部件,我可以做一些事情,这样我就可以在printSample()这里调用函数..

class MyApplicationState extends State<MyApplication>{
...
@override
  Widget build(BuildContext context) {
    return new FlatButton(
      child: new Text("Print Sample Text"),
      onPressed :(){
       // i want to cal the function here how is it possible to call the 
       // function 
       // printSample()  from here??  
      }
    );
  }
...
}
Run Code Online (Sandbox Code Playgroud)

Mah*_*eja 42

您可以通过使用小部件的来做到这一点

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");
      }
        
 }
Run Code Online (Sandbox Code Playgroud)

现在,当使用 进myWidget声明GlobalKey作为全球关键

  GlobalKey<MyState> _myKey = GlobalKey();
Run Code Online (Sandbox Code Playgroud)

并在创建小部件时传递它

MyWidget(
key : _myKey,
)
Run Code Online (Sandbox Code Playgroud)

通过这个键,你可以调用 state 内的任何公共方法

_myKey.currentState.printSample();
Run Code Online (Sandbox Code Playgroud)

  • 我被卡住了,无法使用 GlobalKey,因为我的状态小部件前面有下划线,如``_MyState````,更改为````MyState``` - 然后它工作正常。 (4认同)

bof*_*mer 41

要调用父项的函数,可以使用回调模式.在此示例中,将function(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);
Run Code Online (Sandbox Code Playgroud)

内部颤动小部件(如按钮或表单字段)使用完全相同的模式.如果您只想调用不带任何参数的函数,则可以使用该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),
    )
  }
}
Run Code Online (Sandbox Code Playgroud)

在Flutter中不鼓励从父窗口小部件调用子窗口小部件的方法.相反,Flutter鼓励您将子状态作为构造函数参数传递下去.您只需调用setState父窗口小部件来更新其子窗口,而不是调用子窗口的方法.


一种替代的方法是controller在扑(类ScrollController,AnimationController...).它们也作为构造函数参数传递给子setState节点,它们包含控制子节点状态而不调用父节点的方法.例:

scrollController.animateTo(200.0, duration: Duration(seconds: 1), curve: Curves.easeInOut);
Run Code Online (Sandbox Code Playgroud)

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


期货和流是传递状态的另一种选择,也可以用来调用孩子的功能.

但我真的不推荐它.如果您需要调用子窗口小部件的方法,那就非常类似于您的应用程序架构存在缺陷.尝试将状态移动到共同的祖先!

  • 有点复杂!! (3认同)

mkf*_*mkf 16

如果你想调用 printSample() func 你可以使用:

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();
...
Run Code Online (Sandbox Code Playgroud)

  • 我不认为这对更复杂的情况有效:Myapp StatefulWidget 可以重复构造,因此任何给定实例持有的状态可能与 Flutter 持有并与树关联的状态对象不同,对吧? (6认同)

ccp*_*zza 9

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

\n

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

\n

注册事件控制器

\n

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

\n
class _TopLevelPageState extends State<TopLevelPage> {\n  StreamController<MyCustomEventType> eventController = StreamController<MyCustomEventType>.broadcast();\n\n// ...\n  @override\n  void dispose() {\n    eventController.close();\n    super.dispose();\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
\n

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

\n
\n

触发事件

\n

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

\n
eventController.sink.add(MyCustomEventType.UserLoginIsComplete);\n
Run Code Online (Sandbox Code Playgroud)\n

聆听事件

\n

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

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

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

\n
\n
\n

注意:如果两个小部件之间有一对一的通信关系,那么您不需要广播事件风味 \xe2\x80\x94 在这种情况下,您可以创建不带 的控制器,.broadcast()即使用StreamController<MyCustomEventType>()和 来监听,而不是.stream.asBroadcastStream().listen()可以使用.stream.listen()。另请参阅https://api.dart.dev/stable/dart-async/Stream-class.html \n

\n

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

\n


Cop*_*oad 7

您可以尝试一下,它会从( ) 小部件调用Page2( StatefulWidget) 中定义的方法。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();
}
Run Code Online (Sandbox Code Playgroud)

  • 这个答案与 Mehmet Akif BAYSAL 的答案完全相同(概念 100% 相同)。Mehmet Akif BAYSAL 的答案在代码示例中更加雄辩地说明,因此更容易理解。 (2认同)

mn1*_*28b 6

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

import 'main.dart' as main;
Run Code Online (Sandbox Code Playgroud)

然后将此行添加到onPressed下。

main.MyAppState().printSample();
Run Code Online (Sandbox Code Playgroud)

  • 这是反模式,你应该提升状态 (3认同)
  • 这里你正在实例化一个新的状态对象,这是完全错误的。 (2认同)
  • @ProCo 我的更正是删除这个答案。 (2认同)