Flutter:如何避免唤起键盘时MaterialApp.router重复调用builder?
我未能找到该问题的解决方案,目前类似场景多与使用MediaQuery的组件重建相关,但本次情况并非如此。点击TextField唤起键盘后,MaterialApp.router内的builder会被调用数十次,请问能否避免这种频繁调用?
复现代码(main.dart)
import 'package:flutter/material.dart'; import 'package:routemaster/routemaster.dart'; final routes = RouteMap( onUnknownRoute: (route) => const Redirect("/"), routes: { '/': (_) => const MaterialPage(child: FirstPage()), }, ); void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp.router( builder: (context, widget) { debugPrint("Info: MaterialApp.router Build"); return widget!; }, routeInformationParser: const RoutemasterParser(), routerDelegate: RoutemasterDelegate( routesBuilder: (context) { debugPrint("Info: Router Build"); return routes; }, ), ); } } class FirstPage extends StatelessWidget { const FirstPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { debugPrint("Info: FirstPage Build"); return Scaffold( body: Column( children: const [ Padding( padding: EdgeInsets.only(top: 200), child: TextField(), ), ], ), ); } }
控制台输出
Launching lib/main.dart on iPhone 13-2 in debug mode... Running Xcode build... Xcode build done. 14,4s Debug service listening on ws://127.0.0.1:62493/BzKbBCNai2Q=/ws Syncing files to device iPhone 13-2... flutter: Info: MaterialApp.router Build flutter: Info: Router Build flutter: Info: FirstPage Build flutter: Info: MaterialApp.router Build // opening keyboard flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build flutter: Info: MaterialApp.router Build
解决方案
1. 理解频繁调用的原因
MaterialApp.router的builder会在媒体查询参数变化时被反复调用——键盘弹出/收起时,系统会更新MediaQuery的viewInsets、padding等参数,而MaterialApp内部会监听这些变化以维护正确的上下文环境,因此会触发builder调用。这种行为本身是Flutter的正常机制,若builder仅做简单的Widget转发(如示例中仅返回widget!),其实不会对性能造成明显影响。
2. 避免不必要的重建(针对有耗时操作的场景)
如果你的builder中包含耗时逻辑(如复杂Widget创建、状态计算),可以通过以下方式缓存结果,减少重复执行:
方法一:将builder逻辑封装为独立的缓存Widget
创建一个StatefulWidget来缓存需要保留的内容,仅在必要时更新:
class CachedBuilder extends StatefulWidget { final Widget child; const CachedBuilder({super.key, required this.child}); @override State<CachedBuilder> createState() => _CachedBuilderState(); } class _CachedBuilderState extends State<CachedBuilder> { late Widget _cachedChild; @override void initState() { super.initState(); // 初始化时缓存子Widget _cachedChild = widget.child; } @override Widget build(BuildContext context) { debugPrint("Info: CachedBuilder Build"); return _cachedChild; } }
然后修改MaterialApp.router的builder:
builder: (context, widget) { return CachedBuilder(child: widget!); },
方法二:使用const构造或Key缓存子Widget
如果子Widget支持const构造(如示例中的FirstPage),确保使用const修饰,这样即使MaterialApp重建,子Widget也不会被重新创建。此外,给关键Widget添加固定的Key也能帮助Flutter识别Widget身份,避免不必要的重建。
3. 无需处理的情况
如果你的builder仅做简单的Widget传递(如示例代码),其实不需要刻意避免这些调用——Flutter的build过程经过优化,轻量的builder调用不会影响应用性能。
内容的提问来源于stack exchange,提问作者Xx Zz

