从代码调试 Flutter 应用
本指南介绍你可以在代码中启用哪些调试功能。有关调试和性能分析工具的完整列表,请查阅调试页面。
为你的应用添加日志
#以下列表包含一些可用于记录应用程序行为的语句。你可以在 DevTools 的日志视图或系统控制台中查看日志。
print()
: 打印一个stdout
(标准输出)消息。是dart:io
库的一部分。stderr.method_to_invoke()
: 打印一个stderr
(标准错误)消息。将method_to_invoke()
替换为stderr
属性支持的方法,例如writeln()
或write()
。通常在try...catch
块中使用。是dart:io
库的一部分。dartstderr.writeln('print me');
log()
: 在日志输出中包含更大的粒度和更多信息。是dart:developer
库的一部分。debugPrint()
: 如果输出过多导致日志行被丢弃,请使用此函数保留这些行。它会在发布模式下打印消息,除非它是调试模式检查或断言的一部分。是foundations
库的一部分。
示例 1
#import 'dart:developer' as developer;
void main() {
developer.log('log me', name: 'my.app.category');
developer.log('log me 1', name: 'my.other.category');
developer.log('log me 2', name: 'my.other.category');
}
你也可以将应用数据传递给日志调用。惯例是在 log()
调用上使用 error:
命名参数,将要发送的对象进行 JSON 编码,然后将编码后的字符串传递给 error 参数。
示例 2
#import 'dart:convert';
import 'dart:developer' as developer;
void main() {
var myCustomObject = MyCustomObject();
developer.log(
'log me',
name: 'my.app.category',
error: jsonEncode(myCustomObject),
);
}
DevTool 的日志视图将 JSON 编码的错误参数解释为数据对象。DevTool 在该日志条目的详细信息视图中进行渲染。
设置断点
#你可以在 DevTools 的调试器或 IDE 内置的调试器中设置断点。
设置程序化断点
将
dart:developer
包导入到相关文件。使用
debugger()
语句插入程序化断点。此语句接受一个可选的when
参数。当给定条件解析为 true 时,此布尔参数将设置一个断点。示例 3 对此进行了说明。
示例 3
#import 'dart:developer';
void someFunction(double offset) {
debugger(when: offset > 30);
// ...
}
使用标志调试应用层
#Flutter 框架的每一层都提供一个函数,使用 debugPrint
属性将其当前状态或事件转储到控制台。
打印 widget 树
#要转储 Widgets 库的状态,请调用 debugDumpApp()
函数。
- 打开你的源文件。
- 导入
package:flutter/rendering.dart
。 - 在
runApp()
函数中调用debugDumpApp()
函数。你的应用需要处于调试模式。当应用正在构建时,你不能在build()
方法内部调用此函数。 - 如果你尚未启动应用,请使用 IDE 调试它。
- 如果你的应用已启动,请保存源文件。热重载会重新渲染你的应用。
示例 4: 调用 debugDumpApp()
#import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: AppHome()));
}
class AppHome extends StatelessWidget {
const AppHome({super.key});
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: TextButton(
onPressed: () {
debugDumpApp();
},
child: const Text('Dump Widget Tree'),
),
),
);
}
}
此函数从 widget 树的根开始,递归调用 toStringDeep()
方法。它返回一个“扁平化”的树。
示例 4 生成以下 widget 树。它包括:
所有通过各种构建函数投影的 widget。
许多未出现在你的应用源代码中的 widget。框架的 widget 构建函数在构建期间插入它们。
例如,下面的树显示了
_InkFeatures
。该类实现了Material
widget 的一部分。它并未出现在示例 4 的代码中的任何地方。
展开以查看示例 4 的 widget 树
flutter: WidgetsFlutterBinding - DEBUG MODE
flutter: [root](renderObject: RenderView#06beb)
flutter: └View-[GlobalObjectKey FlutterView#7971c]
flutter: └_ViewScope
flutter: └_MediaQueryFromView(state: _MediaQueryFromViewState#d790c)
flutter: └MediaQuery(MediaQueryData(size: Size(800.0, 600.0), devicePixelRatio: 1.0, textScaleFactor: 1.0, platformBrightness: Brightness.dark, padding: EdgeInsets.zero, viewPadding: EdgeInsets.zero, viewInsets: EdgeInsets.zero, systemGestureInsets: EdgeInsets.zero, alwaysUse24HourFormat: false, accessibleNavigation: false, highContrast: false, disableAnimations: false, invertColors: false, boldText: false, navigationMode: traditional, gestureSettings: DeviceGestureSettings(touchSlop: null), displayFeatures: []))
flutter: └MaterialApp(state: _MaterialAppState#27fa9)
flutter: └ScrollConfiguration(behavior: MaterialScrollBehavior)
flutter: └HeroControllerScope
flutter: └Focus(state: _FocusState#d7f97)
flutter: └_FocusInheritedScope
flutter: └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#a6464)
flutter: └WidgetsApp-[GlobalObjectKey _MaterialAppState#27fa9](state: _WidgetsAppState#b5b17)
flutter: └RootRestorationScope(state: _RootRestorationScopeState#6b028)
flutter: └UnmanagedRestorationScope
flutter: └RestorationScope(dependencies: [UnmanagedRestorationScope], state: _RestorationScopeState#d1369)
flutter: └UnmanagedRestorationScope
flutter: └SharedAppData(state: _SharedAppDataState#95e82)
flutter: └_SharedAppModel
flutter: └Shortcuts(shortcuts: <Default WidgetsApp Shortcuts>, state: _ShortcutsState#272dc)
flutter: └Focus(debugLabel: "Shortcuts", dependencies: [_FocusInheritedScope], state: _FocusState#a3300)
flutter: └_FocusInheritedScope
flutter: └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#db110)
flutter: └DefaultTextEditingShortcuts
flutter: └Shortcuts(shortcuts: <Default Text Editing Shortcuts>, state: _ShortcutsState#1d796)
flutter: └Focus(debugLabel: "Shortcuts", dependencies: [_FocusInheritedScope], state: _FocusState#0081b)
flutter: └_FocusInheritedScope
flutter: └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#0d70e)
flutter: └Shortcuts(shortcuts: <Web Disabling Text Editing Shortcuts>, state: _ShortcutsState#56bac)
flutter: └Focus(debugLabel: "Shortcuts", dependencies: [_FocusInheritedScope], state: _FocusState#3152e)
flutter: └_FocusInheritedScope
flutter: └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#b7eaf)
flutter: └Actions(dispatcher: null, actions: {DoNothingIntent: DoNothingAction#0fda1, DoNothingAndStopPropagationIntent: DoNothingAction#17f30, RequestFocusIntent: RequestFocusAction#10bd0, NextFocusIntent: NextFocusAction#60317, PreviousFocusIntent: PreviousFocusAction#2a933, DirectionalFocusIntent: DirectionalFocusAction#a6922, ScrollIntent: _OverridableContextAction<ScrollIntent>#964fe(defaultAction: ScrollAction#ffb50), PrioritizedIntents: PrioritizedAction#be0e2, VoidCallbackIntent: VoidCallbackAction#805fa}, state: _ActionsState#bbd25)
flutter: └_ActionsScope
flutter: └FocusTraversalGroup(policy: ReadingOrderTraversalPolicy#f1e76, state: _FocusTraversalGroupState#0c200)
flutter: └Focus(debugLabel: "FocusTraversalGroup", focusNode: _FocusTraversalGroupNode#ffcad(FocusTraversalGroup [IN FOCUS PATH]), dependencies: [_FocusInheritedScope], state: _FocusState#c7dc2)
flutter: └_FocusInheritedScope
flutter: └TapRegionSurface(renderObject: RenderTapRegionSurface#17aba)
flutter: └ShortcutRegistrar(state: _ShortcutRegistrarState#44954)
flutter: └_ShortcutRegistrarScope
flutter: └Shortcuts(manager: ShortcutManager#eb38c(shortcuts: {}), shortcuts: {}, state: _ShortcutsState#f85ac)
flutter: └Focus(debugLabel: "Shortcuts", dependencies: [_FocusInheritedScope], state: _FocusState#8c1a7)
flutter: └_FocusInheritedScope
flutter: └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#1fc98)
flutter: └Localizations(locale: en_US, delegates: [DefaultMaterialLocalizations.delegate(en_US), DefaultCupertinoLocalizations.delegate(en_US), DefaultWidgetsLocalizations.delegate(en_US)], state: _LocalizationsState#ae3a0)
flutter: └Semantics(container: false, properties: SemanticsProperties, tooltip: null, textDirection: ltr, renderObject: RenderSemanticsAnnotations#8776e)
flutter: └_LocalizationsScope-[GlobalKey#61ca6]
flutter: └Directionality(textDirection: ltr)
flutter: └Title(color: Color(0xff2196f3))
flutter: └CheckedModeBanner("DEBUG")
flutter: └Banner("DEBUG", textDirection: ltr, location: topEnd, Color(0xa0b71c1c), text inherit: true, text color: Color(0xffffffff), text size: 10.2, text weight: 900, text height: 1.0x, dependencies: [Directionality])
flutter: └CustomPaint(renderObject: RenderCustomPaint#c014d)
flutter: └DefaultTextStyle(debugLabel: fallback style; consider putting your text in a Material, inherit: true, color: Color(0xd0ff0000), family: monospace, size: 48.0, weight: 900, decoration: double Color(0xffffff00) TextDecoration.underline, softWrap: wrapping at box width, overflow: clip)
flutter: └Builder(dependencies: [MediaQuery])
flutter: └ScaffoldMessenger(dependencies: [MediaQuery], state: ScaffoldMessengerState#5b36e)
flutter: └_ScaffoldMessengerScope
flutter: └DefaultSelectionStyle
flutter: └AnimatedTheme(duration: 200ms, state: _AnimatedThemeState#cd149(ticker inactive, ThemeDataTween(ThemeData#ef3b2 → ThemeData#ef3b2)))
flutter: └Theme(ThemeData#ef3b2, dependencies: [DefaultSelectionStyle])
flutter: └_InheritedTheme
flutter: └CupertinoTheme(brightness: light, primaryColor: MaterialColor(primary value: Color(0xff2196f3)), primaryContrastingColor: Color(0xffffffff), scaffoldBackgroundColor: Color(0xfffafafa), actionTextStyle: TextStyle(inherit: false, color: MaterialColor(primary value: Color(0xff2196f3)), family: .SF Pro Text, size: 17.0, letterSpacing: -0.4, decoration: TextDecoration.none), navActionTextStyle: TextStyle(inherit: false, color: MaterialColor(primary value: Color(0xff2196f3)), family: .SF Pro Text, size: 17.0, letterSpacing: -0.4, decoration: TextDecoration.none))
flutter: └_InheritedCupertinoTheme
flutter: └IconTheme(color: MaterialColor(primary value: Color(0xff2196f3)))
flutter: └IconTheme(color: Color(0xdd000000))
flutter: └DefaultSelectionStyle
flutter: └FocusScope(debugLabel: "Navigator Scope", AUTOFOCUS, dependencies: [_FocusInheritedScope], state: _FocusScopeState#acbd8)
flutter: └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#ab3f0)
flutter: └_FocusInheritedScope
flutter: └Navigator-[GlobalObjectKey<NavigatorState> _WidgetsAppState#b5b17](dependencies: [HeroControllerScope, UnmanagedRestorationScope], state: NavigatorState#1395a(tickers: tracking 1 ticker))
flutter: └HeroControllerScope
flutter: └Listener(listeners: [down, up, cancel], behavior: deferToChild, renderObject: RenderPointerListener#34172)
flutter: └AbsorbPointer(absorbing: false, renderObject: RenderAbsorbPointer#f8711)
flutter: └FocusTraversalGroup(policy: ReadingOrderTraversalPolicy#f1e76, state: _FocusTraversalGroupState#8d61a)
flutter: └Focus(debugLabel: "FocusTraversalGroup", focusNode: _FocusTraversalGroupNode#dd2b1(FocusTraversalGroup [IN FOCUS PATH]), dependencies: [_FocusInheritedScope], state: _FocusState#0bb03)
flutter: └_FocusInheritedScope
flutter: └Focus(debugLabel: "Navigator", AUTOFOCUS, focusNode: FocusNode#a3309(Navigator [IN FOCUS PATH]), dependencies: [_FocusInheritedScope], state: _FocusState#d3d07)
flutter: └_FocusInheritedScope
flutter: └UnmanagedRestorationScope
flutter: └Overlay-[LabeledGlobalKey<OverlayState>#5485a](state: OverlayState#5bd52(entries: [OverlayEntry#fc947(opaque: true; maintainState: false), OverlayEntry#05a32(opaque: false; maintainState: true)]))
flutter: └_Theater(skipCount: 0, dependencies: [Directionality], renderObject: _RenderTheater#e86c3)
flutter: ├_OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#1b37e](state: _OverlayEntryWidgetState#06ab0)
flutter: │└TickerMode(state: _TickerModeState#0b4ac(requested mode: enabled))
flutter: │ └_EffectiveTickerMode(effective mode: enabled)
flutter: │ └_RenderTheaterMarker
flutter: │ └IgnorePointer(ignoring: false, renderObject: RenderIgnorePointer#34c66)
flutter: │ └ModalBarrier
flutter: │ └BlockSemantics(blocking: true, renderObject: RenderBlockSemantics#97799)
flutter: │ └ExcludeSemantics(excluding: true, renderObject: RenderExcludeSemantics#8c4ce)
flutter: │ └_ModalBarrierGestureDetector
flutter: │ └RawGestureDetector(state: RawGestureDetectorState#556f6(gestures: [any tap], behavior: opaque))
flutter: │ └_GestureSemantics(renderObject: RenderSemanticsGestureHandler#616f1)
flutter: │ └Listener(listeners: [down, panZoomStart], behavior: opaque, renderObject: RenderPointerListener#c2b89)
flutter: │ └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#c3b31)
flutter: │ └MouseRegion(listeners: <none>, cursor: SystemMouseCursor(basic), renderObject: RenderMouseRegion#53cdb)
flutter: │ └ConstrainedBox(BoxConstraints(biggest), renderObject: RenderConstrainedBox#faa51)
flutter: └_OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#bc0aa](state: _OverlayEntryWidgetState#cbf35)
flutter: └TickerMode(state: _TickerModeState#23e73(requested mode: enabled))
flutter: └_EffectiveTickerMode(effective mode: enabled)
flutter: └_RenderTheaterMarker
flutter: └Semantics(container: false, properties: SemanticsProperties, tooltip: null, sortKey: OrdinalSortKey#135f4(order: 0.0), renderObject: RenderSemanticsAnnotations#5565e)
flutter: └_ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#4fe82](state: _ModalScopeState<dynamic>#4da7d)
flutter: └AnimatedBuilder(listenable: ValueNotifier<String?>#d87c6(null), state: _AnimatedState#dde81)
flutter: └RestorationScope(dependencies: [UnmanagedRestorationScope], state: _RestorationScopeState#78c51)
flutter: └UnmanagedRestorationScope
flutter: └_ModalScopeStatus(active)
flutter: └Offstage(offstage: false, renderObject: RenderOffstage#5e498)
flutter: └PageStorage
flutter: └Builder
flutter: └Actions(dispatcher: null, actions: {DismissIntent: _DismissModalAction#6279e}, state: _ActionsState#48019)
flutter: └_ActionsScope
flutter: └PrimaryScrollController(ScrollController#6a546(no clients))
flutter: └FocusScope(debugLabel: "_ModalScopeState<dynamic> Focus Scope", focusNode: FocusScopeNode#0e2af(_ModalScopeState<dynamic> Focus Scope [PRIMARY FOCUS]), dependencies: [_FocusInheritedScope], state: _FocusScopeState#0bac4)
flutter: └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#44b4e)
flutter: └_FocusInheritedScope
flutter: └RepaintBoundary(renderObject: RenderRepaintBoundary#38f41)
flutter: └AnimatedBuilder(listenable: Listenable.merge([AnimationController#9d623(⏭ 1.000; paused; for MaterialPageRoute<dynamic>(/))➩ProxyAnimation, kAlwaysDismissedAnimation➩ProxyAnimation➩ProxyAnimation]), dependencies: [_InheritedTheme, _LocalizationsScope-[GlobalKey#61ca6]], state: _AnimatedState#47725)
flutter: └CupertinoPageTransition(dependencies: [Directionality])
flutter: └SlideTransition(listenable: kAlwaysDismissedAnimation➩ProxyAnimation➩ProxyAnimation➩Cubic(0.35, 0.91, 0.33, 0.97)ₒₙ/Cubic(0.67, 0.03, 0.65, 0.09)➩Tween<Offset>(Offset(0.0, 0.0) → Offset(-0.3, 0.0))➩Offset(0.0, 0.0), state: _AnimatedState#b6162)
flutter: └FractionalTranslation(renderObject: RenderFractionalTranslation#fb461)
flutter: └SlideTransition(listenable: AnimationController#9d623(⏭ 1.000; paused; for MaterialPageRoute<dynamic>(/))➩ProxyAnimation➩ThreePointCubic ₒₙ/FlippedCurve(ThreePointCubic )➩Tween<Offset>(Offset(1.0, 0.0) → Offset(0.0, 0.0))➩Offset(0.0, 0.0), state: _AnimatedState#834bf)
flutter: └FractionalTranslation(renderObject: RenderFractionalTranslation#73ea4)
flutter: └DecoratedBoxTransition(listenable: AnimationController#9d623(⏭ 1.000; paused; for MaterialPageRoute<dynamic>(/))➩ProxyAnimation➩Cubic(0.35, 0.91, 0.33, 0.97)➩DecorationTween(_CupertinoEdgeShadowDecoration(colors: null) → _CupertinoEdgeShadowDecoration(colors: [Color(0x04000000), Color(0x00000000)]))➩_CupertinoEdgeShadowDecoration(colors: [Color(0x04000000), Color(0x00000000)]), state: _AnimatedState#a7fca)
flutter: └DecoratedBox(bg: _CupertinoEdgeShadowDecoration(colors: [Color(0x04000000), Color(0x00000000)]), dependencies: [Directionality, MediaQuery, _LocalizationsScope-[GlobalKey#61ca6]], renderObject: RenderDecoratedBox#9965c)
flutter: └_CupertinoBackGestureDetector<dynamic>(dependencies: [Directionality, MediaQuery], state: _CupertinoBackGestureDetectorState<dynamic>#ab8cd)
flutter: └Stack(alignment: AlignmentDirectional.topStart, fit: passthrough, dependencies: [Directionality], renderObject: RenderStack#b2b7c)
flutter: ├AnimatedBuilder(listenable: ValueNotifier<bool>#1a88e(false), state: _AnimatedState#6e33c)
flutter: │└IgnorePointer(ignoring: false, renderObject: RenderIgnorePointer#2b763)
flutter: │ └RepaintBoundary-[GlobalKey#628f4](renderObject: RenderRepaintBoundary#5a53b)
flutter: │ └Builder
flutter: │ └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#f8795)
flutter: │ └AppHome
flutter: │ └Material(type: canvas, dependencies: [_InheritedTheme, _LocalizationsScope-[GlobalKey#61ca6]], state: _MaterialState#7d183)
flutter: │ └AnimatedPhysicalModel(duration: 200ms, shape: rectangle, borderRadius: BorderRadius.zero, elevation: 0.0, color: Color(0xfffafafa), animateColor: false, shadowColor: Color(0xff000000), animateShadowColor: true, state: _AnimatedPhysicalModelState#d479e(ticker inactive))
flutter: │ └PhysicalModel(shape: rectangle, borderRadius: BorderRadius.zero, elevation: 0.0, color: Color(0xfffafafa), shadowColor: Color(0xff000000), renderObject: RenderPhysicalModel#c60b5)
flutter: │ └NotificationListener<LayoutChangedNotification>
flutter: │ └_InkFeatures-[GlobalKey#e9da0 ink renderer](renderObject: _RenderInkFeatures#d8e6d)
flutter: │ └AnimatedDefaultTextStyle(duration: 200ms, debugLabel: (englishLike bodyMedium 2014).merge(blackRedwoodCity bodyMedium), inherit: false, color: Color(0xdd000000), family: .AppleSystemUIFont, size: 14.0, weight: 400, baseline: alphabetic, decoration: TextDecoration.none, softWrap: wrapping at box width, overflow: clip, state: _AnimatedDefaultTextStyleState#12f43(ticker inactive))
flutter: │ └DefaultTextStyle(debugLabel: (englishLike bodyMedium 2014).merge(blackRedwoodCity bodyMedium), inherit: false, color: Color(0xdd000000), family: .AppleSystemUIFont, size: 14.0, weight: 400, baseline: alphabetic, decoration: TextDecoration.none, softWrap: wrapping at box width, overflow: clip)
flutter: │ └Center(alignment: Alignment.center, dependencies: [Directionality], renderObject: RenderPositionedBox#b088f)
flutter: │ └TextButton(dirty, dependencies: [MediaQuery, _InheritedTheme, _LocalizationsScope-[GlobalKey#61ca6]], state: _ButtonStyleState#687c9)
flutter: │ └Semantics(container: true, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#ca411 relayoutBoundary=up1)
flutter: │ └_InputPadding(renderObject: _RenderInputPadding#60ede relayoutBoundary=up2)
flutter: │ └ConstrainedBox(BoxConstraints(56.0<=w<=Infinity, 28.0<=h<=Infinity), renderObject: RenderConstrainedBox#34800 relayoutBoundary=up3)
flutter: │ └Material(type: button, color: Color(0x00000000), shadowColor: Color(0xff000000), textStyle.debugLabel: ((englishLike labelLarge 2014).merge(blackRedwoodCity labelLarge)).copyWith, textStyle.inherit: false, textStyle.color: MaterialColor(primary value: Color(0xff2196f3)), textStyle.family: .AppleSystemUIFont, textStyle.size: 14.0, textStyle.weight: 500, textStyle.baseline: alphabetic, textStyle.decoration: TextDecoration.none, shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.circular(4.0)), dependencies: [_InheritedTheme, _LocalizationsScope-[GlobalKey#61ca6]], state: _MaterialState#50a4d(tickers: tracking 5 tickers))
flutter: │ └_MaterialInterior(duration: 200ms, shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.circular(4.0)), elevation: 0.0, color: Color(0x00000000), shadowColor: Color(0xff000000), dependencies: [Directionality, _InheritedTheme, _LocalizationsScope-[GlobalKey#61ca6]], state: _MaterialInteriorState#d296d(ticker inactive))
flutter: │ └PhysicalShape(clipper: ShapeBorderClipper, elevation: 0.0, color: Color(0x00000000), shadowColor: Color(0xff000000), renderObject: RenderPhysicalShape#43df6 relayoutBoundary=up4)
flutter: │ └_ShapeBorderPaint(dependencies: [Directionality])
flutter: │ └CustomPaint(renderObject: RenderCustomPaint#c1a3c relayoutBoundary=up5)
flutter: │ └NotificationListener<LayoutChangedNotification>
flutter: │ └_InkFeatures-[GlobalKey#625bc ink renderer](renderObject: _RenderInkFeatures#54439 relayoutBoundary=up6)
flutter: │ └AnimatedDefaultTextStyle(duration: 200ms, debugLabel: ((englishLike labelLarge 2014).merge(blackRedwoodCity labelLarge)).copyWith, inherit: false, color: MaterialColor(primary value: Color(0xff2196f3)), family: .AppleSystemUIFont, size: 14.0, weight: 500, baseline: alphabetic, decoration: TextDecoration.none, softWrap: wrapping at box width, overflow: clip, state: _AnimatedDefaultTextStyleState#2f29d(ticker inactive))
flutter: │ └DefaultTextStyle(debugLabel: ((englishLike labelLarge 2014).merge(blackRedwoodCity labelLarge)).copyWith, inherit: false, color: MaterialColor(primary value: Color(0xff2196f3)), family: .AppleSystemUIFont, size: 14.0, weight: 500, baseline: alphabetic, decoration: TextDecoration.none, softWrap: wrapping at box width, overflow: clip)
flutter: │ └InkWell
flutter: │ └_InkResponseStateWidget(gestures: [tap], mouseCursor: ButtonStyleButton_MouseCursor, clipped to BoxShape.rectangle, dirty, dependencies: [Directionality, MediaQuery, _InheritedTheme, _LocalizationsScope-[GlobalKey#61ca6]], state: _InkResponseState#0b11d)
flutter: │ └_ParentInkResponseProvider
flutter: │ └Actions(dispatcher: null, actions: {ActivateIntent: CallbackAction<ActivateIntent>#018db, ButtonActivateIntent: CallbackAction<ButtonActivateIntent>#ef87a}, state: _ActionsState#a5eab)
flutter: │ └_ActionsScope
flutter: │ └Focus(dependencies: [_FocusInheritedScope], state: _FocusState#5a9de)
flutter: │ └_FocusInheritedScope
flutter: │ └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#8ac3e relayoutBoundary=up7)
flutter: │ └MouseRegion(listeners: [enter, exit], cursor: SystemMouseCursor(click), renderObject: RenderMouseRegion#13d4e relayoutBoundary=up8)
flutter: │ └Builder(dependencies: [DefaultSelectionStyle])
flutter: │ └DefaultSelectionStyle
flutter: │ └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#d99cc relayoutBoundary=up9)
flutter: │ └GestureDetector(startBehavior: start, dependencies: [MediaQuery])
flutter: │ └RawGestureDetector(state: RawGestureDetectorState#b8d93(gestures: [tap], excludeFromSemantics: true, behavior: opaque))
flutter: │ └Listener(listeners: [down, panZoomStart], behavior: opaque, renderObject: RenderPointerListener#a4c3b relayoutBoundary=up10)
flutter: │ └Builder(dependencies: [IconTheme])
flutter: │ └IconTheme(color: MaterialColor(primary value: Color(0xff2196f3)))
flutter: │ └Padding(padding: EdgeInsets(8.0, 0.0, 8.0, 0.0), dependencies: [Directionality], renderObject: RenderPadding#18a87 relayoutBoundary=up11)
flutter: │ └Align(alignment: Alignment.center, widthFactor: 1.0, heightFactor: 1.0, dependencies: [Directionality], renderObject: RenderPositionedBox#fb8a8 relayoutBoundary=up12)
flutter: │ └Text("Dump Widget Tree", dependencies: [DefaultSelectionStyle, DefaultTextStyle, MediaQuery])
flutter: │ └RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "Dump Widget Tree", dependencies: [Directionality, _LocalizationsScope-[GlobalKey#61ca6]], renderObject: RenderParagraph#d15aa relayoutBoundary=up13)
flutter: └PositionedDirectional(dependencies: [Directionality])
flutter: └Positioned(left: 0.0, top: 0.0, bottom: 0.0, width: 20.0)
flutter: └Listener(listeners: [down], behavior: translucent, renderObject: RenderPointerListener#d884c)
flutter:
flutter:
当按钮从按下状态变为释放状态时,这将调用 debugDumpApp()
函数。它还与 TextButton
对象调用 setState()
并因此标记自身为脏巧合。这解释了为什么 Flutter 将特定对象标记为“脏”。当你查看 widget 树时,寻找类似于以下内容的行:
└TextButton(dirty, dependencies: [MediaQuery, _InheritedTheme, _LocalizationsScope-[GlobalKey#5880d]], state: _ButtonStyleState#ab76e)
如果你编写自己的 widget,请重写 debugFillProperties()
方法以添加信息。将 DiagnosticsProperty 对象添加到方法的参数中,并调用超类方法。toString
方法使用此函数来填充 widget 的描述。
打印渲染树
#调试布局问题时,Widgets 层的树可能缺乏细节。下一级别的调试可能需要渲染树。要转储渲染树:
- 打开你的源文件。
- 调用
debugDumpRenderTree()
函数。你可以在任何时候调用此函数,除了在布局或绘制阶段。考虑从帧回调或事件处理程序中调用它。 - 如果你尚未启动应用,请使用 IDE 调试它。
- 如果你的应用已启动,请保存源文件。热重载会重新渲染你的应用。
示例 5: 调用 debugDumpRenderTree()
#import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: AppHome()));
}
class AppHome extends StatelessWidget {
const AppHome({super.key});
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: TextButton(
onPressed: () {
debugDumpRenderTree();
},
child: const Text('Dump Render Tree'),
),
),
);
}
}
调试布局问题时,请查看 size
和 constraints
字段。约束从树向下流动,而大小则向上流回。
展开以查看示例 5 的渲染树
flutter: RenderView#02c80
flutter: │ debug mode enabled - macos
flutter: │ view size: Size(800.0, 600.0) (in physical pixels)
flutter: │ device pixel ratio: 1.0 (physical pixels per logical pixel)
flutter: │ configuration: Size(800.0, 600.0) at 1.0x (in logical pixels)
flutter: │
flutter: └─child: RenderSemanticsAnnotations#fe6b5
flutter: │ needs compositing
flutter: │ creator: Semantics ← _FocusInheritedScope ← Focus ←
flutter: │ HeroControllerScope ← ScrollConfiguration ← MaterialApp ←
flutter: │ MediaQuery ← _MediaQueryFromView ← _ViewScope ←
flutter: │ View-[GlobalObjectKey FlutterView#6cffa] ← [root]
flutter: │ parentData: <none>
flutter: │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ size: Size(800.0, 600.0)
flutter: │
flutter: └─child: RenderSemanticsAnnotations#6edef
flutter: │ needs compositing
flutter: │ creator: Semantics ← _FocusInheritedScope ← Focus ← Shortcuts ←
flutter: │ _SharedAppModel ← SharedAppData ← UnmanagedRestorationScope ←
flutter: │ RestorationScope ← UnmanagedRestorationScope ←
flutter: │ RootRestorationScope ← WidgetsApp-[GlobalObjectKey
flutter: │ _MaterialAppState#5c303] ← Semantics ← ⋯
flutter: │ parentData: <none> (can use size)
flutter: │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ size: Size(800.0, 600.0)
flutter: │
flutter: └─child: RenderSemanticsAnnotations#e8ce8
flutter: │ needs compositing
flutter: │ creator: Semantics ← _FocusInheritedScope ← Focus ← Shortcuts ←
flutter: │ DefaultTextEditingShortcuts ← Semantics ← _FocusInheritedScope
flutter: │ ← Focus ← Shortcuts ← _SharedAppModel ← SharedAppData ←
flutter: │ UnmanagedRestorationScope ← ⋯
flutter: │ parentData: <none> (can use size)
flutter: │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ size: Size(800.0, 600.0)
flutter: │
flutter: └─child: RenderSemanticsAnnotations#fc545
flutter: │ needs compositing
flutter: │ creator: Semantics ← _FocusInheritedScope ← Focus ← Shortcuts ←
flutter: │ Semantics ← _FocusInheritedScope ← Focus ← Shortcuts ←
flutter: │ DefaultTextEditingShortcuts ← Semantics ← _FocusInheritedScope
flutter: │ ← Focus ← ⋯
flutter: │ parentData: <none> (can use size)
flutter: │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ size: Size(800.0, 600.0)
flutter: │
flutter: └─child: RenderTapRegionSurface#ff857
flutter: │ needs compositing
flutter: │ creator: TapRegionSurface ← _FocusInheritedScope ← Focus ←
flutter: │ FocusTraversalGroup ← _ActionsScope ← Actions ← Semantics ←
flutter: │ _FocusInheritedScope ← Focus ← Shortcuts ← Semantics ←
flutter: │ _FocusInheritedScope ← ⋯
flutter: │ parentData: <none> (can use size)
flutter: │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ size: Size(800.0, 600.0)
flutter: │ behavior: deferToChild
flutter: │
flutter: └─child: RenderSemanticsAnnotations#fe316
flutter: │ needs compositing
flutter: │ creator: Semantics ← _FocusInheritedScope ← Focus ← Shortcuts ←
flutter: │ _ShortcutRegistrarScope ← ShortcutRegistrar ← TapRegionSurface
flutter: │ ← _FocusInheritedScope ← Focus ← FocusTraversalGroup ←
flutter: │ _ActionsScope ← Actions ← ⋯
flutter: │ parentData: <none> (can use size)
flutter: │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ size: Size(800.0, 600.0)
flutter: │
flutter: └─child: RenderSemanticsAnnotations#fa55c
flutter: │ needs compositing
flutter: │ creator: Semantics ← Localizations ← Semantics ←
flutter: │ _FocusInheritedScope ← Focus ← Shortcuts ←
flutter: │ _ShortcutRegistrarScope ← ShortcutRegistrar ← TapRegionSurface
flutter: │ ← _FocusInheritedScope ← Focus ← FocusTraversalGroup ← ⋯
flutter: │ parentData: <none> (can use size)
flutter: │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ size: Size(800.0, 600.0)
flutter: │
flutter: └─child: RenderCustomPaint#4b256
flutter: │ needs compositing
flutter: │ creator: CustomPaint ← Banner ← CheckedModeBanner ← Title ←
flutter: │ Directionality ← _LocalizationsScope-[GlobalKey#4a3aa] ←
flutter: │ Semantics ← Localizations ← Semantics ← _FocusInheritedScope ←
flutter: │ Focus ← Shortcuts ← ⋯
flutter: │ parentData: <none> (can use size)
flutter: │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ size: Size(800.0, 600.0)
flutter: │ painter: null
flutter: │ foregroundPainter: BannerPainter#1bfd7(Instance of
flutter: │ '_SystemFontsNotifier')
flutter: │
flutter: └─child: RenderSemanticsAnnotations#f470f
flutter: │ needs compositing
flutter: │ creator: Semantics ← FocusScope ← DefaultSelectionStyle ←
flutter: │ IconTheme ← IconTheme ← _InheritedCupertinoTheme ←
flutter: │ CupertinoTheme ← _InheritedTheme ← Theme ← AnimatedTheme ←
flutter: │ DefaultSelectionStyle ← _ScaffoldMessengerScope ← ⋯
flutter: │ parentData: <none> (can use size)
flutter: │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ size: Size(800.0, 600.0)
flutter: │
flutter: └─child: RenderPointerListener#f59c8
flutter: │ needs compositing
flutter: │ creator: Listener ← HeroControllerScope ←
flutter: │ Navigator-[GlobalObjectKey<NavigatorState>
flutter: │ _WidgetsAppState#0d73a] ← _FocusInheritedScope ← Semantics ←
flutter: │ FocusScope ← DefaultSelectionStyle ← IconTheme ← IconTheme ←
flutter: │ _InheritedCupertinoTheme ← CupertinoTheme ← _InheritedTheme ← ⋯
flutter: │ parentData: <none> (can use size)
flutter: │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ size: Size(800.0, 600.0)
flutter: │ behavior: deferToChild
flutter: │ listeners: down, up, cancel
flutter: │
flutter: └─child: RenderAbsorbPointer#c91bd
flutter: │ needs compositing
flutter: │ creator: AbsorbPointer ← Listener ← HeroControllerScope ←
flutter: │ Navigator-[GlobalObjectKey<NavigatorState>
flutter: │ _WidgetsAppState#0d73a] ← _FocusInheritedScope ← Semantics ←
flutter: │ FocusScope ← DefaultSelectionStyle ← IconTheme ← IconTheme ←
flutter: │ _InheritedCupertinoTheme ← CupertinoTheme ← ⋯
flutter: │ parentData: <none> (can use size)
flutter: │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ size: Size(800.0, 600.0)
flutter: │ absorbing: false
flutter: │ ignoringSemantics: null
flutter: │
flutter: └─child: _RenderTheater#07897
flutter: │ needs compositing
flutter: │ creator: _Theater ←
flutter: │ Overlay-[LabeledGlobalKey<OverlayState>#49a93] ←
flutter: │ UnmanagedRestorationScope ← _FocusInheritedScope ← Focus ←
flutter: │ _FocusInheritedScope ← Focus ← FocusTraversalGroup ←
flutter: │ AbsorbPointer ← Listener ← HeroControllerScope ←
flutter: │ Navigator-[GlobalObjectKey<NavigatorState>
flutter: │ _WidgetsAppState#0d73a] ← ⋯
flutter: │ parentData: <none> (can use size)
flutter: │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ size: Size(800.0, 600.0)
flutter: │ skipCount: 0
flutter: │ textDirection: ltr
flutter: │
flutter: ├─onstage 1: RenderIgnorePointer#3b659
flutter: │ │ creator: IgnorePointer ← _RenderTheaterMarker ←
flutter: │ │ _EffectiveTickerMode ← TickerMode ←
flutter: │ │ _OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#a47f4]
flutter: │ │ ← _Theater ← Overlay-[LabeledGlobalKey<OverlayState>#49a93] ←
flutter: │ │ UnmanagedRestorationScope ← _FocusInheritedScope ← Focus ←
flutter: │ │ _FocusInheritedScope ← Focus ← ⋯
flutter: │ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use
flutter: │ │ size)
flutter: │ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ │ size: Size(800.0, 600.0)
flutter: │ │ ignoring: false
flutter: │ │ ignoringSemantics: null
flutter: │ │
flutter: │ └─child: RenderBlockSemantics#7586c
flutter: │ │ creator: BlockSemantics ← ModalBarrier ← IgnorePointer ←
flutter: │ │ _RenderTheaterMarker ← _EffectiveTickerMode ← TickerMode ←
flutter: │ │ _OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#a47f4]
flutter: │ │ ← _Theater ← Overlay-[LabeledGlobalKey<OverlayState>#49a93] ←
flutter: │ │ UnmanagedRestorationScope ← _FocusInheritedScope ← Focus ← ⋯
flutter: │ │ parentData: <none> (can use size)
flutter: │ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ │ blocks semantics of earlier render objects below the common
flutter: │ │ boundary
flutter: │ │ size: Size(800.0, 600.0)
flutter: │ │ blocking: true
flutter: │ │
flutter: │ └─child: RenderExcludeSemantics#c1d3f
flutter: │ │ creator: ExcludeSemantics ← BlockSemantics ← ModalBarrier ←
flutter: │ │ IgnorePointer ← _RenderTheaterMarker ← _EffectiveTickerMode ←
flutter: │ │ TickerMode ←
flutter: │ │ _OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#a47f4]
flutter: │ │ ← _Theater ← Overlay-[LabeledGlobalKey<OverlayState>#49a93] ←
flutter: │ │ UnmanagedRestorationScope ← _FocusInheritedScope ← ⋯
flutter: │ │ parentData: <none> (can use size)
flutter: │ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ │ size: Size(800.0, 600.0)
flutter: │ │ excluding: true
flutter: │ │
flutter: │ └─child: RenderSemanticsGestureHandler#70b16
flutter: │ │ creator: _GestureSemantics ← RawGestureDetector ←
flutter: │ │ _ModalBarrierGestureDetector ← ExcludeSemantics ←
flutter: │ │ BlockSemantics ← ModalBarrier ← IgnorePointer ←
flutter: │ │ _RenderTheaterMarker ← _EffectiveTickerMode ← TickerMode ←
flutter: │ │ _OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#a47f4]
flutter: │ │ ← _Theater ← ⋯
flutter: │ │ parentData: <none> (can use size)
flutter: │ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ │ size: Size(800.0, 600.0)
flutter: │ │ behavior: opaque
flutter: │ │ gestures: <none>
flutter: │ │
flutter: │ └─child: RenderPointerListener#1f34a
flutter: │ │ creator: Listener ← _GestureSemantics ← RawGestureDetector ←
flutter: │ │ _ModalBarrierGestureDetector ← ExcludeSemantics ←
flutter: │ │ BlockSemantics ← ModalBarrier ← IgnorePointer ←
flutter: │ │ _RenderTheaterMarker ← _EffectiveTickerMode ← TickerMode ←
flutter: │ │ _OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#a47f4]
flutter: │ │ ← ⋯
flutter: │ │ parentData: <none> (can use size)
flutter: │ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ │ size: Size(800.0, 600.0)
flutter: │ │ behavior: opaque
flutter: │ │ listeners: down, panZoomStart
flutter: │ │
flutter: │ └─child: RenderSemanticsAnnotations#73467
flutter: │ │ creator: Semantics ← Listener ← _GestureSemantics ←
flutter: │ │ RawGestureDetector ← _ModalBarrierGestureDetector ←
flutter: │ │ ExcludeSemantics ← BlockSemantics ← ModalBarrier ←
flutter: │ │ IgnorePointer ← _RenderTheaterMarker ← _EffectiveTickerMode ←
flutter: │ │ TickerMode ← ⋯
flutter: │ │ parentData: <none> (can use size)
flutter: │ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ │ size: Size(800.0, 600.0)
flutter: │ │
flutter: │ └─child: RenderMouseRegion#560dc
flutter: │ │ creator: MouseRegion ← Semantics ← Listener ← _GestureSemantics ←
flutter: │ │ RawGestureDetector ← _ModalBarrierGestureDetector ←
flutter: │ │ ExcludeSemantics ← BlockSemantics ← ModalBarrier ←
flutter: │ │ IgnorePointer ← _RenderTheaterMarker ← _EffectiveTickerMode ← ⋯
flutter: │ │ parentData: <none> (can use size)
flutter: │ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ │ size: Size(800.0, 600.0)
flutter: │ │ behavior: opaque
flutter: │ │ listeners: <none>
flutter: │ │ cursor: SystemMouseCursor(basic)
flutter: │ │
flutter: │ └─child: RenderConstrainedBox#01e8c
flutter: │ creator: ConstrainedBox ← MouseRegion ← Semantics ← Listener ←
flutter: │ _GestureSemantics ← RawGestureDetector ←
flutter: │ _ModalBarrierGestureDetector ← ExcludeSemantics ←
flutter: │ BlockSemantics ← ModalBarrier ← IgnorePointer ←
flutter: │ _RenderTheaterMarker ← ⋯
flutter: │ parentData: <none> (can use size)
flutter: │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: │ size: Size(800.0, 600.0)
flutter: │ additionalConstraints: BoxConstraints(biggest)
flutter: │
flutter: ├─onstage 2: RenderSemanticsAnnotations#8187b
flutter: ╎ │ needs compositing
flutter: ╎ │ creator: Semantics ← _RenderTheaterMarker ← _EffectiveTickerMode
flutter: ╎ │ ← TickerMode ←
flutter: ╎ │ _OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#8cd54]
flutter: ╎ │ ← _Theater ← Overlay-[LabeledGlobalKey<OverlayState>#49a93] ←
flutter: ╎ │ UnmanagedRestorationScope ← _FocusInheritedScope ← Focus ←
flutter: ╎ │ _FocusInheritedScope ← Focus ← ⋯
flutter: ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use
flutter: ╎ │ size)
flutter: ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: ╎ │ size: Size(800.0, 600.0)
flutter: ╎ │
flutter: ╎ └─child: RenderOffstage#f211d
flutter: ╎ │ needs compositing
flutter: ╎ │ creator: Offstage ← _ModalScopeStatus ← UnmanagedRestorationScope
flutter: ╎ │ ← RestorationScope ← AnimatedBuilder ←
flutter: ╎ │ _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#db401]
flutter: ╎ │ ← Semantics ← _RenderTheaterMarker ← _EffectiveTickerMode ←
flutter: ╎ │ TickerMode ←
flutter: ╎ │ _OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#8cd54]
flutter: ╎ │ ← _Theater ← ⋯
flutter: ╎ │ parentData: <none> (can use size)
flutter: ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: ╎ │ size: Size(800.0, 600.0)
flutter: ╎ │ offstage: false
flutter: ╎ │
flutter: ╎ └─child: RenderSemanticsAnnotations#9436c
flutter: ╎ │ needs compositing
flutter: ╎ │ creator: Semantics ← FocusScope ← PrimaryScrollController ←
flutter: ╎ │ _ActionsScope ← Actions ← Builder ← PageStorage ← Offstage ←
flutter: ╎ │ _ModalScopeStatus ← UnmanagedRestorationScope ←
flutter: ╎ │ RestorationScope ← AnimatedBuilder ← ⋯
flutter: ╎ │ parentData: <none> (can use size)
flutter: ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: ╎ │ size: Size(800.0, 600.0)
flutter: ╎ │
flutter: ╎ └─child: RenderRepaintBoundary#f8f28
flutter: ╎ │ needs compositing
flutter: ╎ │ creator: RepaintBoundary ← _FocusInheritedScope ← Semantics ←
flutter: ╎ │ FocusScope ← PrimaryScrollController ← _ActionsScope ← Actions
flutter: ╎ │ ← Builder ← PageStorage ← Offstage ← _ModalScopeStatus ←
flutter: ╎ │ UnmanagedRestorationScope ← ⋯
flutter: ╎ │ parentData: <none> (can use size)
flutter: ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: ╎ │ layer: OffsetLayer#e73b7
flutter: ╎ │ size: Size(800.0, 600.0)
flutter: ╎ │ metrics: 66.7% useful (1 bad vs 2 good)
flutter: ╎ │ diagnosis: insufficient data to draw conclusion (less than five
flutter: ╎ │ repaints)
flutter: ╎ │
flutter: ╎ └─child: RenderFractionalTranslation#c3a54
flutter: ╎ │ needs compositing
flutter: ╎ │ creator: FractionalTranslation ← SlideTransition ←
flutter: ╎ │ CupertinoPageTransition ← AnimatedBuilder ← RepaintBoundary ←
flutter: ╎ │ _FocusInheritedScope ← Semantics ← FocusScope ←
flutter: ╎ │ PrimaryScrollController ← _ActionsScope ← Actions ← Builder ← ⋯
flutter: ╎ │ parentData: <none> (can use size)
flutter: ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: ╎ │ size: Size(800.0, 600.0)
flutter: ╎ │ translation: Offset(0.0, 0.0)
flutter: ╎ │ transformHitTests: false
flutter: ╎ │
flutter: ╎ └─child: RenderFractionalTranslation#7fcf2
flutter: ╎ │ needs compositing
flutter: ╎ │ creator: FractionalTranslation ← SlideTransition ←
flutter: ╎ │ FractionalTranslation ← SlideTransition ←
flutter: ╎ │ CupertinoPageTransition ← AnimatedBuilder ← RepaintBoundary ←
flutter: ╎ │ _FocusInheritedScope ← Semantics ← FocusScope ←
flutter: ╎ │ PrimaryScrollController ← _ActionsScope ← ⋯
flutter: ╎ │ parentData: <none> (can use size)
flutter: ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: ╎ │ size: Size(800.0, 600.0)
flutter: ╎ │ translation: Offset(0.0, 0.0)
flutter: ╎ │ transformHitTests: true
flutter: ╎ │
flutter: ╎ └─child: RenderDecoratedBox#713ec
flutter: ╎ │ needs compositing
flutter: ╎ │ creator: DecoratedBox ← DecoratedBoxTransition ←
flutter: ╎ │ FractionalTranslation ← SlideTransition ← FractionalTranslation
flutter: ╎ │ ← SlideTransition ← CupertinoPageTransition ← AnimatedBuilder ←
flutter: ╎ │ RepaintBoundary ← _FocusInheritedScope ← Semantics ← FocusScope
flutter: ╎ │ ← ⋯
flutter: ╎ │ parentData: <none> (can use size)
flutter: ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: ╎ │ size: Size(800.0, 600.0)
flutter: ╎ │ ├─decoration: _CupertinoEdgeShadowDecoration
flutter: ╎ │ colors: Color(0x04000000), Color(0x00000000)
flutter: ╎ │
flutter: ╎ │ configuration: ImageConfiguration(bundle:
flutter: ╎ │ PlatformAssetBundle#164ca(), devicePixelRatio: 1.0, locale:
flutter: ╎ │ en_US, textDirection: TextDirection.ltr, platform: macOS)
flutter: ╎ │
flutter: ╎ └─child: RenderStack#83b13
flutter: ╎ │ needs compositing
flutter: ╎ │ creator: Stack ← _CupertinoBackGestureDetector<dynamic> ←
flutter: ╎ │ DecoratedBox ← DecoratedBoxTransition ← FractionalTranslation ←
flutter: ╎ │ SlideTransition ← FractionalTranslation ← SlideTransition ←
flutter: ╎ │ CupertinoPageTransition ← AnimatedBuilder ← RepaintBoundary ←
flutter: ╎ │ _FocusInheritedScope ← ⋯
flutter: ╎ │ parentData: <none> (can use size)
flutter: ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: ╎ │ size: Size(800.0, 600.0)
flutter: ╎ │ alignment: AlignmentDirectional.topStart
flutter: ╎ │ textDirection: ltr
flutter: ╎ │ fit: passthrough
flutter: ╎ │
flutter: ╎ ├─child 1: RenderIgnorePointer#ad50f
flutter: ╎ │ │ needs compositing
flutter: ╎ │ │ creator: IgnorePointer ← AnimatedBuilder ← Stack ←
flutter: ╎ │ │ _CupertinoBackGestureDetector<dynamic> ← DecoratedBox ←
flutter: ╎ │ │ DecoratedBoxTransition ← FractionalTranslation ←
flutter: ╎ │ │ SlideTransition ← FractionalTranslation ← SlideTransition ←
flutter: ╎ │ │ CupertinoPageTransition ← AnimatedBuilder ← ⋯
flutter: ╎ │ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use
flutter: ╎ │ │ size)
flutter: ╎ │ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: ╎ │ │ size: Size(800.0, 600.0)
flutter: ╎ │ │ ignoring: false
flutter: ╎ │ │ ignoringSemantics: null
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderRepaintBoundary#29754
flutter: ╎ │ │ needs compositing
flutter: ╎ │ │ creator: RepaintBoundary-[GlobalKey#75409] ← IgnorePointer ←
flutter: ╎ │ │ AnimatedBuilder ← Stack ←
flutter: ╎ │ │ _CupertinoBackGestureDetector<dynamic> ← DecoratedBox ←
flutter: ╎ │ │ DecoratedBoxTransition ← FractionalTranslation ←
flutter: ╎ │ │ SlideTransition ← FractionalTranslation ← SlideTransition ←
flutter: ╎ │ │ CupertinoPageTransition ← ⋯
flutter: ╎ │ │ parentData: <none> (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: ╎ │ │ layer: OffsetLayer#fa835
flutter: ╎ │ │ size: Size(800.0, 600.0)
flutter: ╎ │ │ metrics: 90.9% useful (1 bad vs 10 good)
flutter: ╎ │ │ diagnosis: this is an outstandingly useful repaint boundary and
flutter: ╎ │ │ should definitely be kept
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderSemanticsAnnotations#95566
flutter: ╎ │ │ creator: Semantics ← Builder ← RepaintBoundary-[GlobalKey#75409]
flutter: ╎ │ │ ← IgnorePointer ← AnimatedBuilder ← Stack ←
flutter: ╎ │ │ _CupertinoBackGestureDetector<dynamic> ← DecoratedBox ←
flutter: ╎ │ │ DecoratedBoxTransition ← FractionalTranslation ←
flutter: ╎ │ │ SlideTransition ← FractionalTranslation ← ⋯
flutter: ╎ │ │ parentData: <none> (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: ╎ │ │ size: Size(800.0, 600.0)
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderPhysicalModel#bc9d7
flutter: ╎ │ │ creator: PhysicalModel ← AnimatedPhysicalModel ← Material ←
flutter: ╎ │ │ AppHome ← Semantics ← Builder ←
flutter: ╎ │ │ RepaintBoundary-[GlobalKey#75409] ← IgnorePointer ←
flutter: ╎ │ │ AnimatedBuilder ← Stack ←
flutter: ╎ │ │ _CupertinoBackGestureDetector<dynamic> ← DecoratedBox ← ⋯
flutter: ╎ │ │ parentData: <none> (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: ╎ │ │ size: Size(800.0, 600.0)
flutter: ╎ │ │ elevation: 0.0
flutter: ╎ │ │ color: Color(0xfffafafa)
flutter: ╎ │ │ shadowColor: Color(0xfffafafa)
flutter: ╎ │ │ shape: BoxShape.rectangle
flutter: ╎ │ │ borderRadius: BorderRadius.zero
flutter: ╎ │ │
flutter: ╎ │ └─child: _RenderInkFeatures#ac819
flutter: ╎ │ │ creator: _InkFeatures-[GlobalKey#d721e ink renderer] ←
flutter: ╎ │ │ NotificationListener<LayoutChangedNotification> ← PhysicalModel
flutter: ╎ │ │ ← AnimatedPhysicalModel ← Material ← AppHome ← Semantics ←
flutter: ╎ │ │ Builder ← RepaintBoundary-[GlobalKey#75409] ← IgnorePointer ←
flutter: ╎ │ │ AnimatedBuilder ← Stack ← ⋯
flutter: ╎ │ │ parentData: <none> (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: ╎ │ │ size: Size(800.0, 600.0)
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderPositionedBox#dc1df
flutter: ╎ │ │ creator: Center ← DefaultTextStyle ← AnimatedDefaultTextStyle ←
flutter: ╎ │ │ _InkFeatures-[GlobalKey#d721e ink renderer] ←
flutter: ╎ │ │ NotificationListener<LayoutChangedNotification> ← PhysicalModel
flutter: ╎ │ │ ← AnimatedPhysicalModel ← Material ← AppHome ← Semantics ←
flutter: ╎ │ │ Builder ← RepaintBoundary-[GlobalKey#75409] ← ⋯
flutter: ╎ │ │ parentData: <none> (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(w=800.0, h=600.0)
flutter: ╎ │ │ size: Size(800.0, 600.0)
flutter: ╎ │ │ alignment: Alignment.center
flutter: ╎ │ │ textDirection: ltr
flutter: ╎ │ │ widthFactor: expand
flutter: ╎ │ │ heightFactor: expand
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderSemanticsAnnotations#a0a4b relayoutBoundary=up1
flutter: ╎ │ │ creator: Semantics ← TextButton ← Center ← DefaultTextStyle ←
flutter: ╎ │ │ AnimatedDefaultTextStyle ← _InkFeatures-[GlobalKey#d721e ink
flutter: ╎ │ │ renderer] ← NotificationListener<LayoutChangedNotification> ←
flutter: ╎ │ │ PhysicalModel ← AnimatedPhysicalModel ← Material ← AppHome ←
flutter: ╎ │ │ Semantics ← ⋯
flutter: ╎ │ │ parentData: offset=Offset(329.0, 286.0) (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(0.0<=w<=800.0, 0.0<=h<=600.0)
flutter: ╎ │ │ semantic boundary
flutter: ╎ │ │ size: Size(142.0, 28.0)
flutter: ╎ │ │
flutter: ╎ │ └─child: _RenderInputPadding#4672f relayoutBoundary=up2
flutter: ╎ │ │ creator: _InputPadding ← Semantics ← TextButton ← Center ←
flutter: ╎ │ │ DefaultTextStyle ← AnimatedDefaultTextStyle ←
flutter: ╎ │ │ _InkFeatures-[GlobalKey#d721e ink renderer] ←
flutter: ╎ │ │ NotificationListener<LayoutChangedNotification> ← PhysicalModel
flutter: ╎ │ │ ← AnimatedPhysicalModel ← Material ← AppHome ← ⋯
flutter: ╎ │ │ parentData: <none> (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(0.0<=w<=800.0, 0.0<=h<=600.0)
flutter: ╎ │ │ size: Size(142.0, 28.0)
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderConstrainedBox#425d6 relayoutBoundary=up3
flutter: ╎ │ │ creator: ConstrainedBox ← _InputPadding ← Semantics ← TextButton
flutter: ╎ │ │ ← Center ← DefaultTextStyle ← AnimatedDefaultTextStyle ←
flutter: ╎ │ │ _InkFeatures-[GlobalKey#d721e ink renderer] ←
flutter: ╎ │ │ NotificationListener<LayoutChangedNotification> ← PhysicalModel
flutter: ╎ │ │ ← AnimatedPhysicalModel ← Material ← ⋯
flutter: ╎ │ │ parentData: offset=Offset(0.0, 0.0) (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(0.0<=w<=800.0, 0.0<=h<=600.0)
flutter: ╎ │ │ size: Size(142.0, 28.0)
flutter: ╎ │ │ additionalConstraints: BoxConstraints(56.0<=w<=Infinity,
flutter: ╎ │ │ 28.0<=h<=Infinity)
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderPhysicalShape#8e171 relayoutBoundary=up4
flutter: ╎ │ │ creator: PhysicalShape ← _MaterialInterior ← Material ←
flutter: ╎ │ │ ConstrainedBox ← _InputPadding ← Semantics ← TextButton ←
flutter: ╎ │ │ Center ← DefaultTextStyle ← AnimatedDefaultTextStyle ←
flutter: ╎ │ │ _InkFeatures-[GlobalKey#d721e ink renderer] ←
flutter: ╎ │ │ NotificationListener<LayoutChangedNotification> ← ⋯
flutter: ╎ │ │ parentData: <none> (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(56.0<=w<=800.0, 28.0<=h<=600.0)
flutter: ╎ │ │ size: Size(142.0, 28.0)
flutter: ╎ │ │ elevation: 0.0
flutter: ╎ │ │ color: Color(0x00000000)
flutter: ╎ │ │ shadowColor: Color(0x00000000)
flutter: ╎ │ │ clipper: ShapeBorderClipper
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderCustomPaint#eea46 relayoutBoundary=up5
flutter: ╎ │ │ creator: CustomPaint ← _ShapeBorderPaint ← PhysicalShape ←
flutter: ╎ │ │ _MaterialInterior ← Material ← ConstrainedBox ← _InputPadding ←
flutter: ╎ │ │ Semantics ← TextButton ← Center ← DefaultTextStyle ←
flutter: ╎ │ │ AnimatedDefaultTextStyle ← ⋯
flutter: ╎ │ │ parentData: <none> (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(56.0<=w<=800.0, 28.0<=h<=600.0)
flutter: ╎ │ │ size: Size(142.0, 28.0)
flutter: ╎ │ │ painter: null
flutter: ╎ │ │ foregroundPainter: _ShapeBorderPainter#ac724()
flutter: ╎ │ │
flutter: ╎ │ └─child: _RenderInkFeatures#b19a7 relayoutBoundary=up6
flutter: ╎ │ │ creator: _InkFeatures-[GlobalKey#87971 ink renderer] ←
flutter: ╎ │ │ NotificationListener<LayoutChangedNotification> ← CustomPaint ←
flutter: ╎ │ │ _ShapeBorderPaint ← PhysicalShape ← _MaterialInterior ←
flutter: ╎ │ │ Material ← ConstrainedBox ← _InputPadding ← Semantics ←
flutter: ╎ │ │ TextButton ← Center ← ⋯
flutter: ╎ │ │ parentData: <none> (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(56.0<=w<=800.0, 28.0<=h<=600.0)
flutter: ╎ │ │ size: Size(142.0, 28.0)
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderSemanticsAnnotations#4d1b3 relayoutBoundary=up7
flutter: ╎ │ │ creator: Semantics ← _FocusInheritedScope ← Focus ← _ActionsScope
flutter: ╎ │ │ ← Actions ← _ParentInkResponseProvider ←
flutter: ╎ │ │ _InkResponseStateWidget ← InkWell ← DefaultTextStyle ←
flutter: ╎ │ │ AnimatedDefaultTextStyle ← _InkFeatures-[GlobalKey#87971 ink
flutter: ╎ │ │ renderer] ← NotificationListener<LayoutChangedNotification> ← ⋯
flutter: ╎ │ │ parentData: <none> (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(56.0<=w<=800.0, 28.0<=h<=600.0)
flutter: ╎ │ │ size: Size(142.0, 28.0)
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderMouseRegion#e5b3f relayoutBoundary=up8
flutter: ╎ │ │ creator: MouseRegion ← Semantics ← _FocusInheritedScope ← Focus ←
flutter: ╎ │ │ _ActionsScope ← Actions ← _ParentInkResponseProvider ←
flutter: ╎ │ │ _InkResponseStateWidget ← InkWell ← DefaultTextStyle ←
flutter: ╎ │ │ AnimatedDefaultTextStyle ← _InkFeatures-[GlobalKey#87971 ink
flutter: ╎ │ │ renderer] ← ⋯
flutter: ╎ │ │ parentData: <none> (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(56.0<=w<=800.0, 28.0<=h<=600.0)
flutter: ╎ │ │ size: Size(142.0, 28.0)
flutter: ╎ │ │ behavior: opaque
flutter: ╎ │ │ listeners: enter, exit
flutter: ╎ │ │ cursor: SystemMouseCursor(click)
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderSemanticsAnnotations#deb9b relayoutBoundary=up9
flutter: ╎ │ │ creator: Semantics ← DefaultSelectionStyle ← Builder ←
flutter: ╎ │ │ MouseRegion ← Semantics ← _FocusInheritedScope ← Focus ←
flutter: ╎ │ │ _ActionsScope ← Actions ← _ParentInkResponseProvider ←
flutter: ╎ │ │ _InkResponseStateWidget ← InkWell ← ⋯
flutter: ╎ │ │ parentData: <none> (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(56.0<=w<=800.0, 28.0<=h<=600.0)
flutter: ╎ │ │ size: Size(142.0, 28.0)
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderPointerListener#2017a relayoutBoundary=up10
flutter: ╎ │ │ creator: Listener ← RawGestureDetector ← GestureDetector ←
flutter: ╎ │ │ Semantics ← DefaultSelectionStyle ← Builder ← MouseRegion ←
flutter: ╎ │ │ Semantics ← _FocusInheritedScope ← Focus ← _ActionsScope ←
flutter: ╎ │ │ Actions ← ⋯
flutter: ╎ │ │ parentData: <none> (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(56.0<=w<=800.0, 28.0<=h<=600.0)
flutter: ╎ │ │ size: Size(142.0, 28.0)
flutter: ╎ │ │ behavior: opaque
flutter: ╎ │ │ listeners: down, panZoomStart
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderPadding#8455f relayoutBoundary=up11
flutter: ╎ │ │ creator: Padding ← IconTheme ← Builder ← Listener ←
flutter: ╎ │ │ RawGestureDetector ← GestureDetector ← Semantics ←
flutter: ╎ │ │ DefaultSelectionStyle ← Builder ← MouseRegion ← Semantics ←
flutter: ╎ │ │ _FocusInheritedScope ← ⋯
flutter: ╎ │ │ parentData: <none> (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(56.0<=w<=800.0, 28.0<=h<=600.0)
flutter: ╎ │ │ size: Size(142.0, 28.0)
flutter: ╎ │ │ padding: EdgeInsets(8.0, 0.0, 8.0, 0.0)
flutter: ╎ │ │ textDirection: ltr
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderPositionedBox#80b8d relayoutBoundary=up12
flutter: ╎ │ │ creator: Align ← Padding ← IconTheme ← Builder ← Listener ←
flutter: ╎ │ │ RawGestureDetector ← GestureDetector ← Semantics ←
flutter: ╎ │ │ DefaultSelectionStyle ← Builder ← MouseRegion ← Semantics ← ⋯
flutter: ╎ │ │ parentData: offset=Offset(8.0, 0.0) (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(40.0<=w<=784.0, 28.0<=h<=600.0)
flutter: ╎ │ │ size: Size(126.0, 28.0)
flutter: ╎ │ │ alignment: Alignment.center
flutter: ╎ │ │ textDirection: ltr
flutter: ╎ │ │ widthFactor: 1.0
flutter: ╎ │ │ heightFactor: 1.0
flutter: ╎ │ │
flutter: ╎ │ └─child: RenderParagraph#59bc2 relayoutBoundary=up13
flutter: ╎ │ │ creator: RichText ← Text ← Align ← Padding ← IconTheme ← Builder
flutter: ╎ │ │ ← Listener ← RawGestureDetector ← GestureDetector ← Semantics ←
flutter: ╎ │ │ DefaultSelectionStyle ← Builder ← ⋯
flutter: ╎ │ │ parentData: offset=Offset(0.0, 6.0) (can use size)
flutter: ╎ │ │ constraints: BoxConstraints(0.0<=w<=784.0, 0.0<=h<=600.0)
flutter: ╎ │ │ size: Size(126.0, 16.0)
flutter: ╎ │ │ textAlign: start
flutter: ╎ │ │ textDirection: ltr
flutter: ╎ │ │ softWrap: wrapping at box width
flutter: ╎ │ │ overflow: clip
flutter: ╎ │ │ locale: en_US
flutter: ╎ │ │ maxLines: unlimited
flutter: ╎ │ ╘═╦══ text ═══
flutter: ╎ │ ║ TextSpan:
flutter: ╎ │ ║ debugLabel: ((englishLike labelLarge 2014).merge(blackRedwoodCity
flutter: ╎ │ ║ labelLarge)).copyWith
flutter: ╎ │ ║ inherit: false
flutter: ╎ │ ║ color: MaterialColor(primary value: Color(0xff2196f3))
flutter: ╎ │ ║ family: .AppleSystemUIFont
flutter: ╎ │ ║ size: 14.0
flutter: ╎ │ ║ weight: 500
flutter: ╎ │ ║ baseline: alphabetic
flutter: ╎ │ ║ decoration: TextDecoration.none
flutter: ╎ │ ║ "Dump Render Tree"
flutter: ╎ │ ╚═══════════
flutter: ╎ └─child 2: RenderPointerListener#db4b5
flutter: ╎ creator: Listener ← Positioned ← PositionedDirectional ← Stack ←
flutter: ╎ _CupertinoBackGestureDetector<dynamic> ← DecoratedBox ←
flutter: ╎ DecoratedBoxTransition ← FractionalTranslation ←
flutter: ╎ SlideTransition ← FractionalTranslation ← SlideTransition ←
flutter: ╎ CupertinoPageTransition ← ⋯
flutter: ╎ parentData: top=0.0; bottom=0.0; left=0.0; width=20.0;
flutter: ╎ offset=Offset(0.0, 0.0) (can use size)
flutter: ╎ constraints: BoxConstraints(w=20.0, h=600.0)
flutter: ╎ size: Size(20.0, 600.0)
flutter: ╎ behavior: translucent
flutter: ╎ listeners: down
flutter: ╎
flutter: └╌no offstage children
flutter:
在示例 5 的渲染树中:
RenderView
(或窗口大小)将所有渲染对象(包括RenderPositionedBox
#dc1df
渲染对象)限制到屏幕大小。此示例将大小设置为Size(800.0, 600.0)
每个渲染对象的
constraints
属性限制每个子级的大小。此属性将BoxConstraints
渲染对象作为值。从RenderSemanticsAnnotations#fe6b5
开始,约束等于BoxConstraints(w=800.0, h=600.0)
。Center
widget 在RenderSemanticsAnnotations#8187b
子树下创建了RenderPositionedBox#dc1df
渲染对象。此渲染对象下的每个子项都有包含最小和最大值的
BoxConstraints
。例如,RenderSemanticsAnnotations#a0a4b
使用BoxConstraints(0.0<=w<=800.0, 0.0<=h<=600.0)
。RenderPhysicalShape#8e171
渲染对象的所有子项都使用BoxConstraints(BoxConstraints(56.0<=w<=800.0, 28.0<=h<=600.0))
。子级
RenderPadding#8455f
将padding
值设置为EdgeInsets(8.0, 0.0, 8.0, 0.0)
。这将所有后续子级渲染对象的左右填充都设置为 8。它们现在有了新的约束:BoxConstraints(40.0<=w<=784.0, 28.0<=h<=600.0)
。
这个对象,creator
字段告诉我们它很可能是 TextButton
定义的一部分,它为其内容设置了 88 像素的最小宽度和 36.0 的特定高度。这是 TextButton
类实现了 Material Design 关于按钮尺寸的指南。
RenderPositionedBox#80b8d
渲染对象再次放宽了约束,以便将文本居中于按钮内。RenderParagraph
#59bc2 渲染对象根据其内容选择其大小。如果你沿着树向上跟踪大小,你会看到文本的大小如何影响构成按钮的所有框的宽度。所有父级都采用其子级的尺寸来调整自身大小。
另一种注意到这一点的方法是查看每个框描述中的 relayoutBoundary
属性。这会告诉你,有多少祖先依赖于此元素的大小。
例如,最里面的 RenderPositionedBox
行具有 relayoutBoundary=up13
。这意味着当 Flutter 将 RenderConstrainedBox
标记为脏时,它还会将其 13 个祖先标记为脏,因为新尺寸可能会影响这些祖先。
如果你编写自己的渲染对象,要将信息添加到转储中,请重写 debugFillProperties()
。将 DiagnosticsProperty 对象添加到方法的参数中,然后调用超类方法。
打印层树
#要调试合成问题,请使用 debugDumpLayerTree()
。
示例 6: 调用 debugDumpLayerTree()
#import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: AppHome()));
}
class AppHome extends StatelessWidget {
const AppHome({super.key});
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: TextButton(
onPressed: () {
debugDumpLayerTree();
},
child: const Text('Dump Layer Tree'),
),
),
);
}
}
展开以查看示例 6 的层树输出
flutter: TransformLayer#214da
flutter: │ owner: RenderView#ebaaf
flutter: │ creator: [root]
flutter: │ engine layer: TransformEngineLayer#535de
flutter: │ handles: 1
flutter: │ offset: Offset(0.0, 0.0)
flutter: │ transform:
flutter: │ [0] 1.0,0.0,0.0,0.0
flutter: │ [1] 0.0,1.0,0.0,0.0
flutter: │ [2] 0.0,0.0,1.0,0.0
flutter: │ [3] 0.0,0.0,0.0,1.0
flutter: │
flutter: ├─child 1: OffsetLayer#0f766
flutter: │ │ creator: RepaintBoundary ← _FocusInheritedScope ← Semantics ←
flutter: │ │ FocusScope ← PrimaryScrollController ← _ActionsScope ← Actions
flutter: │ │ ← Builder ← PageStorage ← Offstage ← _ModalScopeStatus ←
flutter: │ │ UnmanagedRestorationScope ← ⋯
flutter: │ │ engine layer: OffsetEngineLayer#1768d
flutter: │ │ handles: 2
flutter: │ │ offset: Offset(0.0, 0.0)
flutter: │ │
flutter: │ ├─child 1: PictureLayer#dd023
flutter: │ │ handles: 1
flutter: │ │ paint bounds: Rect.fromLTRB(0.0, 0.0, 800.0, 600.0)
flutter: │ │ picture: _NativePicture#36f94
flutter: │ │ raster cache hints: isComplex = false, willChange = false
flutter: │ │
flutter: │ └─child 2: OffsetLayer#4cfc8
flutter: │ │ creator: RepaintBoundary-[GlobalKey#bd5d9] ← IgnorePointer ←
flutter: │ │ AnimatedBuilder ← Stack ←
flutter: │ │ _CupertinoBackGestureDetector<dynamic> ← DecoratedBox ←
flutter: │ │ DecoratedBoxTransition ← FractionalTranslation ←
flutter: │ │ SlideTransition ← FractionalTranslation ← SlideTransition ←
flutter: │ │ CupertinoPageTransition ← ⋯
flutter: │ │ engine layer: OffsetEngineLayer#a1676
flutter: │ │ handles: 2
flutter: │ │ offset: Offset(0.0, 0.0)
flutter: │ │
flutter: │ └─child 1: PictureLayer#aee55
flutter: │ handles: 1
flutter: │ paint bounds: Rect.fromLTRB(0.0, 0.0, 800.0, 600.0)
flutter: │ picture: _NativePicture#e732d
flutter: │ raster cache hints: isComplex = false, willChange = false
flutter: │
flutter: └─child 2: PictureLayer#b16e5
flutter: handles: 1
flutter: paint bounds: Rect.fromLTRB(0.0, 0.0, 800.0, 600.0)
flutter: picture: _NativePicture#eef0a
flutter: raster cache hints: isComplex = false, willChange = false
flutter:
RepaintBoundary
widget 创建了:
一个
RenderRepaintBoundary
RenderObject,如示例 5 的结果所示。╎ └─child: RenderRepaintBoundary#f8f28 ╎ │ needs compositing ╎ │ creator: RepaintBoundary ← _FocusInheritedScope ← Semantics ← ╎ │ FocusScope ← PrimaryScrollController ← _ActionsScope ← Actions ╎ │ ← Builder ← PageStorage ← Offstage ← _ModalScopeStatus ← ╎ │ UnmanagedRestorationScope ← ⋯ ╎ │ parentData: <none> (can use size) ╎ │ constraints: BoxConstraints(w=800.0, h=600.0) ╎ │ layer: OffsetLayer#e73b7 ╎ │ size: Size(800.0, 600.0) ╎ │ metrics: 66.7% useful (1 bad vs 2 good) ╎ │ diagnosis: insufficient data to draw conclusion (less than five ╎ │ repaints)
层树中的一个新层,如示例 6 的结果所示。
├─child 1: OffsetLayer#0f766 │ │ creator: RepaintBoundary ← _FocusInheritedScope ← Semantics ← │ │ FocusScope ← PrimaryScrollController ← _ActionsScope ← Actions │ │ ← Builder ← PageStorage ← Offstage ← _ModalScopeStatus ← │ │ UnmanagedRestorationScope ← ⋯ │ │ engine layer: OffsetEngineLayer#1768d │ │ handles: 2 │ │ offset: Offset(0.0, 0.0)
这减少了需要重新绘制的数量。
打印焦点树
#要调试焦点或快捷方式问题,请使用 debugDumpFocusTree()
函数转储焦点树。
debugDumpFocusTree()
方法返回应用的焦点树。
焦点树以下列方式标记节点:
- 焦点节点标记为
PRIMARY FOCUS
。 - 焦点节点的祖先标记为
IN FOCUS PATH
。
如果你的应用使用了 Focus
widget,请使用 debugLabel
属性来简化在树中查找其焦点节点。
你还可以使用 debugFocusChanges
布尔属性,以在焦点更改时启用详细日志记录。
示例 7: 调用 debugDumpFocusTree()
#import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: AppHome()));
}
class AppHome extends StatelessWidget {
const AppHome({super.key});
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: TextButton(
onPressed: () {
debugDumpFocusTree();
},
child: const Text('Dump Focus Tree'),
),
),
);
}
}
展开以查看示例 7 的焦点树
flutter: FocusManager#9d096
flutter: │ primaryFocus: FocusScopeNode#926dc(_ModalScopeState<dynamic>
flutter: │ Focus Scope [PRIMARY FOCUS])
flutter: │ primaryFocusCreator: FocusScope ← PrimaryScrollController ←
flutter: │ _ActionsScope ← Actions ← Builder ← PageStorage ← Offstage ←
flutter: │ _ModalScopeStatus ← UnmanagedRestorationScope ←
flutter: │ RestorationScope ← AnimatedBuilder ←
flutter: │ _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#bd53e]
flutter: │ ← Semantics ← _RenderTheaterMarker ← _EffectiveTickerMode ←
flutter: │ TickerMode ←
flutter: │ _OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#89dd7]
flutter: │ ← _Theater ← Overlay-[LabeledGlobalKey<OverlayState>#52f82] ←
flutter: │ UnmanagedRestorationScope ← ⋯
flutter: │
flutter: └─rootScope: FocusScopeNode#f4205(Root Focus Scope [IN FOCUS PATH])
flutter: │ IN FOCUS PATH
flutter: │ focusedChildren: FocusScopeNode#a0d10(Navigator Scope [IN FOCUS
flutter: │ PATH])
flutter: │
flutter: └─Child 1: FocusNode#088ec([IN FOCUS PATH])
flutter: │ context: Focus
flutter: │ NOT FOCUSABLE
flutter: │ IN FOCUS PATH
flutter: │
flutter: └─Child 1: FocusNode#85f70(Shortcuts [IN FOCUS PATH])
flutter: │ context: Focus
flutter: │ NOT FOCUSABLE
flutter: │ IN FOCUS PATH
flutter: │
flutter: └─Child 1: FocusNode#f0c18(Shortcuts [IN FOCUS PATH])
flutter: │ context: Focus
flutter: │ NOT FOCUSABLE
flutter: │ IN FOCUS PATH
flutter: │
flutter: └─Child 1: FocusNode#0749f(Shortcuts [IN FOCUS PATH])
flutter: │ context: Focus
flutter: │ NOT FOCUSABLE
flutter: │ IN FOCUS PATH
flutter: │
flutter: └─Child 1: _FocusTraversalGroupNode#28990(FocusTraversalGroup [IN FOCUS PATH])
flutter: │ context: Focus
flutter: │ NOT FOCUSABLE
flutter: │ IN FOCUS PATH
flutter: │
flutter: └─Child 1: FocusNode#5b515(Shortcuts [IN FOCUS PATH])
flutter: │ context: Focus
flutter: │ NOT FOCUSABLE
flutter: │ IN FOCUS PATH
flutter: │
flutter: └─Child 1: FocusScopeNode#a0d10(Navigator Scope [IN FOCUS PATH])
flutter: │ context: FocusScope
flutter: │ IN FOCUS PATH
flutter: │ focusedChildren: FocusScopeNode#926dc(_ModalScopeState<dynamic>
flutter: │ Focus Scope [PRIMARY FOCUS])
flutter: │
flutter: └─Child 1: _FocusTraversalGroupNode#72c8a(FocusTraversalGroup [IN FOCUS PATH])
flutter: │ context: Focus
flutter: │ NOT FOCUSABLE
flutter: │ IN FOCUS PATH
flutter: │
flutter: └─Child 1: FocusNode#eb709(Navigator [IN FOCUS PATH])
flutter: │ context: Focus
flutter: │ IN FOCUS PATH
flutter: │
flutter: └─Child 1: FocusScopeNode#926dc(_ModalScopeState<dynamic> Focus Scope [PRIMARY FOCUS])
flutter: │ context: FocusScope
flutter: │ PRIMARY FOCUS
flutter: │
flutter: └─Child 1: FocusNode#a6b74
flutter: context: Focus
flutter:
打印语义树
#debugDumpSemanticsTree()
函数打印应用的语义树。
语义树被呈现给系统辅助功能 API。要获取语义树的转储:
- 使用系统辅助工具或
SemanticsDebugger
启用辅助功能 - 使用
debugDumpSemanticsTree()
函数。
示例 8: 调用 debugDumpSemanticsTree()
#import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(const MaterialApp(home: AppHome()));
}
class AppHome extends StatelessWidget {
const AppHome({super.key});
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Semantics(
button: true,
enabled: true,
label: 'Clickable text here!',
child: GestureDetector(
onTap: () {
debugDumpSemanticsTree();
if (kDebugMode) {
print('Clicked!');
}
},
child: const Text('Click Me!', style: TextStyle(fontSize: 56)),
),
),
),
);
}
}
展开以查看示例 8 的语义树
flutter: SemanticsNode#0
flutter: │ Rect.fromLTRB(0.0, 0.0, 800.0, 600.0)
flutter: │
flutter: └─SemanticsNode#1
flutter: │ Rect.fromLTRB(0.0, 0.0, 800.0, 600.0)
flutter: │ textDirection: ltr
flutter: │
flutter: └─SemanticsNode#2
flutter: │ Rect.fromLTRB(0.0, 0.0, 800.0, 600.0)
flutter: │ sortKey: OrdinalSortKey#824a2(order: 0.0)
flutter: │
flutter: └─SemanticsNode#3
flutter: │ Rect.fromLTRB(0.0, 0.0, 800.0, 600.0)
flutter: │ flags: scopesRoute
flutter: │
flutter: └─SemanticsNode#4
flutter: Rect.fromLTRB(278.0, 267.0, 522.0, 333.0)
flutter: actions: tap
flutter: flags: isButton, hasEnabledState, isEnabled
flutter: label:
flutter: "Clickable text here!
flutter: Click Me!"
flutter: textDirection: ltr
flutter:
flutter: Clicked!
打印事件时间
#如果你想知道事件相对于帧的开始和结束何时发生,你可以设置打印来记录这些事件。要将帧的开始和结束打印到控制台,请切换 debugPrintBeginFrameBanner
和 debugPrintEndFrameBanner
。
示例 1 的打印帧横幅日志
I/flutter : ▄▄▄▄▄▄▄▄ Frame 12 30s 437.086ms ▄▄▄▄▄▄▄▄
I/flutter : Debug print: Am I performing this work more than once per frame?
I/flutter : Debug print: Am I performing this work more than once per frame?
I/flutter : ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
要打印导致当前帧被调度的调用堆栈,请使用 debugPrintScheduleFrameStacks
标志。
调试布局问题
#要使用 GUI 调试布局问题,请将 debugPaintSizeEnabled
设置为 true
。此标志可在 rendering
库中找到。你可以在任何时候启用它,并在其为 true
时影响所有绘制。考虑将其添加到 void main()
入口点的顶部。
示例 9
#请看以下代码中的一个示例:
// Add import to the Flutter rendering library.
import 'package:flutter/rendering.dart';
void main() {
debugPaintSizeEnabled = true;
runApp(const MyApp());
}
启用后,Flutter 会显示对应用进行的以下更改:
- 以亮青色边框显示所有框。
- 将所有填充显示为带褪色蓝色填充和蓝色边框的框,围绕子 widget。
- 用黄色箭头显示所有对齐定位。
- 当它们没有子项时,将所有间隔符显示为灰色。
debugPaintBaselinesEnabled
标志执行类似的操作,但用于具有基线的对象。应用程序以亮绿色显示字母字符的基线,以橙色显示表意字符的基线。字母字符“坐”在字母基线上,但该基线“穿过”CJK 字符的底部。Flutter 将表意基线定位在文本行的最底部。
debugPaintPointersEnabled
标志会开启一种特殊模式,以青色突出显示你点击的任何对象。这可以帮助你确定对象是否未能通过点击测试。如果对象落在其父级边界之外,从而最初未被考虑进行点击测试,则可能会发生这种情况。
如果你正在尝试调试合成器层,请考虑使用以下标志。
使用
debugPaintLayerBordersEnabled
标志查找每个层的边界。此标志会用橙色描绘每个层的边界。使用
debugRepaintRainbowEnabled
标志显示重新绘制的层。每当层重新绘制时,它都会覆盖一组旋转的颜色。
Flutter 框架中任何以 debug...
开头的函数或方法只在调试模式下工作。
调试动画问题
#将 timeDilation
变量(来自 scheduler
库)设置为大于 1.0 的数字,例如 50.0。最好只在应用启动时设置一次。如果你在运行时更改它,特别是当动画正在运行时你降低它时,框架可能会观察到时间倒退,这可能会导致断言并通常干扰你的工作。
调试性能问题
#Flutter 提供了各种顶级属性和函数,可在开发周期的各个阶段帮助你调试应用。要使用这些功能,请在调试模式下编译你的应用。
以下列表重点介绍了渲染库中的一些标志和一个用于调试性能问题的函数。
debugDumpRenderTree()
要将渲染树转储到控制台,请在非布局或重新绘制阶段调用此函数。
要设置这些标志,可以:
- 编辑框架代码
- 导入模块,在
main()
函数中设置值,然后热重启。
debugPaintLayerBordersEnabled
- 要显示每个图层的边界,请将此属性设置为
true
。设置后,每个图层会围绕其边界绘制一个框。 debugRepaintRainbowEnabled
- 要为每个 widget 显示彩色边框,请将此属性设置为
true
。这些边框会随着应用用户在应用中滚动而改变颜色。要设置此标志,请在应用中添加debugRepaintRainbowEnabled = true;
作为顶级属性。如果设置此标志后任何静态 widget 颜色循环,请考虑在这些区域添加重新绘制边界。 debugPrintMarkNeedsLayoutStacks
- 要确定你的应用是否创建了比预期更多的布局,请将此属性设置为
true
。此布局问题可能发生在时间轴、配置文件或布局方法中的print
语句中。设置后,框架会将堆栈跟踪输出到控制台,以解释你的应用为何将每个渲染对象标记为需要布局。 debugPrintMarkNeedsPaintStacks
- 要确定你的应用是否绘制了比预期更多的布局,请将此属性设置为
true
。
你也可以按需生成堆栈跟踪。要打印自己的堆栈跟踪,请将 debugPrintStack()
函数添加到你的应用中。
追踪 Dart 代码性能
#要执行自定义性能追踪并测量 Dart 代码任意部分的实际时间或 CPU 时间,请使用 dart:developer
Timeline 工具。
打开你的源代码。
将你要测量的代码包装在
Timeline
方法中。dartimport 'dart:developer'; void main() { Timeline.startSync('interesting function'); // iWonderHowLongThisTakes(); Timeline.finishSync(); }
连接到应用后,打开 DevTools 的时间线事件标签页。
在性能设置中选择 Dart 录制选项。
执行你要测量的函数。
为了确保运行时性能特征与最终产品紧密匹配,请在配置文件模式下运行你的应用。
添加性能叠加层
#要在代码中启用 PerformanceOverlay
widget,请在 MaterialApp
、CupertinoApp
或 WidgetsApp
构造函数上将 showPerformanceOverlay
属性设置为 true
示例 10
#import 'package:flutter/material.dart';
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
showPerformanceOverlay: true,
title: 'My Awesome App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const MyHomePage(title: 'My Awesome App'),
);
}
}
(如果你没有使用 MaterialApp
、CupertinoApp
或 WidgetsApp
,你可以通过将你的应用程序包装在一个堆栈中,并在你的堆栈上放置一个通过调用 PerformanceOverlay.allEnabled()
创建的 widget 来获得相同的效果。)
要了解如何解释叠加层中的图表,请查阅分析 Flutter 性能中的性能叠加层。
添加 widget 对齐网格
#要在应用中添加一个覆盖层到 Material Design 基线网格,以帮助验证对齐,请在 MaterialApp
构造函数中添加 debugShowMaterialGrid
参数。
要为非 Material 应用添加叠加层,请添加 GridPaper
widget。