抽屉导航,可保留或恢复页面状态

时间:2018-06-27 16:53:20

标签: dart flutter

我正在构建Flutter应用程序,并且试图将我的头转向导航和状态。我在下面构建了一个非常简单的应用程序,该应用程序有两个页面,两个页面上都带有增量按钮。它们都共享一个Scaffold定义,因此两个页面上都有一个一致的导航抽屉。

基本上我想要的功能是FirstPageSecondPage是单例。因此,如果您将FirstPage上的计数器增加几次,请转到SecondPage,然后通过抽屉(而不是返回按钮)返回FirstPage,{{1} }的计数器仍应增加。

现在,如果执行此操作,则由于FirstPage似乎正在创建FirstPage的新实例。另外,如果您在Navigation.push()上并出于某些原因使用抽屉再次单击“第一页”,则不应丢失状态。

我看到Flutter开发人员在这里提到了“ non-linear navigation”一词,这让我认为这样的事情是可能的。感谢您的帮助。

FirstPage

1 个答案:

答案 0 :(得分:1)

我建议不要制作一个StatelessWidget来构建一个脚手架,并以一个子控件作为参数,而不是扩展Scaffold。通过按照自己的方式进行操作,您不会得到context或其他任何内容,因此通常建议在Flutter中采用封装而不是继承。

除了这样做,您还可以分离前进/后退逻辑。您可以先执行push(secondPage),然后pop回到首页,而不是每次都推送新页面。颤抖的导航是一个堆栈;每次按下按钮时,它都会添加到顶部,并且每次弹出按钮时,都将删除顶部元素。

如果您希望SecondPage保留其计数器,那将变得有些困难。您在这里有两个选择-第一个选择是在构建它时传递初始值。然后它将在内部递增该值,然后在您调用Navigator.pop(context, [value])时将其传递回去。它将保存为第一个屏幕的状态,然后再次按下页面时,您将传递新的初始值。

第二个选项是将计数器的值保留在高于第二个页面的水平上(使用StatefulWidget并可能使用InheritedWidget)-即,在任何包含您的导航器/ materialapp的小部件中。不过,这可能有点矫kill过正。

编辑:为响应OP的评论,已经明确表明实际上存在多个页面,而不仅仅是计数器,信息更为复杂。

有多种方法可以处理此问题;一种是使用Redux作为策略来实现该应用;与flutter_persist一起使用时,它可以成为持久状态的强大工具,我相信还有一个插件可以与firestore集成以进行云备份(对此不确定)。

我自己并不是redux的忠实拥护者;在我看来,这增加了很多开销,这与flutter的简单性背道而驰(尽管我可以看到对于大型应用程序或团队而言,它如何创建一致性)。

一个更简单的选项如编辑前所述;使用InheritedWidget包含更高级别的状态。我在下面做了一个简单的例子:

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

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

class MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return CounterInfo(
      child: new MaterialApp(
        routes: {
          "/": (context) => new FirstPage(),
          "/second": (context) => new SecondPage(),
        },
      ),
    );
  }
}

class _InheritedCounterInfo extends InheritedWidget {
  final CounterInfoState data;

  _InheritedCounterInfo({
    Key key,
    @required this.data,
    @required Widget child,
  }) : super(key: key, child: child);

  @override
  bool updateShouldNotify(_InheritedCounterInfo old) => true;
}

class CounterInfo extends StatefulWidget {
  final Widget child;

  const CounterInfo({Key key, this.child}) : super(key: key);

  static CounterInfoState of(BuildContext context) {
    return (context.inheritFromWidgetOfExactType(_InheritedCounterInfo) as _InheritedCounterInfo).data;
  }

  @override
  State<StatefulWidget> createState() => new CounterInfoState();
}

class CounterInfoState extends State<CounterInfo> {

  int firstCounter = 0;
  int secondCounter = 0;

  void incrementFirst() {
    setState(() {
      firstCounter++;
    });
  }

  void incrementSecond() {
    setState(() {
      secondCounter++;
    });
  }

  @override
  Widget build(BuildContext context) => new _InheritedCounterInfo(data: this, child: widget.child);
}

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counterInfo = CounterInfo.of(context);

    return new MyScaffold(
      title: "First page",
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("${counterInfo.firstCounter}"),
            RaisedButton(onPressed: counterInfo.incrementFirst, child: Icon(Icons.add)),
          ],
        ),
      ),
    );
  }
}

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counterInfo = CounterInfo.of(context);

    return new MyScaffold(
      title: "Second page",
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("${counterInfo.secondCounter}"),
            RaisedButton(onPressed: counterInfo.incrementSecond, child: Icon(Icons.add)),
          ],
        ),
      ),
    );
  }
}

class MyScaffold extends StatelessWidget {
  final String title;
  final Widget child;

  const MyScaffold({Key key, this.title, this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(title: new Text(title)),
      drawer: Drawer(
          child: ListView(padding: EdgeInsets.zero, children: <Widget>[
        SizedBox(height: 100.0),
        ListTile(
          title: Text("First Page"),
          onTap: () => Navigator.of(context).pushReplacementNamed("/"),
        ),
        ListTile(title: Text("Second Page"), onTap: () => Navigator.of(context).pushReplacementNamed("/second")),
      ])),
      body: child,
    );
  }
}

无论您是采用这种方式还是考虑使用Redux,this都是一个有用的网站,具有各种Flutter体系结构模型,包括使用继承的小部件。