从代码调试 Flutter 应用程序
本指南介绍了可以在代码中启用哪些调试功能。有关调试和分析工具的完整列表,请查看调试页面。
向应用程序添加日志记录
您有两个选项可以记录应用程序日志。
- 使用
print()
语句打印到stdout
和stderr
。 -
导入
dart:io
并调用stderr
和stdout
的方法。例如stderr.writeln('print me');
如果您一次输出太多,那么 Android 可能会丢弃一些日志行。为避免这种情况,请使用 Flutter 的 foundation
库中的 debugPrint()
。此 print
包装器会限制输出,以避免 Android 内核丢弃输出。
您还可以使用 dart:developer
log()
函数记录应用程序。这使您可以在日志输出中包含更细粒度和更多信息。
示例 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 编码的 error 参数解释为数据对象。DevTool 会在该日志条目的详细信息视图中呈现。
设置断点
您可以在 DevTools 的 调试器 或 IDE 的内置调试器中设置断点。
要设置编程断点
- 将
dart:developer
包导入相关文件。 -
使用
debugger()
语句插入编程断点。此语句采用可选的when
参数。此布尔参数在给定条件解析为 true 时设置断点。示例 3 说明了这一点。
示例 3
import 'dart:developer';
void someFunction(double offset) {
debugger(when: offset > 30);
// ...
}
使用标志调试应用程序层
Flutter 框架的每一层都提供了一个函数,用于使用 debugPrint
属性将当前状态或事件转储到控制台。
打印小部件树
要转储 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'),
),
),
);
}
}
此函数从部件树的根节点开始递归调用 toStringDeep()
方法。它返回一个“扁平化”的树。
示例 4 生成以下部件树。它包括
- 通过其各种构建函数投影的所有部件。
-
许多未在应用源中显示的部件。框架的部件构建函数在构建期间会插入这些部件。
例如,以下树显示了
_InkFeatures
。该类实现了Material
部件的一部分。它未出现在 示例 4 中的任何代码中。
展开以查看示例 4 的部件树
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 将特定对象标记为“脏”的原因。查看部件树时,请查找类似于以下内容的行
└TextButton(dirty, dependencies: [MediaQuery, _InheritedTheme, _LocalizationsScope-[GlobalKey#5880d]], state: _ButtonStyleState#ab76e)
如果你编写自己的部件,请覆盖 debugFillProperties()
方法以添加信息。将 DiagnosticsProperty 对象添加到该方法的参数中,并调用超类方法。 toString
方法使用此函数来填写部件的描述。
打印渲染树
在调试布局问题时,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
小部件在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
小组件创建
-
渲染树中的
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
小组件,请使用 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 rendering library
import 'package:flutter/rendering.dart';
void main() {
debugPaintSizeEnabled = true;
runApp(const MyApp());
}
启用后,Flutter 会对你的应用程序显示以下更改
- 以亮青色边框显示所有框。
- 将所有填充显示为一个框,其中包含一个淡蓝色填充和围绕子小部件的蓝色边框。
- 使用黄色箭头显示所有对齐定位。
- 当没有子项时,以灰色显示所有间隔符。
debugPaintBaselinesEnabled
标志对具有基线的对象执行类似的操作。应用程序以亮绿色显示字母字符的基线,以橙色显示表意字符的基线。字母字符“位于”字母基线上,但该基线“切穿”CJK 字符 的底部。Flutter 将表意基线定位在文本行的最底部。
debugPaintPointersEnabled
标志开启一种特殊模式,该模式将您点击的任何对象突出显示为青绿色。这可以帮助您确定对象是否未通过点击测试。如果对象超出其父对象的边界,则可能会发生这种情况,因此一开始不会考虑对该对象进行点击测试。
如果您尝试调试合成器图层,请考虑使用以下标志。
-
使用
debugPaintLayerBordersEnabled
标志查找每个图层的边界。此标志将导致用橙色勾勒出每个图层的边界。 -
使用
debugRepaintRainbowEnabled
标志显示重新绘制的图层。每当图层重新绘制时,它都会覆盖一组旋转的颜色。
Flutter 框架中以 debug...
开头的任何函数或方法仅在 调试模式 下有效。
调试动画问题
将 timeDilation
变量(来自 scheduler
库)设置为大于 1.0 的数字,例如 50.0。最好仅在应用启动时设置一次。如果您在运行时更改它,尤其是在动画运行时将其减少,则框架可能会观察到时间倒退,这可能会导致断言并通常会干扰您的工作。
调试性能问题
Flutter 提供了各种顶级属性和函数,以帮助您在开发周期的各个点调试应用。要使用这些功能,请在调试模式下编译您的应用。
以下列表重点介绍了 渲染库 中的一些标志和一个函数,用于调试性能问题。
debugDumpRenderTree()
- 要在控制台中转储渲染树,请在不在布局或重绘阶段时调用此函数。
要设置这些标志,请
- 编辑框架代码
- 导入模块,在
main()
函数中设置值,然后热重启。
debugPaintLayerBordersEnabled
- 要显示每层的边界,请将此属性设为
true
。设置后,每层都会在其边界周围绘制一个框。 debugRepaintRainbowEnabled
- 要显示每个小部件周围的彩色边框,请将此属性设为
true
。当应用用户在应用中滚动时,这些边框会改变颜色。要设置此标志,请将debugRepaintRainbowEnabled = true;
作为应用中的顶级属性添加。如果设置此标志后有任何静态小部件在颜色之间旋转,请考虑向这些区域添加重绘边界。 debugPrintMarkNeedsLayoutStacks
- 要确定应用是否创建了超出预期的布局,请将此属性设为
true
。此布局问题可能发生在时间轴上、个人资料中或布局方法中的print
语句中。设置后,框架会将堆栈跟踪输出到控制台,以解释应用标记每个渲染对象进行布局的原因。 debugPrintMarkNeedsPaintStacks
- 要确定应用是否绘制了超出预期的布局,请将此属性设为
true
。
您还可以按需生成堆栈跟踪。要打印自己的堆栈跟踪,请将 debugPrintStack()
函数添加到应用中。
跟踪 Dart 代码性能
要执行自定义性能跟踪并测量 Dart 代码任意片段的墙上时间或 CPU 时间(就像 Android 使用 systrace 所做的那样),请使用 dart:developer
时间轴 实用程序。
- 打开源代码。
-
将您想要测量的代码包装在
Timeline
方法中。import 'dart:developer'; void main() { Timeline.startSync('interesting function'); // iWonderHowLongThisTakes(); Timeline.finishSync(); }
- 在连接到您的应用时,打开 DevTools 的 时间线事件选项卡。
- 在 **性能设置** 中选择 **Dart** 记录选项。
- 执行您想要测量的函数。
为了确保运行时性能特征与您的最终产品紧密匹配,请在 配置文件 模式下运行您的应用。
添加性能覆盖
要在代码中启用 PerformanceOverlay
小部件,请将 showPerformanceOverlay
属性设置为 true
,位于 MaterialApp
、CupertinoApp
或 WidgetsApp
构造函数上
示例 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()
创建的小部件来获得相同的效果。)
要了解如何解释覆盖中的图表,请查看 性能覆盖,位于 分析 Flutter 性能。
添加小部件对齐网格
要在您的应用上添加一个覆盖到 材质设计基线网格,以帮助验证对齐,请在 MaterialApp
构造函数 中添加 debugShowMaterialGrid
参数。
要在非材质应用中添加覆盖,请添加一个 GridPaper
小部件。