繁体   English   中英

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

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

我有一个正在处理的颤振项目,我不能把整个代码放在 500 多行代码中,所以我会尝试像我使用 imp 一样简单地提出我的问题。 部分代码。

我有一个有状态的小部件,并且在扩展State<MusicPlayer>的类下的有状态小部件中有一些功能

文件lib\\main.dart

只需采取一个简单的功能,如

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

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

还有另一个文件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??  
      }
    );
  }
...
}

要调用父函数,您可以使用回调模式。 在这个例子中,一个函数 ( 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 的源代码以了解其工作原理。


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

但我真的不推荐它。 如果你需要调用子widget的方法,那很像是你的应用架构有缺陷。 尝试将状态向上移动到共同祖先!

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

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

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

_myKey.currentState.printSample();

如果你想调用 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();
...

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

import 'main.dart' as main;

然后在 onPressed 下添加这一行。

main.MyAppState().printSample();

您可以尝试一下,它会从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();
}

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

如果您发现自己添加了越来越多的回调/全局键,并且开始变得混乱,那么可能是时候切换到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()的控制器,即使用StreamController<MyCustomEventType>()并你可以使用.stream.listen()而不是.stream.asBroadcastStream().listen() .stream.listen() 另见https://api.dart.dev/stable/dart-async/Stream-class.html

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

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

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

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

显然,您只需创建一个仅包含一个方法的文件,您就可以直接调用它

例如,我想在名为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"),
                  )
              );
            }),
      ),
    );
  }
}

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

我正在开发一个扑朔迷离的项目,我无法将整个代码引起其超过500行的代码,因此我将尝试问我的问题,就像使用amp的icn一样简单。 代码部分。

我有一个有状态的小部件,并且在有状态的小部件内的某些类下,扩展了类,扩展了State<MusicPlayer>

文件lib\\main.dart

只是采取一个简单的功能,如

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

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

还有另一个文件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??  
      }
    );
  }
...
}

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM