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

我有一个颤动的项目,我的工作,我不能把整个代码,因为它的500多行代码,所以我会尽量简单地问我的问题,因为我可以使用的 imp。代码的一部分。

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

file lib\main.dart

只需要一个简单的函数,比如

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

这个函数位于 main 类中的有状态小部件中。

还有一个文件 lib\MyApplication.dart

this file also has a stateful widget can I do something so that I can call the function printSample() here ..

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??
}
);
}
}
165558 次浏览

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


Futures and streams are another alternative to pass down state, and could also be used to call a function of a child.

但我真的不建议这么做。如果您需要调用子窗口小部件的方法,那么您的应用程序体系结构就是有缺陷的。试着把国家提升到共同的祖先!

I found another solution by trial-and-error, but it worked.

import 'main.dart' as main;

然后在 onPress 下面添加这一行。

main.MyAppState().printSample();

if you want to call printSample() func you can use:

class Myapp extends StatefulWidget{
...
final 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();
...

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

您可以通过使用 widget 的 钥匙来实现这一点

我的 Widget.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

  GlobalKey<MyState> _myKey = GlobalKey();

and pass it when create widget

MyWidget(
key : _myKey,
)

通过这个键可以调用状态内的任何公共方法

_myKey.currentState.printSample();

这里的首页是父页,儿童页是子页。有一个名为 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)
),
);
}
}

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

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.

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

注册事件控制器

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:

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


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

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

MyCustomEventType可以是枚举(如果您不需要传递额外的数据) ,也可以是具有所需字段的常规对象,以防需要在事件上设置额外的数据。

Trigger an event

现在,在任何小部件(包括声明 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.asBroadcastStream().listen()进行监听,你可以使用 .stream.listen()。另请参阅 < a href = “ https://api.dart.dev/stat/dart-sync/Stream-class.html”rel = “ norefrer”> https://api.dart.dev/stable/dart-async/stream-class.html

有关这个问题和其他方法的答案,请参阅 部件间通信

我想到了一个简单的解决方案

步骤1——定义子窗口小部件

    class MyChildWidget extends StatelessWidget {
final Function detailsButtonFunction;
MyChildWidget(
{Key? key,
required this.detailsButtonFunction,
})
: super(key: key);


@override
Widget build(BuildContext context) {
return  Container(
child: SingleChildScrollView(
child: Container(
child: ElevatedButton(
child: Text("Some Sample text")
onPressed : (){
detailsButtonFunction()
}
)
),
),
),
);
}
}

步骤2-将其放置到 Parent 小部件中

  class MyparentWidget extends StatelessWidget {


MyParentWidget({Key? key}): super(key: key);


@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
     

child: SingleChildScrollView(
child: Container(
child: Column(
children:[ MyChildWidget(detailsButtonFunction:(){
print("something")
}  ),]
),
),
),
),
);
}
}

最后注意——格式和拼写可能有一些错误,只要集中在函数部分。.当我们通过我们做这样的 (){Print("someThing")},当我们收到我们做这样的 (){functioname()}... 。加括号是非常重要的