每当页面在屏幕上时如何重新加载页面-Flutter

时间:2020-05-19 16:22:49

标签: flutter dart flutter-layout

每次在屏幕上可见页面时,在flutter中是否有可用的回调?在ios中,有一些委托方法,例如viewWillAppearviewDidAppearviewDidload

我想在屏幕上显示特定页面时调用API调用。

注意:我不是在询问应用程序的状态,例如前景,背景,暂停,恢复。

谢谢!

5 个答案:

答案 0 :(得分:5)

专门针对您的问题:

使用initState,但是请注意,您不能在async中使用initState调用,因为它会在初始化小部件之前按名称的意思进行调用。如果您要在创建UI后做点事,didChangeDependencies很棒。但切勿在未使用FutureBuilderStreamBuilder

的情况下使用build()

简单的示例进行演示:

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() ?? ""),
      ),
    );
  }
}

StatefulWidgetState类的某些生命周期方法:

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.canUpdate确定的子树。 Read more

didChangeDependencies()

此State对象的依赖项更改时调用。

例如,如果先前的构建调用引用了 后来更改的InheritedWidget,框架将其称为 通知此对象有关更改的方法。

此方法也将在initState之后立即调用。这是安全的 从此方法调用BuildContextdependOnInheritedWidgetOfExactTypeRead more

build()(无状态小组件)

描述此小部件代表的用户界面部分。

当将此小部件插入到 给定BuildContext中的树,以及此小部件的依赖性 更改(例如,此小部件引用的InheritedWidget更改)。 Read more

didUpdateWidget(Widget oldWidget)

每当窗口小部件配置更改时调用。

如果父窗口小部件重建并请求在 更新树以显示具有相同runtimeType和 Widget.key,框架将更新此窗口小部件的属性 State对象引用新的小部件,然后使用 前一个小部件作为参数。 Read more

答案 1 :(得分:3)

有些小部件是无状态的,有些是有状态的。如果它是无状态窗口小部件,则只能更改值,但不会呈现UI更改。

有状态窗口小部件的方法相同,它的值和UI都会改变。

现在,将研究方法。

  1. initState():这是在创建小部件时但在构造函数调用之后调用的第一个方法。

@override
void initState() {
   // TODO: implement initState
   super.initState();
}
  1. didChangeDependecies()-在此State对象的依赖项更改时调用。在initState方法之后立即调用。

@override
  void didChangeDependencies() {
    super.didChangeDependencies();
  }
  1. didUpdateWidget()-每当更改窗口小部件配置时都会调用它。框架始终在didUpdateWidget之后调用构建

@override
void didUpdateWidget (
   covariant Scaffold oldWidget
)
  1. setState()-每当State对象的内部状态想要更改时,都需要在setState方法内部调用它。

 setState(() {});
  1. dispose()-在将该对象从树中永久删除时调用。

@override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
  }

答案 2 :(得分:2)

每次显示屏幕时,您都不需要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');
        },
      ),
    );
  }
}

这是带有零样板的简单代码。

答案 3 :(得分:0)

如果要进行API调用,则必须(或者实际上应该)使用StatefulWidget。

遍历它,假设您的有状态窗口小部件收到一些ID,该ID需要进行API调用。

每次您的小部件收到新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(现在,就像我说的那样,我是全新的)。逻辑(对于我而言)更容易推理。

答案 4 :(得分:0)

最简单的方法是使用need_resume

1。将此添加到您程序包的pubspec.yaml文件中:

dependencies:
  need_resume: ^1.0.4

2。使用类型ResumableState而非State为有状态窗口小部件创建状态类

class HomeScreen extends StatefulWidget {
    @override
    HomeScreenState createState() => HomeScreenState();
}

class HomeScreenState extends ResumableState<HomeScreen> {
    @override
    void onReady() {
        // Implement your code inside here

        print('HomeScreen is ready!');
    }

    @override
    void onResume() {
        // Implement your code inside here

        print('HomeScreen is resumed!');
    }

    @override
    void onPause() {
        // Implement your code inside here

        print('HomeScreen is paused!');
    }


    @override
    Widget build(BuildContext context) {
        return Scaffold(
            body: Center(
                child: RaisedButton(
                    child: Text('Go to Another Screen'),
                    onPressed: () {
                       print("hi");
                    },
                ),
            ),
        );
    }
}