从另一个有状态小部件调用一个有状态小部件中的方法 - Flutter
Posted
技术标签:
【中文标题】从另一个有状态小部件调用一个有状态小部件中的方法 - Flutter【英文标题】:call method in one stateful widget from another stateful widget - Flutter 【发布时间】:2018-12-04 09:27:28 【问题描述】:我有一个正在处理的颤振项目,我无法将整个代码放入其中,因为它的代码超过 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??
);
【问题讨论】:
Trigger a function from a widget to a State object的可能重复 如果你能确保一个小部件是另一个小部件的后代,你可以使用InheritedWidget @JacobPhillips 在那种情况下使用context.ancestorStateOfType
@RémiRousselet 我需要更多帮助,我正在尝试将数据从子小部件发送到父小部件,所有流和可听的示例都相反,这正是我的问题所在.我想从一个子小部件调用一个函数到一个父小部件,很抱歉没有提到上面的内容..
如果您的孩子也应该提交活动,请将整个 StreamController
传递给您的孩子,而不仅仅是 Stream
。
【参考方案1】:
要调用父级的函数,可以使用回调模式。在此示例中,将一个函数 (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
类(ScrollController
、AnimationController
、...)。这些也作为构造函数参数传递给子级,它们包含控制子级状态的方法,而无需在父级上调用setState
。示例:
scrollController.animateTo(200.0, duration: Duration(seconds: 1), curve: Curves.easeInOut);
然后要求子级监听这些更改以更新其内部状态。当然,你也可以实现自己的控制器类。如果需要,我建议您查看 Flutter 的源代码以了解其工作原理。
Future 和 Streams 是传递状态的另一种选择,也可以用于调用子函数。
但我真的不推荐它。如果你需要调用一个子部件的方法,那很像你的应用架构有缺陷。 尝试将状态提升到共同祖先!
【讨论】:
“不鼓励从父小部件调用子小部件的方法”即使通过 GlobalKey 调用也不鼓励?? 有点复杂!! 好的,谢谢!这是一个救生员。我有一个从浮动操作按钮调用的大对话框,其中定义了许多本地下拉列表,并且不想复制/粘贴它,但也希望在子状态页面中使用相同的浮动操作按钮。这正是我想要的。 @boformer 如果我想在不同的类中定义这些类,在一个 dart 文件中定义 ColorPicker,在不同的类中定义(Parent,ParentState),那么我应该在哪里定义 typedef ColorCallback,因为我尝试过但它失败了 太棒了,我从这个答案中学到了很多东西,它帮助我避免了构建 Flutter 应用程序的一些陷阱。谢谢boformer!【参考方案2】:我通过反复试验找到了另一个解决方案,但它奏效了。
import 'main.dart' as main;
然后在onPressed下面添加这一行。
main.MyAppState().printSample();
【讨论】:
这是反模式,你应该提升状态 这里你正在实例化一个新的状态对象,这是完全错误的。 @ProCo 我的更正是删除这个答案。 你能给它的替代方案吗,这个答案很简单,但我不知道它是如何工作的。【参考方案3】:如果你想调用 printSample() 函数,你可以使用:
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();
...
【讨论】:
我认为这不适用于更复杂的情况:Myapp StatefulWidget 可以重复构造,因此任何给定实例持有的状态可能与 Flutter 持有和关联的状态对象不同和树一起,对吧? 你好@PatNiemeyer,对吗? _StatefulButtonState _lastState; //覆盖状态 createState() _lastState = new _StatefulButtonState(onPressed: onPressed);返回_lastState; 【参考方案4】:你可以试一试,它会从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();
【讨论】:
这个答案与 Mehmet Akif BAYSAL 的答案完全相同(概念 100% 相同)。 Mehmet Akif BAYSAL 的答案在代码示例中得到了更雄辩的说明,因此更容易理解。 如果我想在 bloc 中调用一个方法,正确的方法是什么? ***.com/questions/62097660/…【参考方案5】:您可以通过使用小部件的 key 来做到这一点
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,
)
通过这个键你可以调用状态内的任何公共方法
_myKey.currentState.printSample();
【讨论】:
你不是说_myKey.currentState.printSample();
吗?
我卡住了,无法使用 GlobalKey,因为我的状态小部件前面有下划线,如 _MyState
,更改为 MyState
- 然后它工作正常。
@J.Diaz 下划线前的类名指私有类也用于变量和成员,所以你不能从目录外访问它【参考方案6】:
这里的 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)
),
);
所以,通过使用widget和父类状态,我们可以调用父类方法。
【讨论】:
【参考方案7】:虽然使用回调和GlobalKey
's 对于简单的用例来说很好,但对于更复杂的设置,它们可能应该被视为反模式,因为它们挂钩到小部件类型并依赖于低级实现逻辑。
如果您发现自己添加了越来越多的回调/全局键,并且开始变得混乱,那么可能是时候切换到 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.asBroadcastStream().listen()
,您可以使用.stream.listen()
。另见https://api.dart.dev/stable/dart-async/Stream-class.html
有关概述此方法和其他方法的答案,请参阅Inter Widget communication
【讨论】:
以上是关于从另一个有状态小部件调用一个有状态小部件中的方法 - Flutter的主要内容,如果未能解决你的问题,请参考以下文章