简体   繁体   English

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

[英]call method in one stateful widget from another stateful widget - Flutter

I have a flutter project that i am working on i cant put the whole code cause its more than 500 lines of code so i will try to ask my question as simply as i acn using the imp.我有一个正在处理的颤振项目,我不能把整个代码放在 500 多行代码中,所以我会尝试像我使用 imp 一样简单地提出我的问题。 section of the code .部分代码。

i am having a stateful widget and having some functions inside that stateful widget under the class that extends extends State<MusicPlayer>我有一个有状态的小部件,并且在扩展State<MusicPlayer>的类下的有状态小部件中有一些功能

file lib\\main.dart文件lib\\main.dart

just take a simple function like只需采取一个简单的功能,如

class MyAppState extends State<MyApp>{
...
void printSample (){
  print("Sample text");
}
...

this function is inside the stateful widget inside main class .此函数位于主类中的有状态小部件内。

there is another file lib\\MyApplication.dart还有另一个文件lib\\MyApplication.dart

this file also has a stateful widget can i do something so that i can call the function printSample() here ..这个文件还有一个有状态的小部件,我可以做些什么,以便我可以在这里调用函数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??  
      }
    );
  }
...
}

To call a function of a parent, you can use the callback pattern.要调用父函数,您可以使用回调模式。 In this example, a function ( onColorSelected ) is passed to the child.在这个例子中,一个函数 ( onColorSelected ) 被传递给孩子。 The child calls the function when a button is pressed:当按下按钮时,孩子会调用该函数:

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);

Internal Flutter widgets like buttons or form fields use exactly the same pattern.内部 Flutter 小部件(如按钮或表单字段)使用完全相同的模式。 If you only want to call a function without any arguments, you can use the VoidCallback type instead defining your own callback type.如果您只想调用不带任何参数的函数,您可以使用VoidCallback类型来代替定义您自己的回调类型。


If you want to notify a higher up parent, you can just repeat this pattern on every hierarchy level:如果您想通知更高级别的父母,您可以在每个层次结构级别重复此模式:

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),
    )
  }
}

Calling a method of child widget from a parent widget is discouraged in Flutter. Flutter 不鼓励从父小部件调用子小部件的方法。 Instead, Flutter encourages you to pass down the state of a child as constructor parameters.相反,Flutter 鼓励您将子项的状态作为构造函数参数传递。 Instead of calling a method of the child, you just call setState in the parent widget to update its children.您只需在父小部件中调用setState来更新其子部件,而不是调用子部件的方法。


One alternative approach are the controller classes in Flutter ( ScrollController , AnimationController , ...).一种替代方法是 Flutter 中的controller类( ScrollControllerAnimationController ,...)。 These are also passed to the children as constructor parameters, and they contain methods to control the state of the child without calling setState on the parent.这些也作为构造函数参数传递给子级,它们包含控制子级状态的方法,而无需在父级上调用setState Example:例子:

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

The children are then required to listen to these changes to update their internal state.然后要求孩子们聆听这些变化以更新他们的内部状态。 Of course, you can also implement your own controller class.当然,你也可以实现自己的控制器类。 If you need to, I recommend you to look at the source code of Flutter to understand how that works.如果需要,我建议您查看 Flutter 的源代码以了解其工作原理。


Futures and streams are another alternative to pass down state, and could also be used to call a function of a child.期货和流是传递状态的另一种选择,也可用于调用子函数。

But I really don't recommend it.但我真的不推荐它。 If you need to call a method of a child widget, it is very like that your application architecture is flawed.如果你需要调用子widget的方法,那很像是你的应用架构有缺陷。 Try to move the state up to the common ancestor!尝试将状态向上移动到共同祖先!

You can do that by use key of widget您可以通过使用小部件的来做到这一点

myWidget.dart 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");
      }
        
 }

now when use MyWidget declare GlobalKey as global key现在,当使用进myWidget声明GlobalKey作为全球关键

  GlobalKey<MyState> _myKey = GlobalKey();

and pass it when create widget并在创建小部件时传递它

MyWidget(
key : _myKey,
)

by this key you can call any public method inside state通过这个键,你可以调用 state 内的任何公共方法

_myKey.currentState.printSample();

if you want to call printSample() func you can use:如果你想调用 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();
...

I found another solution by trial-and-error, but it worked.我通过反复试验找到了另一个解决方案,但它奏效了。

import 'main.dart' as main;

Then add this line under the onPressed.然后在 onPressed 下添加这一行。

main.MyAppState().printSample();

You can give this a try, it will call a method defined in Page2 ( StatefulWidget ) from Page1 ( StatefulWidget ) widget.您可以尝试一下,它会从Page1 ( StatefulWidget ) 小部件调用Page2 ( StatefulWidget ) 中定义的方法。

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();
}

While using callbacks and GlobalKey 's is fine for simple use cases, for more complex setups they should probably be considered anti-patterns given that they hook into widget types and rely on low-level implementation logic.虽然使用回调和GlobalKey对简单的用例很好,但对于更复杂的设置,它们可能应该被视为反模式,因为它们挂钩到小部件类型并依赖于低级实现逻辑。

If you find yourself adding more and more callbacks/globalkeys, and it starts to get messy, then it might be time to switch to something like StreamController + StreamSubscription .如果您发现自己添加了越来越多的回调/全局键,并且开始变得混乱,那么可能是时候切换到StreamController + StreamSubscription类的东西了。 This way you can decouple your events from the specific widget types and abstract away the inter-widget communication logic.通过这种方式,您可以将事件与特定的小部件类型分离,并抽象出小部件间的通信逻辑。

Register an event controller注册一个事件控制器

In your top-level widget (app-level or page-level depending on your needs) create the StreamController instance, and make sure you release it in the dispose() method:在您的顶级小部件(应用程序级或页面级,取决于您的需要)中创建StreamController实例,并确保在dispose()方法中释放它:

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

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

Pass the eventController instance as a constructor argument down into any child widget that needs to listen to events and/or trigger events.eventController实例作为构造函数参数传递给任何需要侦听事件和/或触发事件的子小部件。

MyCustomEventType can be either an enum (if you don't need to pass extra data) or a regular object with whatever fields you need in case you need to set extra data on the event. MyCustomEventType可以是一个枚举(如果您不需要传递额外的数据)或一个带有任何您需要的字段的常规对象,以防您需要在事件上设置额外的数据。

Trigger an event触发事件

Now in any of the widgets (including the parent one, where you declared the StreamController ) you can trigger events with:现在,在任何小部件(包括您声明StreamController的父小部件)中,您都可以通过以下方式触发事件:

eventController.sink.add(MyCustomEventType.UserLoginIsComplete);

Listen to events监听事件

To set up a listener in your child (or parent widget) put the following code in initState() :要在您的孩子(或父小部件)中设置侦听器,请将以下代码放入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();
  }
}

Be aware that if you override StreamController.done() then your listeners will not fire because done() replaces whatever listeners you have set previously.请注意,如果您覆盖StreamController.done()则您的侦听器将不会触发,因为done()替换了您之前设置的任何侦听器。


NOTE: If you have a one-to-one communication relationship between two widgets then you do not need the broadcast events flavour — in that case you can create the controller without .broadcast() , ie with StreamController<MyCustomEventType>() and to listen instead of .stream.asBroadcastStream().listen() you can use .stream.listen() .注意:如果您在两个小部件之间有一对一的通信关系,那么您不需要广播事件风格——在这种情况下,您可以创建没有.broadcast()的控制器,即使用StreamController<MyCustomEventType>()并你可以使用.stream.listen()而不是.stream.asBroadcastStream().listen() .stream.listen() Also see https://api.dart.dev/stable/dart-async/Stream-class.html另见https://api.dart.dev/stable/dart-async/Stream-class.html

For an answer which outlines this and other approaches see Inter Widget communication有关概述此方法和其他方法的答案,请参阅Inter Widget 通信

Here HomePage is parent page and ChildPage is child page.这里 HomePage 是父页面, ChildPage 是子页面。 There is one method called onSelectItem, which we need to call from child page.有一种方法叫做 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)
      ),
    );
  }
}

So, by using the widget and parent class state we can call the parent class method.因此,通过使用小部件父类状态,我们可以调用父类方法。

I just found a simplest solution for this question我刚刚为这个问题找到了一个最简单的解决方案

Appearently, You can just create a file that contain only a method and you will be able to call it directly显然,您只需创建一个仅包含一个方法的文件,您就可以直接调用它

For Example, I want to create a showModalBottomSheet method in file named custom_show_bottom_sheet.dart :例如,我想在名为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,
          ],
        );
      });
}

And you can simply call it like this:你可以简单地这样称呼它:

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"),
                  )
              );
            }),
      ),
    );
  }
}

Hope this be a help!希望这会有所帮助! Please let me know if I misunderstand your question or anything.如果我误解了您的问题或任何内容,请告诉我。

I have a flutter project that i am working on i cant put the whole code cause its more than 500 lines of code so i will try to ask my question as simply as i acn using the imp.我正在开发一个扑朔迷离的项目,我无法将整个代码引起其超过500行的代码,因此我将尝试问我的问题,就像使用amp的icn一样简单。 section of the code .代码部分。

i am having a stateful widget and having some functions inside that stateful widget under the class that extends extends State<MusicPlayer>我有一个有状态的小部件,并且在有状态的小部件内的某些类下,扩展了类,扩展了State<MusicPlayer>

file lib\\main.dart文件lib\\main.dart

just take a simple function like只是采取一个简单的功能,如

class MyAppState extends State<MyApp>{
...
void printSample (){
  print("Sample text");
}
...

this function is inside the stateful widget inside main class .此函数位于主类内部的有状态窗口小部件中。

there is another file lib\\MyApplication.dart还有另一个文件lib\\MyApplication.dart

this file also has a stateful widget can i do something so that i can call the function printSample() here ..这个文件也有一个有状态的小部件,我可以做些什么,以便在这里调用函数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??  
      }
    );
  }
...
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 将字符串值从一个有状态小部件更改为另一个有状态小部件 - Change String value from one stateful widget to another stateful widget Flutter - 从另一个有状态小部件设置一个有状态小部件的变量值 - Flutter - Setting value of a variable of one stateful widget from another stateful widget 如何在 Flutter 中从另一个有状态小部件更改一个有状态小部件的状态? - How to change state of one Stateful Widget from another Stateful Widget in Flutter? 无法从 static 方法调用 Flutter Stateful Widget 的 setState - Unable to call setState of Flutter Stateful Widget from a static method 如何在另一个有状态小部件中调用方法 - How to call a method in another stateful widget Flutter:如何从另一个有状态类更新 UI 状态小部件 - Flutter:How to update a UI -stateful widget from another stateful class 如何在 Flutter 中将事件从一个有状态小部件广播到另一个小部件 - How To Broadcast An Event From One Stateful Widget To Another In Flutter 如何从另一个Dart文件中调用有状态的widget(具有表单)方法?-Flutter - How to Call stateful widget(have form) method from another dart file?- Flutter 从 Flutter 中的另一个 dart 文件在有状态小部件 class 下使用 SetState 方法调用 void - Call void with SetState method under stateful widget class from another dart file in Flutter Flutter,如何从返回的 Widget 调用 Stateful Widget 内部的函数? - Flutter, how to call a function inside Stateful Widget from a returned Widget?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM