弹出时强制颤动导航器重新加载状态

时间:2018-04-12 19:56:30

标签: dart flutter

我在Flutter中有一个Dropzone.autoDiscover = false; $(document).ready(function(){ $("#mydropzone").dropzone({ init: function() {...} ); }); 按钮,使用StatefulWidget将我导航到另一个StatefulWidget。在第二个小部件上,我正在改变全局状态(一些用户首选项)。当我从第二个小部件返回到第一个小部件时,使用Navigator.push()第一个小部件处于旧状态,但我想强制它重新加载。知道怎么做吗?我有一个想法,但它看起来很难看:

  1. 弹出以删除第二个小部件(当前的小部件)
  2. 再次弹出以删除第一个小部件(上一个小部件)
  3. 推送第一个小部件(它应强制重绘)

19 个答案:

答案 0 :(得分:13)

简单技巧是使用 Navigator.pushReplacement 方法

第1页

Navigator.pushReplacement(
  context,
  MaterialPageRoute(
    builder: (context) => Page2(),
  ),
);

第2页

Navigator.pushReplacement(
  context,
  MaterialPageRoute(
    builder: (context) => Page1(),
  ),
);

答案 1 :(得分:11)

你可以在这里做几件事。 @ Mahi的回答虽然正确可能会更简洁,实际上使用push而不是showDialog,正如OP所询问的那样。这是一个使用Navigator.push

的示例
import 'package:flutter/material.dart';

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Container(
      color: Colors.green,
      child: new Column(
        children: <Widget>[
          new RaisedButton(
            onPressed: () => Navigator.pop(context),
            child: new Text("back"),
          ),
        ],
      ),
    );
  }
}

class FirstPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new FirstPageState();
}

class FirstPageState extends State<FirstPage> {

  Color color = Colors.white;

  @override
  Widget build(BuildContext context) {
    return new Container(
      color: color,
      child: new Column(
        children: <Widget>[
          new RaisedButton(
              child: new Text("next"),
              onPressed: () {
                Navigator
                    .push(
                  context,
                  new MaterialPageRoute(builder: (context) => new SecondPage()),
                )
                    .then((value) {
                  setState(() {
                    color = color == Colors.white ? Colors.grey : Colors.white;
                  });
                });
              }),
        ],
      ),
    );
  }
}

void main() => runApp(
      new MaterialApp(
        builder: (context, child) => new SafeArea(child: child),
        home: new FirstPage(),
      ),
    );

但是,还有另一种方法可以很好地适应您的用例。如果您使用global作为影响第一页构建的内容,则可以使用InheritedWidget来定义全局用户首选项,每次更改时,您的FirstPage都将重建。这甚至可以在无状态小部件中工作,如下所示(但也应该在有状态小部件中工作)。

flutter中的inheritedWidget的一个例子是应用程序的主题,虽然它们在一个小部件中定义它,而不是像我这里那样直接构建它。

import 'package:flutter/material.dart';
import 'package:meta/meta.dart';

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Container(
      color: Colors.green,
      child: new Column(
        children: <Widget>[
          new RaisedButton(
            onPressed: () {
              ColorDefinition.of(context).toggleColor();
              Navigator.pop(context);
            },
            child: new Text("back"),
          ),
        ],
      ),
    );
  }
}

class ColorDefinition extends InheritedWidget {
  ColorDefinition({
    Key key,
    @required Widget child,
  }): super(key: key, child: child);

  Color color = Colors.white;

  static ColorDefinition of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(ColorDefinition);
  }

  void toggleColor() {
    color = color == Colors.white ? Colors.grey : Colors.white;
    print("color set to $color");
  }

  @override
  bool updateShouldNotify(ColorDefinition oldWidget) =>
      color != oldWidget.color;
}

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var color = ColorDefinition.of(context).color;

    return new Container(
      color: color,
      child: new Column(
        children: <Widget>[
          new RaisedButton(
              child: new Text("next"),
              onPressed: () {
                Navigator.push(
                  context,
                  new MaterialPageRoute(builder: (context) => new SecondPage()),
                );
              }),
        ],
      ),
    );
  }
}

void main() => runApp(
      new MaterialApp(
        builder: (context, child) => new SafeArea(
              child: new ColorDefinition(child: child),
            ),
        home: new FirstPage(),
      ),
    );

如果您使用继承的窗口小部件,则不必担心会看到您推送的页面的弹出窗口,这将适用于基本用例,但最终可能会在更复杂的场景中出现问题。

答案 2 :(得分:9)

有两件事,从

传递数据
  • 第一页到第二页

    在第一页中使用此

    // sending "Foo" from 1st
    Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
    

    在第二页中使用它。

    class Page2 extends StatelessWidget {
      final String string;
    
      Page2(this.string); // receiving "Foo" in 2nd
    
      ...
    }
    

  • 第二页到第一页

    在第二页中使用此

    // sending "Bar" from 2nd
    Navigator.pop(context, "Bar");
    

    在第1页中使用它,它与以前使用的相同,但几乎没有修改。

    // receiving "Bar" in 1st
    String received = await Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
    

答案 3 :(得分:8)

只需在 page1() 上的 Navigator.push 之后添加 .then((value) { setState(() {});,如下所示:

Navigator.push(context,MaterialPageRoute(builder: (context) => Page2())).then((value) { setState(() {});

现在,当您从 page2 使用 Navigator.pop(context) 时,您的 page1 会自行重建

答案 4 :(得分:5)

这项工作真的很好,我是从以下文档中获得的:flutter doc

我定义了从首页控制导航的方法。

_navigateAndDisplaySelection(BuildContext context) async {
    final result = await Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => AddDirectionPage()),
    );

    //below you can get your result and update the view with setState
    //changing the value if you want, i just wanted know if i have to  
    //update, and if is true, reload state

    if (result) {
      setState(() {});
    }
  }

因此,我在墨水池的action方法中调用它,但是也可以从按钮中调用它:

onTap: () {
   _navigateAndDisplaySelection(context);
},

最后在第二页中,要返回一些内容(我返回了布尔值,您可以返回任意值):

onTap: () {
  Navigator.pop(context, true);
}

答案 5 :(得分:5)

onTapFunction(BuildContext context) async {
    final reLoadPage = await Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => IdDetailsScreen()),
    );

    if (reLoadPage) {
        setState(() {});
    }
}

现在在从第二页执行 Navigator.pop 返回第一页时只返回一些值,在我的情况下,如果 bool 输入

onTap: () {
    Navigator.pop(context, true);
}

答案 6 :(得分:3)

我的解决方案是在SecondPage上添加一个函数参数,然后接收从FirstPage完成的重载函数,然后在Navigator.pop(context)行之前执行该函数。

第一页

refresh() {
setState(() {
//all the reload processes
});
}

然后继续推送到下一页...

Navigator.push(context, new MaterialPageRoute(builder: (context) => new SecondPage(refresh)),);

第二页

final Function refresh;
SecondPage(this.refresh); //constructor

然后在导航器弹出行之前

widget.refresh(); // just refresh() if its statelesswidget
Navigator.pop(context);

需要从上一页重新加载的所有内容都应在弹出窗口后进行更新。

答案 7 :(得分:3)

对我来说,这似乎可行:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

DRIVER_PATH = '/Users/jasonbeedle/Desktop/snaviescraper/chromedriver'

options = Options()
options.page_load_strategy = 'normal'

# Navigate to url
driver = webdriver.Chrome(options=options, executable_path=DRIVER_PATH)
driver.get("https://www.canalplus/programme-tv")


results = driver.find_elements_by_class(
    'cardTitle')

print(results.text)

driver.quit

然后只需在孩子中调用Navigator.of(context).pushNamed("/myRoute").then((value) => setState(() {}));

答案 8 :(得分:2)

///将此位置推送到第二个屏幕(在异步功能中)

Function f;
f = await Navigator.pushNamed(context, 'Screenname');
f();

//将其放置在弹出的位置

 Navigator.pop(context, () {
                setState(() {});
              });

///它的作用是在弹出后将setState函数作为参数传递给屏幕

答案 9 :(得分:1)

如果使用的是警报对话框,则可以使用在关闭对话框时完成的Future。将来完成后,您可以强制窗口小部件重新加载状态。

首页

onPressed: () async {
    await showDialog(
       context: context,
       builder: (BuildContext context) {
            return AlertDialog(
                 ....
            );
       }
    );
    setState(() {});
}

在警报对话框中

Navigator.of(context).pop();

答案 10 :(得分:1)

您可以在弹出上下文时传回dynamic result,然后在值setState((){})时调用true,否则只需保持原状。

我已粘贴了一些代码段供您参考。

handleClear() async {
    try {
      var delete = await deleteLoanWarning(
        context,
        'Clear Notifications?',
        'Are you sure you want to clear notifications. This action cannot be undone',
      );
      if (delete.toString() == 'true') {
        //call setState here to rebuild your state.

      }
    } catch (error) {
      print('error clearing notifications' + error.toString());
             }
  }



Future<bool> deleteLoanWarning(BuildContext context, String title, String msg) async {

  return await showDialog<bool>(
        context: context,
        child: new AlertDialog(
          title: new Text(
            title,
            style: new TextStyle(fontWeight: fontWeight, color: CustomColors.continueButton),
            textAlign: TextAlign.center,
          ),
          content: new Text(
            msg,
            textAlign: TextAlign.justify,
          ),
          actions: <Widget>[
            new Container(
              decoration: boxDecoration(),
              child: new MaterialButton(
                child: new Text('NO',),
                onPressed: () {
                  Navigator.of(context).pop(false);
                },
              ),
            ),
            new Container(
              decoration: boxDecoration(),
              child: new MaterialButton(
                child: new Text('YES', ),
                onPressed: () {
                  Navigator.of(context).pop(true);
                },
              ),
            ),
          ],
        ),
      ) ??
      false;
}

此致 鳅

答案 11 :(得分:1)

推送后只需使用“then”,当导航器弹出时,它将触发 setState 并且视图将刷新。

Navigator.push(blabla...).then((value) => setState(() {}))

答案 12 :(得分:0)

需要强制重建我的无状态小部件之一。不想使用有状态。想出了这个解决方案:

await Navigator.of(context).pushNamed(...);
ModalRoute.of(enclosingWidgetContext);

请注意,context和enclosingWidgetContext可以是相同或不同的上下文。例如,如果您从StreamBuilder内部推送,它们将有所不同。

我们在这里不使用ModalRoute做任何事情。单独订阅的行为足以强制重建。

答案 13 :(得分:0)

这个简单的代码对我有用,它可以进入根目录并重新加载状态:

    ...
    onPressed: () {
         Navigator.of(context).pushNamedAndRemoveUntil('/', ModalRoute.withName('/'));
                },
    ...

答案 14 :(得分:0)

简而言之,您应该使小部件监视状态。为此,您需要状态管理。

我的方法基于Flutter Architecture SamplesFlutter Docs中说明的提供者。请参考它们以获取更简洁的说明,但大致步骤如下:

  • 用小部件需要观察的状态定义状态模型。

您可能有多个状态,例如dataisLoading,以等待某些API进程。该模型本身扩展了ChangeNotifier

  • 使用watcher类包装依赖于这些状态的小部件。

这可以是ConsumerSelector

  • 当您需要“重新加载”时,基本上可以更新这些状态并广播更改。

对于状态模型,该类看起来或多或少如下。请注意notifyListeners,它会广播更改。

class DataState extends ChangeNotifier{

  bool isLoading;
  
  Data data;

  Future loadData(){
    isLoading = true;
    notifyListeners();

    service.get().then((newData){
      isLoading = false;
      data = newData;
      notifyListeners();
    });
  }
  
}

现在是小部件。这将是非常简单的代码。

return ChangeNotifierProvider(

  create: (_) => DataState()..loadData(),
      
  child: ...{
    Selector<DataState, bool>(

        selector: (context, model) => model.isLoading,

        builder: (context, isLoading, _) {
          if (isLoading) {
            return ProgressBar;
          }

          return Container(

              child: Consumer<DataState>(builder: (context, dataState, child) {

                 return WidgetData(...);

              }
          ));
        },
      ),
  }
);

状态模型的实例由ChangeNotifierProvider提供。选择器和消费者监视状态,分别为isLoadingdata的状态。有not much difference between them,但个人使用方式将取决于其构建器提供的内容。消费者提供对状态模型的访问权限,因此对于直接在其下的任何小部件调用loadData都很简单。

如果没有,则可以使用Provider.of。如果我们想在从第二个屏幕返回时刷新页面,则可以执行以下操作:

await Navigator.push(context, 
  MaterialPageRoute(
    builder: (_) {
     return Screen2();
));

Provider.of<DataState>(context, listen: false).loadData();

答案 15 :(得分:0)

为我工作:

...
onPressed: (){pushUpdate('/somePageName');}
...

pushUpdate (string pageName) async {      //in the same class
  await pushPage(context, pageName);
  setState(() {});
}


//---------------------------------------------
//general sub
pushPage (context, namePage) async {
  await Navigator.pushNamed(context, namePage);
}

在这种情况下,弹出的方式(在UI中带有按钮或在android中为“后退”)都无关紧要。

答案 16 :(得分:0)

您可以使用pushReplacement并指定新的路线

答案 17 :(得分:0)

今天,我遇到了同样的情况,但是我设法以更简单的方式解决了它,我只是定义了一个全局变量,该变量在第一个有状态类中使用,当我导航到第二个有状态小部件时,我让它更新了值全局变量的值,它会自动强制第一个窗口小部件更新。这是一个示例(我匆忙编写了它,所以我没有放置脚手架或材料应用程序,我只是想说明我的观点):

import 'package:flutter/material.dart';
int count = 0 ;

class FirstPage extends StatefulWidget {
FirstPage({Key key}) : super(key: key);

@override
_FirstPageState createState() => _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
@override
Widget build(BuildContext context) {
return InkWell(
onTap(){
Navigator.of(context).push(MaterialPageRoute(builder: (context) =>
                  new SecondPage());
},
child: Text('First', style : TextStyle(fontSize: count == 0 ? 20.0 : 12.0)),
),

}


class SecondPage extends StatefulWidget {
SecondPage({Key key}) : super(key: key);

@override
_SecondPageState createState() => _SecondPageState();
}

class _SecondPageState extends State<SecondPage> {
@override
Widget build(BuildContext context) {
return IconButton(
         icon: new Icon(Icons.add),
         color: Colors.amber,
         iconSize: 15.0,
         onPressed: (){
         count++ ;
         },
       ),
     }

答案 18 :(得分:0)

我遇到了类似的问题。

请试试这个:

在第一页:

Navigator.push( context, MaterialPageRoute( builder: (context) => SecondPage()), ).then((value) => setState(() {}));

从 SecondPage() 返回到 FirstPage() 后,“then”语句将运行并刷新页面。