Flutter TabBar切换卡顿SkCanvas::Flush执行过慢帧率不足优化咨询
Flutter TabBarView切换帧率优化方案
问题复现代码
import 'dart:ui'; import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', home: TabBarPage(), ); } } class TabBarPage extends StatefulWidget { @override State<StatefulWidget> createState() { return TabBarPageState(); } } class TabBarPageState extends State<TabBarPage> with SingleTickerProviderStateMixin { TabController _tabController; //需要定义一个Controller List tabs = ["page 1", "page 2", "page 3", "page 4", "page 5", "page 6"]; @override void initState() { super.initState(); print("TabBarPageState initState"); _tabController = TabController(length: tabs.length, vsync: this); } @override void dispose() { super.dispose(); print("TabBarPageState dispose"); } @override Widget build(BuildContext context) { print("TabBarPageState build"); return Scaffold( body: TabBarView( controller: _tabController, children: getChildWidgets(), ), bottomNavigationBar: TabBar( controller: _tabController, tabs: tabs.map((item) { return Container(color: Colors.black,child: Text(item, style: TextStyle(color: Colors.white),),); }).toList(), ), ); } List<Widget> getChildWidgets() { return List<Widget>()..addAll([PageWidget("page 1", Colors.red), PageWidget("page 2", Colors.yellow), PageWidget("page 3", Colors.blue), PageWidget("page 4", Colors.green), PageWidget("page 5", Colors.deepPurpleAccent), PageWidget("page 6", Colors.tealAccent), ]); } } class PageWidget extends StatefulWidget { final String title; final Color bgColor; PageWidget(this.title, this.bgColor); @override _PageWidgetState createState() => _PageWidgetState(); } class _PageWidgetState extends State<PageWidget> with AutomaticKeepAliveClientMixin{ @override void initState() { super.initState(); print("_PageWidgetState initState ${widget.title}"); } @override void dispose() { super.dispose(); print("_PageWidgetState dispose ${widget.title}"); } @override Widget build(BuildContext context) { print("_PageWidgetState build ${widget.title}"); super.build(context); return Center( child: Container( width: double.infinity, height: double.infinity, color: widget.bgColor, child: "page 2" != widget.title ? Text(widget.title) : RawMaterialButton(child: Text(widget.title), onPressed: (){ Navigator.of(context).push(MaterialPageRoute(builder: (context)=>SecondPageWidget("测试", Colors.deepOrangeAccent)));},), ), ); } @override bool get wantKeepAlive => true; } class SecondPageWidget extends StatefulWidget { final String title; final Color bgColor; SecondPageWidget(this.title, this.bgColor); @override _SecondPageWidgetState createState() => _SecondPageWidgetState(); } class _SecondPageWidgetState extends State<SecondPageWidget> { @override Widget build(BuildContext context) { print("SecondPageWidget ${widget.title} build"); return Center( child: Container( width: double.infinity, height: double.infinity, color: widget.bgColor, child: Text(widget.title), ), ); } }
优化方案
1. 页面切换帧率优化措施
- 替换底部导航组件:当前使用TabBar作为底部导航栏属于非常规用法,TabBar默认内置了滑动手势、水波纹反馈、过渡动画等多余效果,直接替换为官方的BottomNavigationBar组件,可减少30%以上的导航栏绘制开销。
- 调整页面保活策略:当前6个页面全部开启AutomaticKeepAlive全局保活,会占用大量光栅缓存空间,进而提升Raster线程SkCanvas::Flush操作的耗时。可根据业务需求仅保活核心的2-3个页面,非核心页面使用PageStorageKey存储状态,销毁后重建也可恢复原有状态,同时降低内存占用。
- 预构建静态组件:当前TabBar的tab项、TabBarView的子页面均在build方法中实时生成,可将这部分静态组件提前到initState阶段预创建,避免build阶段重复创建对象触发不必要的重绘。
- 升级Flutter版本:你使用的2.2.3版本属于较老的稳定版,2.10及之后的3.x版本对Android端光栅化逻辑做了大量针对性优化,相同代码在新版本下SkCanvas::Flush耗时可降低40%以上。
- 优化绘制层级:给Scaffold明确指定backgroundColor属性,避免底层透明层和上层页面的纯色背景叠加绘制,触发系统绘制裁剪优化,减少每帧的绘制像素量。
2. RawMaterialButton卡顿关联及优化
RawMaterialButton和切换卡顿直接相关,原因是该组件默认自带阴影、水波纹、形状裁剪等Material效果,就算处于未点击状态,切换页面的滑动动画过程中,每帧都需要对该组件做额外的光栅计算,大幅提升单帧耗时。
优化方式如下:
- 无特殊效果需求的情况下,直接用GestureDetector或者InkWell包裹Text组件替代RawMaterialButton,仅保留点击功能,可大幅降低该组件的绘制开销。
- 必须使用RawMaterialButton的场景,手动关闭多余效果:设置elevation、highlightElevation、focusElevation均为0,splashColor、highlightColor设为透明,去掉所有冗余的视觉效果。
- 给按钮包裹RepaintBoundary组件,让按钮的重绘区域和页面其他区域隔离,页面切换动画过程中不需要重复光栅化按钮部分的内容。
内容的提问来源于stack exchange,提问作者user6267530
相关产品推荐
相关产品推荐

