簡體   English   中英

即使父小部件重建,在 flutter 中保留小部件的 state

[英]Preserve state of widget in flutter even though parent widget rebuilds

在使用 BottomNavigationBar 在小部件之間切換時,我試圖保留小部件頁面的BottomNavigationBar 我在這里讀到我可以使用IndexedStack來做到這一點,但是,這在我的情況下不起作用,原因有兩個:

  1. 在頁面之間切換時,顯示頁面的Scaffold會被重建,因為對於某些(但不是所有)頁面,應該擴展ScaffoldScaffold( 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());
        },
      ),
    );
  }
}

演示顯示 state 未保留

這是可以被任何其他有狀態小部件替換的計數器:


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++;
          });
        },
      ),
    );
  }
}

首先,好問題! 訣竅是使用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);
    }
  }
}

您應該能夠修改此代碼以添加更多選項卡、功能等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM