有任何回调可用在flutter每次页面在屏幕上可见? 在ios有一些委托方法,如 viewWillAppear
, viewDidAppear
, viewDidload
.
我想每当特定页面在屏幕上时,就调用一个API调用。
注意:我不要求应用程序的状态,如前景,背景,暂停,恢复。
谢谢
你不需要 StatefulWidget
用于每次显示屏幕时调用api。
在下面的示例代码中,按下浮动动作按钮导航到api调用界面,使用后退箭头返回,再次按下浮动动作按钮导航到api页面。
每当你访问这个页面时,api就会自动被调用。
import 'dart:async';
import 'package:flutter/material.dart';
main() => runApp(MaterialApp(home: HomePage()));
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => ApiCaller())),
),
);
}
}
class ApiCaller extends StatelessWidget {
static int counter = 0;
Future<String> apiCallLogic() async {
print("Api Called ${++counter} time(s)");
await Future.delayed(Duration(seconds: 2));
return Future.value("Hello World");
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Api Call Count: $counter'),
),
body: FutureBuilder(
future: apiCallLogic(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) return const CircularProgressIndicator();
if (snapshot.hasData)
return Text('${snapshot.data}');
else
return const Text('Some error happened');
},
),
);
}
}
这就是简单的代码,零模板。
具体到你的问题。
使用 initState
但请注意,您不能使用 async
召见 initState
因为它在初始化widget之前调用,顾名思义,就是在初始化widget之前调用。如果你想在UI创建之后做一些事情 didChangeDependencies
是伟大的。但千万不要用 build()
而不使用 FutureBuilder
或 StreamBuilder
简单的例子来演示一下。
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(MaterialApp(home: ExampleScreen()));
}
class ExampleScreen extends StatefulWidget {
ExampleScreen({Key key}) : super(key: key);
@override
_ExampleScreenState createState() => _ExampleScreenState();
}
class _ExampleScreenState extends State<ExampleScreen> {
List data = [];
bool isLoading = true;
void fetchData() async {
final res = await http.get("https://jsonplaceholder.typicode.com/users");
data = json.decode(res.body);
setState(() => isLoading = false);
}
// this method invokes only when new route push to navigator
@override
void initState() {
super.initState();
fetchData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: isLoading
? CircularProgressIndicator()
: Text(data?.toString() ?? ""),
),
);
}
}
一些生命周期的方法 StatefulWidget
's State
类。
initState()
:
描述该小组件所代表的用户界面部分。
框架在多种不同情况下调用此方法。
After calling initState. After calling didUpdateWidget. After receiving a call to setState. After a dependency of this State object changes (e.g., an InheritedWidget referenced by the previous build changes). After calling deactivate and then reinserting the State object into the tree at another location.
框架用本方法返回的widget替换该widget下面的子树,或者更新现有的子树,或者删除子树并填充一个新的子树,这取决于本方法返回的widget是否能更新现有子树的根,这由调用Widget.canUpdate决定。 阅读更多
didChangeDependencies()
:
当这个State对象的依赖关系发生变化时调用。
例如,如果前一次调用 build 引用的 InheritedWidget 后来发生了变化,框架会调用这个方法来通知这个对象的变化。
这个方法也会在
initState
. 可以说BuildContext
.dependOnInheritedWidgetOfExactType
从这个方法。 阅读更多
build()
(无状态小部件)
描述此小组件所代表的用户界面的一部分。
当此小组件被插入到给定 BuildContext 中的树中时,以及当此小组件的依赖关系发生变化时,框架会调用此方法(例如,一个
InheritedWidget
由这个小组件引用的变化)。) 阅读更多
didUpdateWidget(Widget oldWidget)
:
每当小组件配置改变时调用。
如果父小组件重建并要求树中的此位置更新以显示具有相同 runtimeType 和 "RuntimeType "的新小组件。
Widget.key
框架会更新这个widget的属性。State
对象来引用新的widget,然后使用之前的widget作为参数调用此方法。 阅读更多
有些小组件是无状态的,有些是有状态的。如果是无状态的widget,那么只有值可以改变,但UI的改变不会呈现。
有状态的widget也是一样,它的值和UI都会改变。
现在,将研究方法。
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
}
@override
void didUpdateWidget (
covariant Scaffold oldWidget
)
setState(() {});
@override
void dispose() {
// TODO: implement dispose
super.dispose();
}
如果你想进行API调用,那么你必须(或者真的应该)使用一个StatefulWidget。
演练一下,比方说,你的有状态的widget收到一些id,它需要进行API调用。
每次你的widget接收到一个新的id(包括第一次),那么你就需要用这个id进行一个新的API调用。
所以使用 didUpdateWidget
以检查是否有 id
改变了,如果它改变了(就像当小组件出现时一样,因为旧的 id
将是 null
)然后进行一个新的API调用(也设置适当的加载和错误状态!)
class MyWidget extends StatefulWidget {
Suggestions({Key key, this.someId}) : super(key: key);
String someId
@override
State<StatefulWidget> createState() => MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
dynamic data;
Error err;
bool loading;
@override
Widget build(BuildContext context) {
if(loading) return Loader();
if(err) return SomeErrorMessage(err);
return SomeOtherStateLessWidget(data);
}
@override
void didUpdateWidget(covariant MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// id changed in the widget, I need to make a new API call
if(oldWidget.id != widget.id) update();
}
update() async {
// set loading and reset error
setState(() => {
loading = true,
err = null
});
try {
// make the call
someData = await apiCall(widget.id);
// set the state
setState(() => data = someData)
} catch(e) {
// oops an error happened
setState(() => err = e)
}
// now we're not loading anymore
setState(() => loading = false);
}
}
我是Flutter的新手(真的,这个周末才开始玩),但它基本上重复了React范式,如果这对你有帮助的话。
个人喜好,我非常喜欢这种方法,而不是使用这种方法。FutureBuilder
现在,就像我说的,我是全新的)。逻辑只是更容易推理(对我来说)。