Flutter:在 Drawer 中更改 body 时保留状态

Posted

技术标签:

【中文标题】Flutter:在 Drawer 中更改 body 时保留状态【英文标题】:Flutter: preserve state when changing body in Drawer 【发布时间】:2019-09-27 06:11:51 【问题描述】:

我正在使用 Material 库的 Drawer 类创建一个带有导航抽屉的 Flutter 应用程序。包含DrawerWidgetStatefulWidgetScaffold 的内容根据导航抽屉上的选定项显示。内容是WidgetOneWidgetTwo,两者都保持自己的状态为StatefulWidgets。请参阅下面的代码示例。

目前,当我从一个小部件切换到另一个小部件并返回时,之前显示的小部件的整个状态都会重新加载。这并不理想,因为这两个小部件都有来自 API 的网络调用,并且需要相应地重绘。

到目前为止我已经尝试过什么

在两个子小部件上实现AutomaticKeepAliveClientMixin,如下所示:https://***.com/a/50074067/4009506。但是,这似乎不起作用。 按照此处的建议使用IndexedStack:https://***.com/a/54999503/4009506。这会直接加载所有小部件,即使它们尚未显示。

代码

class DrawerWidget extends StatefulWidget 
  @override
  State<StatefulWidget> createState() => _DrawerState();


class _DrawerState extends State<DrawerWidget> 
  Widget _activeWidget;

  @override
  void initState() 
    _activeWidget = FirstWidget();
    super.initState();
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
        appBar: AppBar(title: Text("Drawer demo")),
        drawer: Drawer(
          child: ListView(
            padding: EdgeInsets.zero,
            children: <Widget>[
              ListTile(
                title: Text("First Widget"),
                onTap: () 
                  setState(() 
                    _activeWidget = FirstWidget();
                  );
                ,
              ),
              ListTile(
                title: Text("Second Widget"),
                onTap: () 
                  setState(() 
                    _activeWidget = SecondWidget();
                  );
                ,
              ),
            ],
          ),
        ),
        body: _activeWidget);
  


class FirstWidget extends StatefulWidget 
  // [..]


class SecondWidget extends StatefulWidget 
  // [..]

想要的结果

WidgetOneWidgetTwo 仅在初始加载时加载(在 Drawer 中选择它们之后)。如果之前已经加载过,则切换到另一个小部件并返回不应重新加载小部件。子小部件不应全部直接加载,只有在它们最初被按下时才加载。

实际结果

FirstWidgetSecondWidget 每次在 Drawer 中被选中时都会重新加载和重绘。

【问题讨论】:

您需要一个 PageView 或类似的东西,KeepAlive 才能工作。 【参考方案1】:

我通过使用PageView 并在所有子小部件上实现AutomaticKeepAliveClientMixin 解决了这个问题:

class DrawerWidget extends StatefulWidget 
  @override
  State<StatefulWidget> createState() => _DrawerState();


class _DrawerState extends State<DrawerWidget> 
  final _pageController = PageController();

  @override
  Widget build(BuildContext context) 
    return Scaffold(
        appBar: AppBar(title: Text("Drawer demo")),
        drawer: Drawer(
          child: ListView(
            padding: EdgeInsets.zero,
            children: <Widget>[
              ListTile(
                title: Text("First Widget"),
                onTap: () 
                  _pageController.jumpToPage(0);
                ,
              ),
              ListTile(
                title: Text("Second Widget"),
                onTap: () 
                  _pageController.jumpToPage(1);
                ,
              ),
            ],
          ),
        ),
        body: PageView(
          controller: _pageController,
          children: <Widget>[
            FirstWidget(),
            SecondWidget()
          ],
          physics: NeverScrollableScrollPhysics()
        ));
  


class FirstWidget extends StatefulWidget 
  @override
  State<StatefulWidget> createState() => _FirstWidgetState();


class _FirstWidgetState extends State<FirstWidget> with AutomaticKeepAliveClientMixin<FirstWidget> 
  // [..]

  @override
  bool get wantKeepAlive => true;
 

class SecondWidget extends StatefulWidget 
  @override
  State<StatefulWidget> createState() => _SecondWidgetState();


class _SecondWidgetState extends State<SecondWidget> with AutomaticKeepAliveClientMixin<SecondWidget> 
  // [..]

  @override
  bool get wantKeepAlive => true;

现在,所有小部件仅在导航抽屉中的初始切换时加载,并且在切换回时不会重新加载。

【讨论】:

以上是关于Flutter:在 Drawer 中更改 body 时保留状态的主要内容,如果未能解决你的问题,请参考以下文章

在 Flutter 中更改 Drawer 小部件的顶部颜色

Flutter,如何制作按钮以在 Flutter 中打开抽屉 [重复]

Flutter之抽屉组件drawer,设置drawer宽度——Flutter基础系列

AppBar 下方的 Flutter Drawer

Flutter-drawer

Flutter -------- Drawer侧滑