Flutter打开键盘触发跨页面组件重建致掉帧的解决方法咨询
解决Flutter键盘唤起导致后台页面组件重建掉帧问题
问题背景
打开PageThree的输入框键盘时,后台的PageTwo、Home页面组件会被触发重建,进而引发掉帧。已知键盘唤起会改变窗口尺寸,依赖MediaQuery的组件(如Scaffold、TabBar、ListView)都会因此重建,且已设置resizeToAvoidBottomInset: false,需要找到避免后台页面不必要重建的办法。
方案1:用AutomaticKeepAliveClientMixin缓存页面状态
Navigator栈里的后台页面默认会被标记为需重建,通过混入AutomaticKeepAliveClientMixin可以让页面保持状态,跳过不必要的重建。
修改PageTwo的State代码:
class _PageTwoState extends State<PageTwo> with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { late TabController _tabController; // 开启状态保持 @override bool get wantKeepAlive => true; @override void initState() { super.initState(); _tabController = TabController(length: 3, vsync: this); } @override Widget build(BuildContext context) { super.build(context); // 必须调用,否则KeepAlive不生效 return Scaffold( appBar: AppBar(title: Text('page2')), body: Column( children: [ TabBar( controller: _tabController, tabs: const [Tab(text: 'Tab 1'), Tab(text: 'Tab 2'), Tab(text: 'Tab 3')], ), ElevatedButton( onPressed: () { Navigator.push(context, MaterialPageRoute(builder: (context) => PageThree())); }, child: Text('page3')), Container( height: 200, child: ListView.builder( itemCount: 2000, itemExtent: 20, itemBuilder: (context, index) { return Container(child: Text('Item $index')); }, ), ) ], ), ); } }
方案2:阻断MediaQuery变化传递
键盘唤起导致的MediaQuery变化会沿着Widget树向下传递,后台页面如果不需要响应这个变化,可以用自定义MediaQuery包裹页面,阻断变化传递。
修改Home页面的代码:
class Home extends StatelessWidget { @override Widget build(BuildContext context) { // 保存当前的MediaQuery快照,后续不再响应变化 final fixedMediaQuery = MediaQuery.of(context); return MediaQuery( data: fixedMediaQuery, child: Scaffold( appBar: AppBar(title: Text('page1')), body: Column( children: [ ElevatedButton( onPressed: () { Navigator.push(context, MaterialPageRoute(builder: (context) => PageTwo())); }, child: Text('page2')), Text('This is page1') ], ), ), ); } }
同样的方法可以套用到PageTwo上,避免它因MediaQuery变化重建。
方案3:给页面设置唯一标识缓存状态
给每个页面设置PageStorageKey或者UniqueKey,让Navigator能识别页面并缓存其状态,防止重建。
示例:跳转时给页面设置key:
// Home中跳转PageTwo的代码修改 ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => PageTwo(key: const PageStorageKey('page_two')), ), ); }, child: Text('page2')),
或者在PageTwo组件定义时接收key:
class PageTwo extends StatefulWidget { const PageTwo({super.key}); @override State<PageTwo> createState() => _PageTwoState(); }
内容的提问来源于stack exchange,提问作者mrliu
相关产品推荐
相关产品推荐

