Flutter:TabBarView 移动到选项卡也会重建其他选项卡

标签 flutter dart flutter-layout

我使用 TabBarView 并且我注意到一些非常奇怪的事情。我在所有选项卡的小部件中放置了一些打印消息,以便在滑动选项卡时获得反馈。我得到了这些结果:

  • 从 0 -> 1: 选项卡 1 调用, 选项卡 3 调用, 选项卡 4 调用, 标签 0 已调用。
  • 从 1 -> 2: 选项卡 2 调用, 选项卡 2 调用, 选项卡 1 调用, 选项卡 3 调用, 选项卡 4 调用, Tab 2 已调用。
  • 从 2 -> 3: 选项卡 1 调用, 选项卡 3 调用, 选项卡 4 调用, Tab 2 已调用。
  • 从 3 -> 4: 选项卡 1 调用, 选项卡 3 调用, Tab 4 已调用。

真是奇怪的事情。它实际上重建(调用 setState)一个选项卡,即使它没有被选中。请记住,只有 Tab 0 和 Tab 2 内部有实际内容,其他的容器都是空的。

这是我的代码:

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

class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
  final AuthService _authService = AuthService();
  final colors = [
    Colors.red,
    Colors.lightGreen,
    Colors.blue,
    Colors.amber,
    Colors.deepPurpleAccent
  ];
  Color scaffoldColor = Colors.red;
  TabController _controller;

  @override
  void initState() {
    super.initState();
    _controller = TabController(vsync: this, length: 5);
    _controller.addListener(() {
      if (_controller.indexIsChanging) {
        print("index is changing");
      } else {
        setState(() {
          scaffoldColor = colors[_controller.index];
        });
      }
    });
    _controller.index = 1;
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 5,
      child: Scaffold(
        backgroundColor: scaffoldColor,
        appBar: AppBar(
          backgroundColor: scaffoldColor,
          leading: IconButton(
            icon: Icon(Icons.menu),
            color: Colors.white,
            onPressed: () {},
          ),
          elevation: 0,
          title: Text(
            'HOME Screen',
            style: TextStyle(fontSize: 26, fontWeight: FontWeight.bold),
          ),
          actions: [
            FlatButton.icon(
              onPressed: () async {
                await _authService.signOut();
              },
              icon: Icon(Icons.exit_to_app),
              label: Text('Log out'),
            ),
          ],
          bottom: TabBar(
            isScrollable: true,
            controller: _controller,
            indicatorWeight: 6,
            indicatorColor: Colors.transparent,

            tabs: <Widget>[
              Tab(
                child: Container(
                  child: Text(
                    'Chats',
                    style: TextStyle(
                        color:
                            _controller.index == 0 ? Colors.white : Colors.grey,
                        fontSize: 18),
                  ),
                ),
              ),
              Tab(
                child: Container(
                  child: Text(
                    'Online',
                    style: TextStyle(
                        color:
                            _controller.index == 1 ? Colors.white : Colors.grey,
                        fontSize: 18),
                  ),
                ),
              ),
              Tab(
                child: Container(
                  child: Text(
                    'Discover',
                    style: TextStyle(
                        color:
                            _controller.index == 2 ? Colors.white : Colors.grey,
                        fontSize: 18),
                  ),
                ),
              ),
              Tab(
                child: Container(
                  child: Text(
                    'NULL',
                    style: TextStyle(
                        color:
                            _controller.index == 3 ? Colors.white : Colors.grey,
                        fontSize: 18),
                  ),
                ),
              ),
              Tab(
                child: Container(
                  child: Text(
                    'NULL2',
                    style: TextStyle(
                        color:
                            _controller.index == 4 ? Colors.white : Colors.grey,
                        fontSize: 18),
                  ),
                ),
              ),
            ],
          ),
        ),
        body: TabBarView(
          controller: _controller,
          children: <Widget>[
            Column(
              children: <Widget>[
                //CategorySelector(),
                Expanded(
                  child: Container(
                    decoration: BoxDecoration(
                      color: Theme.of(context).accentColor,
                      borderRadius: BorderRadius.only(
                        topLeft: Radius.circular(30),
                        topRight: Radius.circular(30),
                      ),
                    ),
                    child: Column(
                      children: <Widget>[
                        FavoriteContacts4(),
                        RecentChats(),
                      ],
                    ),
                  ),
                ),
              ],
            ),
            Tab1(),
            Column(
              children: <Widget>[
                //CategorySelector(),
                Expanded(
                  child: Container(
                    decoration: BoxDecoration(
                      color: Theme.of(context).accentColor,
                      borderRadius: BorderRadius.only(
                        topLeft: Radius.circular(30),
                        topRight: Radius.circular(30),
                      ),
                    ),
                    child: SuggestedUsers(),
                  ),
                ),
              ],
            ),
            Tab3(),
            Tab4(),
          ],
        ),
      ),
    );
  }

  Widget tmpWidget(int i) {
    print("Tab " + i.toString() + " is called");
    return Text("Number" + i.toString());
  }
}

这真的很烦人,因为每次我切换(或几乎)切换到另一个选项卡时,它都会调用选项卡 2 中的 api。我认为这与 Controller 有关!

更新: 即使我已将 AutomaticKeepAliveClientMixin 放入我的每个 Statefull 选项卡小部件中,也将进行重建。例如,当我从 Tab3 滑动到 Tab4 时,所有选项卡都将被重建。 Tab1,2,3,4 全部。

这是我为选项卡 1,3 和 4 编写的代码。选项卡 0 和 2 有其他小部件,但作用相同。

class Tab1 extends StatefulWidget {
  @override
  _Tab1State createState() => _Tab1State();
}

class _Tab1State extends State<Tab1> with AutomaticKeepAliveClientMixin {
  @override
  Widget build(BuildContext context) {
    super.build(context);
    print("Tab 1 Has been built");
    return Text("TAB 1");
  }

  @override
  // TODO: implement wantKeepAlive
  bool get wantKeepAlive => true;
}

最佳答案

那是因为你正在调用 setState({})这意味着你的 build方法将被调用,因为你所有的 tabs都在同一个build方法所有这些都将被重新创建。为了解决这个问题,您必须:

  1. 创建 StatefulWidget对于 TabBarView 的每个 child
  2. 让你的StatefulWidget延伸AutomaticKeepAliveClientMixin ( class ExampleState extends State<Example> with AutomaticKeepAliveClientMixin
  3. 你必须覆盖 wantKeepAlive : @override bool get wantKeepAlive => true;
  4. 调用super.build(context)作为你的第一行 build方法

这将防止每次都重新构建您的小部件

关于Flutter:TabBarView 移动到选项卡也会重建其他选项卡,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64733635/

相关文章:

flutter - 更改 Flutter 桌面应用程序窗口大小

dart - Flutter Stepper - 显示所有内容

flutter :How to build a infinite list using future builder that display n-record at a time

flutter - 如何在子小部件中禁用底部的SafeArea或更改其颜色?

google-maps - flutter 谷歌地图,如何在折线中制作渐变?

flutter - 如何在flutter中实现VerticalMultiDragGestureRecognizer从下到上的手势?

构建方法中的 Flutter Riverpod context.read vs Provider

dart - Flutter - 重写 CupertinoTabBar 的 "build"方法

flutter - 在 flutter 中为 slider 使用离线图像

Flutter tabsView 和 NestedScrollView 滚动问题