[英]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
类( ScrollController
、 AnimationController
,...)。 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.通过这种方式,您可以将事件与特定的小部件类型分离,并抽象出小部件间的通信逻辑。
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
可以是一个枚举(如果您不需要传递额外的数据)或一个带有任何您需要的字段的常规对象,以防您需要在事件上设置额外的数据。
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);
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.