重现步骤
- 运行代码示例
- 点击
pageBuilder
按钮 - 点击
transitionsBuilder
按钮
预期结果
pageBuilder
和 transitionsBuilder
应该各执行一次。
实际结果
pageBuilder
执行了1次,而 transitionsBuilder
执行了38次。
代码示例
代码示例
import 'package:flutter/material.dart';
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State createState() => _HomeScreenState();
}
class _HomeScreenState extends State {
var pageBuilderCount = 0;
var transitionsBuilderCount = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home Screen'),
),
body: Center(
child: Column(
children: [
const Spacer(flex: 3),
Text('$pageBuilderCount builds'),
ElevatedButton(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.blue),
foregroundColor: const WidgetStatePropertyAll(Colors.white),
),
onPressed: () {
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) {
++pageBuilderCount;
WidgetsBinding.instance.addPostFrameCallback(() => setState(() {}));
return FadeTransition(
opacity: animation,
child: Scaffold(
backgroundColor: Colors.blue,
appBar: AppBar(
title: const Text('pageBuilder'),
),
body: const SizedBox(),
),
);
},
),
);
},
child: const Text('pageBuilder'),
),
const Spacer(),
Text('$transitionsBuilderCount builds'),
ElevatedButton(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.red),
foregroundColor: const WidgetStatePropertyAll(Colors.white),
),
onPressed: () {
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) {
return Scaffold(
backgroundColor: Colors.red,
appBar: AppBar(
title: const Text('transitionsBuilder'),
),
body: const SizedBox(),
);
},
transitionsBuilder: (context, animation, secondaryAnimation, child) {
++transitionsBuilderCount;
WidgetsBinding.instance.addPostFrameCallback(() => setState(() {}));
return FadeTransition(
opacity: animation,
child: child,
);
},
),
);
},
child: const Text('transitionsBuilder'),
),
const Spacer(flex: 3),
],
),
),
);
}
}
截图或视频
截图/视频演示screen_recording.mp4
日志
- 无响应*
Flutter Doctor输出
Doctor输出
[✓] Flutter (Channel stable, 3.22.0, on macOS 14.5 23F79 darwin-arm64, locale en-US)
• Flutter version 3.22.0 on channel stable at /opt/homebrew/Caskroom/flutter/3.22.0/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 5dcb86f68f (13 days ago), 2024-05-09 07:39:20 -0500
• Engine revision f6344b75dc
• Dart version 3.4.0
• DevTools version 2.34.3
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
• Android SDK at /Users/user/Library/Android/sdk
• Platform android-34, build-tools 34.0.0
• Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 17.0.10+0-17.0.10b1087.21-11572160)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 15.4)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 15F31d
• CocoaPods version 1.15.2
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2023.3)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 17.0.10+0-17.0.10b1087.21-11572160)
[✓] VS Code (version 1.89.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.88.0
[✓] Connected device (5 available)
• sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64 • Android 14 (API 34) (emulator)
• iPhone 15 Pro Max (mobile) • 9CF6C1A2-E96A-4D3D-B878-1392E4F72A14 • ios • com.apple.CoreSimulator.SimRuntime.iOS-17-5 (simulator)
• macOS (desktop) • macos • darwin-arm64 • macOS 14.5 23F79 darwin-arm64
• Mac Designed for iPad (desktop) • mac-designed-for-ipad • darwin • macOS 14.5 23F79 darwin-arm64
• Chrome (web) • chrome • web-javascript • Google Chrome 124.0.6367.210
[✓] Network resources
• All expected network resources are available.
• No issues found!
3条答案
按热度按时间vhipe2zx1#
ModalRoute
构建了一个_ModalScope
,它构建了一个ListenableBuilder
,该ListenableBuilder
调用了buildTransitions
。来源:
ListenableBuilder
监听animation
和secondaryAnimation
的合并。来源:
这意味着
ListenableBuilder
在过渡的每一帧上都会调用buildTransitions
。文档中说“在过渡期间,当路由可见时,每次都会调用buildTransitions
”,但没有提到它是对每个过渡帧都有条件地调用。来源:
它也没有提到使用
buildTransitions
而不是buildPage
时性能的影响,尽管它建议用户将他们的 transitionwidgets 放在那里。当 Material widget 层通过MaterialRouteTransitionMixin
将MaterialPageRoute
与应用程序主题绑定时,会遵循这个建议。来源:
这样做会导致
MaterialApp
中的每个页面过渡都每帧产生额外的Theme.of(context)
成本。哈希表查找和部件构建是廉价的,但是框架为什么要承担这些成本呢?尤其是在过渡期间,它必须尽快生成帧的时候?如果有原因的话,也许框架可以记录这种行为并建议开发者将他们的过渡部件放在
buildPage
中,只要这些部件在过渡期间不会改变。另一方面,这种行为可能是一个 bug 吗?wmtdaxz32#
感谢您的报告。已复制上述结果。
运行flutter doctor -v (stable和master版本)
wooyq4lh3#
在
Navigator
文档中提到了每帧重建过渡小部件:来源:
如果这是有意为之,我很好奇原因。过渡小部件不需要重建,因为它们监听动画并自动重建自己。
支持不继承自
AnimatedWidget
的小部件的想法是?这样的小部件可以在ModalRoute.buildTransitions
的每一帧内根据animation
和secondaryAnimation
的值重新配置自己。确实,_ModalScopeState
监听动画并提供隐式的AnimatedBuilder
。