【问题标题】:Flutter - Always execute a function when the page appearsFlutter - 页面出现时总是执行一个函数
【发布时间】:2018-04-12 03:12:20
【问题描述】:

如何让name() 函数在出现Page1 页面时运行?

在下面的代码中,在转到Page2 之前,我执行dispose() 已经在Page2 内部,如果我点击后退按钮或Android 的物理按钮,则不会执行函数name(),但如果我点击'go to Page1' 按钮,则会执行函数name()

Page1出现时,你能帮我一直执行name()函数吗?

import 'package:flutter/material.dart';

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {  
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
      routes: <String, WidgetBuilder> {
        '/page2': (BuildContext context) => new Page2(),
      },
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String nameScreen;

  String name() {
    return 'foo1';
  }

  @override
  void initState() {
    super.initState();
    this.nameScreen = name();

  }

  @override
  void dispose() {
    this.nameScreen = '';
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Page 1'),
        backgroundColor: new Color(0xFF26C6DA),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new RaisedButton(
              child: const Text('go to Page2'),
              onPressed: () async {
                dispose();
                bool isLoggedIn = await Navigator.of(context).pushNamed('/page2');
                if (isLoggedIn) {
                  setState((){
                    this.nameScreen = name();
                  });
                }
              },
            ),            
            new Text(
              '$nameScreen',              
            ),
          ],
        ),
      ),
    );
  }
}

class Page2 extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return new Scaffold( 
      appBar: new AppBar(
        title: new Text('Page 2'),
        backgroundColor: new Color(0xFFE57373)
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new RaisedButton(
              child: const Text('go back to Page1'),
              onPressed: () {
                Navigator.pop(context, true);
              }
            ),
          ],
        ),
      ),
    );
  }
}

【问题讨论】:

    标签: dart flutter


    【解决方案1】:

    当您愿意 pop 并稍后更改 State 时,根本不需要调用 dispose,因为 dispose 将从树中删除当前对象,这不会转化为您的逻辑正在努力发展。

    您确实可以覆盖BackButton 并将Navigator.pop(context, result) 的相同调用传递给它。检查以下示例,我对您的代码进行了一些调整,以向您展示nameScreen 字段的每个State 之间的区别。希望对您有所帮助。

    class MyHomePage extends StatefulWidget {
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      String nameScreen = "";
    
      String name() {
        return 'foo1';
      }
    
      @override
      void initState() {
        super.initState();
        this.nameScreen = "From initState";
    
      }
    
    @override
    void dipose(){
        super.dispose();
    }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Page 1'),
            backgroundColor: Color(0xFF26C6DA),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                RaisedButton(
                  child: const Text('go to Page2'),
                  onPressed: () async {
                    //dispose(); ///No need for dispose
                    String result = await Navigator.of(context).pushNamed('/page2');
    
                      setState((){
                        this.nameScreen = result;
                      });
    
                  },
                ),
                Text(
                  '$nameScreen',
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class Page2 extends StatelessWidget{
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
              leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: ()async{
                Navigator.pop(context,"From BackButton");
              }),
              title: const Text('Page 2'),
              backgroundColor: Color(0xFFE57373)
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                RaisedButton(
                    child: const Text('go back to Page1'),
                    onPressed: () {
                      Navigator.pop(context, "From RaisedButton");
                    }
                ),
              ],
            ),
          ),
        );
      }
    

    【讨论】:

    • @azira 感谢您的帮助,如果用户通过 Android 的物理按钮返回,则无法正常工作。有没有办法解决这个问题?
    • 解决问题的一种方法是我刚刚在setState 之上添加了if(result == null) {result = "Android back button";},以防用户使用物理Android 按钮
    • 我绝对认为这是 Flutter 框架中缺少的一个主要功能。检测小部件何时重新聚焦是一个非常明显的用例(例如,刷新来自某个远程源的数据)。类似于原生 Android 的 onResume 回调。
    • 仅针对您提到的特定用例。您可以使用 StreamBuilder 来监听源代码的更改并根据更改重新构建您的小部件
    【解决方案2】:

    一种方法是在导航器小部件上使用.whenComplete() 方法。

    假设您要从第一页转到第二页。在这里,您必须将 functionThatSetsTheState 作为指向代码导航部分的指针传递。

    函数看起来像这样,应该在一个有状态的小部件中。

    void functionThatSetsTheState(){
     setState(() {});
    }
    

    OnPressedOnTapOnLongPress 等的导航代码

    Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => SecondPage())).whenComplete(() => {functionThatSetsTheState()});
    

    【讨论】:

      【解决方案3】:

      您可以覆盖第二个屏幕上的后退按钮。而不是系统关闭,做

      WillPopScope(
          onWillPop: () {
             print('back pressed');
             Navigator.pop(context, "From BackButton");
             return true;
            },
          child: Scaffold(...)
      

      【讨论】:

        【解决方案4】:

        如果您想在页面出现时执行某些功能,您可以使用 RouteObserves,您必须在要运行的页面上实现 RouteAware,每当屏幕出现时执行该功能,您将不得不执行类似的操作这个在你的第1页

        final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>(); // add this on your main class
        void main() {
          runApp(MaterialApp(
            home: Container(),
            navigatorObservers: [routeObserver], // add observer here;
          ));
        } 
        
        
        
        
        
        // your page where func should run whenever this page appears
        class MyHomePage extends StatefulWidget with RouteAware {
          @override
          _MyHomePageState createState() => _MyHomePageState();
        }
        
        class _MyHomePageState extends State<MyHomePage> {
          String nameScreen = "";
        
          String name() {
            return 'foo1';
          }
        
          @override
          void initState() {
            super.initState();
            this.nameScreen = "From initState";
        
          }
        
         @override
          void didChangeDependencies() {
            super.didChangeDependencies();
            routeObserver.subscribe(this, ModalRoute.of(context));
          }
        
          @override
          void dispose() {
            routeObserver.unsubscribe(this);
            super.dispose();
          }
        
        // implementing RouteAware method
        void didPush() {
        // Route was pushed onto navigator and is now topmost route.
            name(); // your func goes here
          }
        
        
          @override
          Widget build(BuildContext context) {
            return Scaffold(
              appBar: AppBar(
                title: const Text('Page 1'),
                backgroundColor: Color(0xFF26C6DA),
              ),
              body: Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    RaisedButton(
                      child: const Text('go to Page2'),
                      onPressed: () async {
                        //dispose(); ///No need for dispose
                        String result = await Navigator.of(context).pushNamed('/page2');
        
                          setState((){
                            this.nameScreen = result;
                          });
        
                      },
                    ),
                    Text(
                      '$nameScreen',
                    ),
                  ],
                ),
              ),
            );
          }
        }
        

        您可以前往此链接了解更多说明 https://api.flutter.dev/flutter/widgets/RouteObserver-class.html

        【讨论】:

        • 覆盖 didPopNext 允许您在堆栈中的下一页弹出并且实现者再次可见时执行某些操作。
        【解决方案5】:

        假设您想从第 1 页导航到第 2 页,并在第 2 页加载后立即在第 2 页中执行一个函数(用于在第 2 页加载时立即显示对话框):

        您可以通过添加第 2 页的 initStatedidChangeDependencies 来做到这一点:

         WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
            // Function to execute
          });
        

        如果您想在执行函数之前添加一些逻辑来放置条件,只需在页面 1 中推送一个参数:

        Navigator.of(context).pushNamed("/page-2", arguments : true)
        

        最后第2页的代码变成了:

         _functionToExecute(){
          print("done");
          }
         @override
         void didChangeDependencies() {
         
          WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
            if(ModalRoute.of(context).settings.arguments)
            _functionToExecute()
          });
         }
        

        【讨论】:

          猜你喜欢
          • 2017-07-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-30
          • 2015-12-21
          • 2020-09-13
          • 1970-01-01
          • 2020-05-16
          相关资源
          最近更新 更多