即使父小部件重建,也要保留颤动中小部件的状态

时间:2021-04-21 14:51:12

标签: flutter dart state

当使用 BottomNavigationBar 在小部件之间切换时,我试图保留小部件页面的状态。我已经读过 here 说我可以使用 IndexedStack 来做到这一点,但是,这在我的情况下不起作用,原因有两个:

  1. 在页面之间切换时,显示页面的 Scaffold 会重建,因为对于某些(但不是全部)页面,Scaffold 应该扩展:Scaffold( extendBody: _pageIndex == 1, ...)
  2. 应该在第一次打开页面时才第一次构建页面,而不是从一开始就构建

这里有一个小例子,表明 IndexStack 没有按预期工作,因为 Scaffold 重建:

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  int _pageIndex = 1;
  List<Widget> _pages = [Text("hi"), Counter()];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBody: _pageIndex == 1,
      appBar: AppBar(),
      body: IndexedStack(
        children: _pages,
        index: _pageIndex,
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Goto 0',),
          BottomNavigationBarItem(icon: Icon(Icons.business), label: 'Goto 1',),
        ],
        currentIndex: _pageIndex,
        onTap: (int index) {
          setState(() {
            _pageIndex = index;
          });
          print("idx " + _pageIndex.toString());
        },
      ),
    );
  }
}

Demo showing that the state is not preserved

这是可以被任何其他有状态小部件替换的计数器:


class Counter extends StatefulWidget {
  @override
  _CounterState createState() => _CounterState();
}

//this part is not important, just to show that state is lost
class _CounterState extends State<Counter> {
  int _count = 0;

  @override
  void initState() {
    _count = 0;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: TextButton(
        child: Text("Count: " + _count.toString(), style: TextStyle(fontSize: 20),),
        onPressed: () {
          setState(() {
            _count++;
          });
        },
      ),
    );
  }
}

1 个答案:

答案 0 :(得分:1)

首先,好问题!诀窍是使用 KeyedSubtree,并根据页面是否已被访问而有条件地呈现页面。

您可以通过这种方式调整您的代码以实现您想要的行为:

class Page {
  const Page(this.subtreeKey, {required this.child});

  final GlobalKey subtreeKey;
  final Widget child;
}

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  var _pageIndex = 1;

  final _pages = [
    Page(GlobalKey(), child: Text('Hi')),
    Page(GlobalKey(), child: Counter()),
  ];

  final _builtPages = List<bool>.generate(2, (_) => false);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBody: _pageIndex == 1,
      appBar: AppBar(),
      body: Stack(
        fit: StackFit.expand,
        children: _pages.map(
          (page) {
            return _buildPage(
              _pages.indexOf(page),
              page,
            );
          },
        ).toList(),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Goto 0',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.business),
            label: 'Goto 1',
          ),
        ],
        currentIndex: _pageIndex,
        onTap: (int index) {
          setState(() {
            _pageIndex = index;
          });
          print("idx " + _pageIndex.toString());
        },
      ),
    );
  }

  Widget _buildPage(
    int tabIndex,
    Page page,
  ) {
    final isCurrentlySelected = tabIndex == _pageIndex;

    _builtPages[tabIndex] = isCurrentlySelected || _builtPages[tabIndex];

    final Widget view = KeyedSubtree(
      key: page.subtreeKey,
      child: _builtPages[tabIndex] ? page.child : Container(),
    );

    if (tabIndex == _pageIndex) {
      return view;
    } else {
      return Offstage(child: view);
    }
  }
}

您应该能够修改此代码以添加更多选项卡、功能等。