我想能够运行函数一旦一个小部件已经完成构建/加载,但我不确定如何。
我目前的用例是检查用户是否通过身份验证,如果没有,则重定向到登录视图。我不想检查之前,并推动登录视图或主视图,它需要发生后,主视图已加载。
有什么我可以用的吗?
你可以用
https://github.com/slightfoot/flutter_after_layout
,在布局完成后只执行一次函数。 或者查看它的实现并将其添加到代码中:-)
基本上就是
void initState() { super.initState(); WidgetsBinding.instance .addPostFrameCallback((_) => yourFunction(context)); }
如果你正在寻找ReactNative的componentDidMount等效,Flutter有它。事情没那么简单,但原理是一样的。在Flutter中,__abc1不直接处理它们的事件。相反,它们使用State对象来完成此操作。
componentDidMount
State
class MyWidget extends StatefulWidget{ @override State<StatefulWidget> createState() => MyState(this); Widget build(BuildContext context){...} //build layout here void onLoad(BuildContext context){...} //callback when layout build done } class MyState extends State<MyWidget>{ MyWidget widget; MyState(this.widget); @override Widget build(BuildContext context) => widget.build(context); @override void initState() => widget.onLoad(context); }
一旦屏幕完成布局渲染,State.initState将立即被调用。如果处于调试模式,即使在热重载时也不会再次调用,直到显式地达到这样做的时间。
State.initState
更新: < em >颤振v1.8.4 < / em >
上述两个代码现在都在工作:
工作:
WidgetsBinding.instance .addPostFrameCallback((_) => yourFunction(context));
工作
import 'package:flutter/scheduler.dart'; SchedulerBinding.instance.addPostFrameCallback((_) => yourFunction(context));
根据官方指南和来源,如果你想确定布局的最后一帧也被绘制,你可以这样写,例如:
import 'package:flutter/scheduler.dart'; void initState() { super.initState(); if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) { SchedulerBinding.instance.addPostFrameCallback((_) => yourFunction(context)); } }
尝试SchedulerBinding,
SchedulerBinding.instance .addPostFrameCallback((_) => setState(() { isDataFetched = true; }));
在flutter版本1.14.6中,Dart版本28。
你只需要将你想要在构建方法之后发生的所有事情捆绑到一个单独的方法或函数中。
@override void initState() { super.initState(); print('hello girl'); WidgetsBinding.instance .addPostFrameCallback((_) => afterLayoutWidgetBuild()); }
有三种可能的方法:
1) WidgetsBinding.instance.addPostFrameCallback((_) => yourFunc(context)); 2) Future.delayed(Duration.zero, () => yourFunc(context)); 3) Timer.run(() => yourFunc(context));
至于context,我需要在所有小部件呈现后在Scaffold.of(context)中使用它。
context
Scaffold.of(context)
但在我看来,最好的办法是:
void main() async { WidgetsFlutterBinding.ensureInitialized(); //all widgets are rendered here await yourFunc(); runApp( MyApp() ); }
最好的方法是,
1. WidgetsBinding
WidgetsBinding.instance.addPostFrameCallback((_) { print("WidgetsBinding"); });
2. SchedulerBinding
SchedulerBinding.instance.addPostFrameCallback((_) { print("SchedulerBinding"); });
它可以在initState内部调用,两者都只会在构建小部件完成渲染后调用一次。
initState
@override void initState() { // TODO: implement initState super.initState(); print("initState"); WidgetsBinding.instance.addPostFrameCallback((_) { print("WidgetsBinding"); }); SchedulerBinding.instance.addPostFrameCallback((_) { print("SchedulerBinding"); }); }
以上两个代码都将使用相同的绑定框架。
https://medium.com/flutterworld/flutter-schedulerbinding-vs-widgetsbinding-149c71cb607f
另一个对我来说工作得很好的解决方案是通过Future.delayed()包装你想调用的函数,如下所示:
Future.delayed()
@override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { Future.delayed(Duration(seconds: 3), () => yourFunction()); }); }
import 'package:flutter/material.dart'; class TestBox extends StatefulWidget { final Color color; final Duration delay; const TestBox({ Key? key, this.color = Colors.red, this.delay = const Duration(seconds: 5), }) : super(key: key); @override _TestBoxState createState() => _TestBoxState(); } class _TestBoxState extends State<TestBox> { String? label; @override void initState() { initialMembers(); super.initState(); } void initialMembers() async { label = await fetchLabel(); if (mounted) setState(() {}); /// don't worry /// if `(!mounted)`, means wen `build` calld /// the label already has the newest value } Future<String> fetchLabel() async { await Future.delayed(widget.delay); print('fetchLabel call'); return 'from fetchLabel()'; } @override Widget build(BuildContext context) { return AnimatedContainer( margin: EdgeInsets.symmetric(vertical: 12), duration: Duration(milliseconds: 500), width: 220, height: 120, color: label == null ? Colors.white : widget.color, child: Center( child: Text(label ?? 'fetching...'), ), ); } }
Column( children: [ TestBox( delay: Duration(seconds: 1), color: Colors.green, ), TestBox( delay: Duration(seconds: 3), color: Colors.yellow, ), TestBox( delay: Duration(seconds: 5), color: Colors.red, ), ], ),
PostFrameCallback在屏幕完全绘制之前触发。因此,Devv上面的回答有助于添加延迟以允许屏幕绘制。
我有一个有状态的小部件,我使用html_editor_enhanced插件小部件。这是在其中设置初始消息的唯一方法。
class _SendChatMessageState extends State<SendChatMessage> { final _htmlController = HtmlEditorController(); @override void initState() { super.initState(); Future.delayed(const Duration(seconds: 3), () { _htmlController.setText(widget.chatMessage.message ?? ''); }); }
我尝试了addPostFrameCallback,但它不起作用,因为JavaScript生成异常“HTML编辑器仍在加载中,请在评估此JS之前等待”;
如果你有问题与新的SDK和旧的答案,你可以尝试我的解决方案。我已经在v3.0.4上测试了它
WidgetsBinding.instance.endOfFrame.then( (_) { if (mounted) { // do some suff // you can get width height of specific widget based on GlobalKey }; }, );
如果你不想使用__ABC0或SchedulerBinding:
SchedulerBinding
Future
Timer
Future<void> _runsAfterBuild() async { // This code runs after build ... } @override Widget build(BuildContext context) { Future(_runsAfterBuild); // <-- Use Future or Timer return Container(); }
Future<void> _runsAfterBuild() async { await Future((){}); // <-- Dummy await // This code runs after build ... } @override Widget build(BuildContext context) { _runsAfterBuild(); return Container(); }