【问题标题】:Flutter - Page with SingleTickerProviderStateMixin cause unnecessary buildFlutter - 带有 SingleTickerProviderStateMixin 的页面导致不必要的构建
【发布时间】:2021-06-24 19:38:34
【问题描述】:

我遇到了这个问题github link. 如果一个小部件使用 TickerProviderStateMixin,那么当页面导航发生时它会被重建。我有一个非常复杂的页面,重建整个页面会导致页面导航出现 UI 卡顿。如果我不重建,那么一切都很好,没有卡顿。有解决方法吗?在我看来,这是某种内部颤振错误或意外行为?

例子:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: PageA(title: 'Flutter Demo Home Page'),
    );
  }
}

class PageA extends StatefulWidget {
  PageA({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _PageAState createState() => _PageAState();
}

class _PageAState extends State<PageA>
    with SingleTickerProviderStateMixin {

  TabController tabController;

  @override
  void initState() {
    super.initState();
    tabController = TabController(length: 2, vsync: this);
  }

  void toPageB() {
    //tabController.animateTo(1);
    Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
      return PageB();
    }));
  }

  @override
  Widget build(BuildContext context) {
    print("Page A");
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: TabBar(
          tabs: [
            Text(
              "Tab A",
              style: Theme.of(context).textTheme.bodyText1,
            ),
            Text(
              "Tab B",
              style: Theme.of(context).textTheme.bodyText1,
            )
          ],
          controller: tabController,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: toPageB,
        child: Icon(Icons.add),
      ),
    );
  }
}

class PageB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("Page B");
    return Scaffold(
        appBar: AppBar(
          title: Text("Page A"),
        ),
        body: Container(
          child: Center(
            child: Text("Page A"),
          ),
        ));
  }
}

【问题讨论】:

  • 即使你不使用TickerProviderStateMixin,重建也可能发生,这只是 Flutter 的做事方式。你无能为力。
  • 好吧,我想我只是对颤振无法保持我的树结构感到失望。我有大约 400 个容器和一些动画对象。
  • 400 个容器在列表、列或其他地方?
  • 在页面视图内
  • 好的,那么你是在使用PageView.builder 还是其他构造函数?

标签: flutter


【解决方案1】:
@override
  // ignore: must_call_super
  void didChangeDependencies() {}

只需添加代码以防止重建,我不知道副作用,但这个走动适用于我的应用程序。

【讨论】:

    【解决方案2】:

    这是我之前使用的解决方案。

    更改您的 Page A 赞

    class PageA extends StatefulWidget {
      PageA({Key key, this.title}) : super(key: key);
      final String title;
    
      @override
      _PageAState createState() => _PageAState();
    }
    
    class _PageAState extends State<PageA> {
      TabController tabController;
    
      // @override
      // void initState() {
      //   super.initState();
      //   tabController = TabController(length: 2, vsync: this);
      // }
    
      void toPageB() {
        tabController.animateTo(1);
        Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
          return PageB();
        }));
      }
    
      @override
      Widget build(BuildContext context) {
        print("Page A");
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: CustomTabBar(
              tabs: [
                Text(
                  "Tab A",
                  style: Theme.of(context).textTheme.bodyText1,
                ),
                Text(
                  "Tab B",
                  style: Theme.of(context).textTheme.bodyText1,
                )
              ],
              controller: (controller) {
                tabController = controller;
              },
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: toPageB,
            child: Icon(Icons.add),
          ),
        );
      }
    }
    

    并添加一个新类CustomTabBar

    class CustomTabBar extends StatefulWidget {
      CustomTabBar({
        Key key,
        this.controller,
        this.tabs,
      }) : super(key: key);
      final Function(TabController) controller;
      final List<Widget> tabs;
    
      @override
      _CustomTabBarState createState() => _CustomTabBarState();
    }
    
    class _CustomTabBarState extends State<CustomTabBar>
        with SingleTickerProviderStateMixin {
      TabController tabController;
      @override
      void initState() {
        super.initState();
        tabController = TabController(length: widget?.tabs?.length ?? 0, vsync: this);
        if (widget.controller != null) {
          widget.controller(tabController);
        }
      }
    
      @override
      void dispose() {
        tabController.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return TabBar(
          tabs: widget?.tabs ?? [],
          controller: tabController,
        );
      }
    }
    

    应该可以解决页面A重建的问题

    【讨论】:

    • 非常感谢您分享这个。我不明白,这对重建有什么帮助?您能否提供更多背景信息?
    • 首先需要保留动画,所以需要SingleTickerProviderStateMixin。正如我们所知,SingleTickerProviderStateMixin 将调用 UI 进行更新。因此,创建一个子 CustomTabBar 并在播放动画时使 CustomTabBar 内的所有 UI 发生变化。你可以看到只有CustomTabBar使用SingleTickerProviderStateMixin,当它在initState时它会将tabController传递给页面A。当页面A调用tabController来播放动画时,它实际上是让孩子CustomTabBar重建
    • 这限制了在 PageA 中使用 AnimationController 的能力
    • 除非你在页面A中有其他动画小部件,但解决方法是一样的。使动画小部件成为一个新的自定义 starefulwidget 并在其 initstate 中将控制器传递回页面 A
    • 所以基本上你的建议是我有一个带有 TickerProviderStateMixin 的小部件并创建多个动画控制器并将它们传递给子子小部件?抱歉,我很难理解您的解决方案:(
    猜你喜欢
    • 2016-02-25
    • 2021-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多