做自(zì)由與創造的先行者

Flutter 調試應用(yòng)

Flutter開(kāi)發手冊

Dart 分析器

在運行應用(yòng)程序前,請(qǐng)運行flutter analyze測試你(nǐ)的代碼。這(zhè)個工(gōng)具(它是dartanalyzer工(gōng)具的一個包裝)将分析你(nǐ)的代碼并幫助你(nǐ)發現(xiàn)可能(néng)的錯誤。 如果你(nǐ)使用(yòng)IntelliJ的Flutter插件,那麽已經自(zì)動啓用(yòng)了(le)。

Dart分析器大(dà)量使用(yòng)了(le)代碼中的類型注釋來(lái)幫助追蹤問題。我們鼓勵您在任何地方使用(yòng)它們(避免var、無類型的參數、無類型的列表文(wén)字等),因爲這(zhè)是追蹤問題的最快(kuài)的方式。

Dart Observatory (語句級的單步調試和(hé)分析器)

如果您使用(yòng)flutter run啓動應用(yòng)程序,那麽當它運行時(shí),您可以打開(kāi)Observatory URL的Web頁面(例如Observatory監聽http://127.0.0.1:8100/), 直接使用(yòng)語句級單步調試器連接到(dào)您的應用(yòng)程序。如果您使用(yòng)的是IntelliJ,則還可以使用(yòng)其内置的調試器來(lái)調試您的應用(yòng)程序。

Observatory 同時(shí)支持分析、檢查堆等。有關Observatory的更多信息請(qǐng)參考Observatory 文(wén)檔.

如果您使用(yòng)Observatory進行分析,請(qǐng)确保通過--profile選項來(lái)運行flutter run命令來(lái)運行應用(yòng)程序。 否則,配置文(wén)件中将出現(xiàn)的主要問題将是調試斷言,以驗證框架的各種不變量(請(qǐng)參閱下(xià)面的“調試模式斷言”)。

window() 聲明(míng)

當使用(yòng)Dart Observatory(或另一個Dart調試器,例如IntelliJ IDE中的調試器)時(shí),可以使用(yòng)該window()語句插入編程式斷點。要使用(yòng)這(zhè)個,你(nǐ)必須添加import 'dart:developer';到(dào)相關文(wén)件頂部。

window()語句采用(yòng)一個可選when參數,您可以指定該參數僅在特定條件爲真時(shí)中斷,如下(xià)所示:

void someFunction(double offset) {

window(when: offset > 30.0);

// ...

}

print、debugPrint、flutter logs

Dart print()功能(néng)将輸出到(dào)系統控制台,您可以使用(yòng)flutter logs了(le)查看(kàn)它(基本上(shàng)是一個包裝adb logcat)。

如果你(nǐ)一次輸出太多,那麽Android有時(shí)會(huì)丢棄一些(xiē)日志行。爲了(le)避免這(zhè)種情況,您可以使用(yòng)Flutter的foundation庫中的debugPrint()。 這(zhè)是一個封裝print,它将輸出限制在一個級别,避免被Android内核丢棄。

Flutter框架中的許多類都有toString實現(xiàn)。按照慣例,這(zhè)些(xiē)輸出通常包括對(duì)象的runtimeType單行輸出,通常在表單中ClassName(more information about this instance…)。 樹中使用(yòng)的一些(xiē)類也(yě)具有toStringDeep,從(cóng)該點返回整個子樹的多行描述。已一些(xiē)具有詳細信息toString的類會(huì)實現(xiàn)一個toStringShort,它隻返回對(duì)象的類型或其他(tā)非常簡短的(一個或兩個單詞)描述。

調試模式斷言

在開(kāi)發過程中,強烈建議(yì)您使用(yòng)Flutter的“調試”模式,有時(shí)也(yě)稱爲“checked”模式(譯者語:Dart2.0後“checked”被廢除,可以使用(yòng)“strong” mode)。 如果您使用(yòng)flutter run運行程序。在這(zhè)種模式下(xià),Dart assert語句被啓用(yòng),并且Flutter框架使用(yòng)它來(lái)執行許多運行時(shí)檢查來(lái)驗證是否違反一些(xiē)不可變的規則。

當一個不可變的規則被違反時(shí),它被報(bào)告給控制台,并帶有一些(xiē)上(shàng)下(xià)文(wén)信息來(lái)幫助追蹤問題的根源。

要關閉調試模式并使用(yòng)發布模式,請(qǐng)使用(yòng)flutter run --release運行您的應用(yòng)程序。 這(zhè)也(yě)關閉了(le)Observatory調試器。一個中間模式可以關閉除Observatory之外(wài)所有調試輔助工(gōng)具的,稱爲“profile mode”,用(yòng)--profile替代--release即可。

調試應用(yòng)程序層

Flutter框架的每一層都提供了(le)将其當前狀态或事(shì)件轉儲(dump)到(dào)控制台(使用(yòng)debugPrint)的功能(néng)。

Widget 層

要轉儲Widgets庫的狀态,請(qǐng)調用(yòng)debugDumpApp()。 隻要應用(yòng)程序已經構建了(le)至少一次(即在調用(yòng)build()之後的任何時(shí)間),您可以在應用(yòng)程序未處于構建階段(即,不在build()方法内調用(yòng) )的任何時(shí)間調用(yòng)此方法(在調用(yòng)runApp()之後)。

如, 這(zhè)個應用(yòng)程序:

import 'package:flutter/material.dart';

void main() {

runApp(

new MaterialApp(

home: new AppHome(),

),

);

}

class AppHome extends StatelessWidget {

@override

Widget build(BuildContext context) {

return new Material(

child: new Center(

child: new FlatButton(

onPressed: () {

debugDumpApp();

},

child: new Text('Dump App'),

),

),

);

}

}

…會(huì)輸出這(zhè)樣的内容(精确的細節會(huì)根據框架的版本、設備的大(dà)小(xiǎo)等等而變化):

I/flutter ( 6559): WidgetsFlutterBinding - CHECKED MODE

I/flutter ( 6559): RenderObjectToWidgetAdapter([GlobalObjectKey RenderView(497039273)]; renderObject: RenderView)

I/flutter ( 6559): └MaterialApp(state: _MaterialAppState(1009803148))

I/flutter ( 6559): └ScrollConfiguration()

I/flutter ( 6559): └AnimatedTheme(duration: 200ms; state: _AnimatedThemeState(543295893; ticker inactive; ThemeDataTween(ThemeData(Brightness.light Color(0xff2196f3) etc...) → null)))

I/flutter ( 6559): └Theme(ThemeData(Brightness.light Color(0xff2196f3) etc...))

I/flutter ( 6559): └WidgetsApp([GlobalObjectKey _MaterialAppState(1009803148)]; state: _WidgetsAppState(552902158))

I/flutter ( 6559): └CheckedModeBanner()

I/flutter ( 6559): └Banner()

I/flutter ( 6559): └CustomPaint(renderObject: RenderCustomPaint)

I/flutter ( 6559): └DefaultTextStyle(inherit: true; color: Color(0xd0ff0000); family: "monospace"; size: 48.0; weight: 900; decoration: double Color(0xffffff00) TextDecoration.underline)

I/flutter ( 6559): └MediaQuery(MediaQueryData(size: Size(411.4, 683.4), devicePixelRatio: 2.625, textScaleFactor: 1.0, padding: EdgeInsets(0.0, 24.0, 0.0, 0.0)))

I/flutter ( 6559): └LocaleQuery(null)

I/flutter ( 6559): └Title(color: Color(0xff2196f3))

I/flutter ( 6559): └Navigator([GlobalObjectKey _WidgetsAppState(552902158)]; state: NavigatorState(240327618; tracking 1 ticker))

I/flutter ( 6559): └Listener(listeners: down, up, cancel; behavior: defer-to-child; renderObject: RenderPointerListener)

I/flutter ( 6559): └AbsorbPointer(renderObject: RenderAbsorbPointer)

I/flutter ( 6559): └Focus([GlobalKey 489139594]; state: _FocusState(739584448))

I/flutter ( 6559): └Semantics(container: true; renderObject: RenderSemanticsAnnotations)

I/flutter ( 6559): └_FocusScope(this scope has focus; focused subscope: [GlobalObjectKey MaterialPageRoute(875520219)])

I/flutter ( 6559): └Overlay([GlobalKey 199833992]; state: OverlayState(619367313; entries: [OverlayEntry@248818791(opaque: false; maintainState: false), OverlayEntry@837336156(opaque: false; maintainState: true)]))

I/flutter ( 6559): └_Theatre(renderObject: _RenderTheatre)

I/flutter ( 6559): └Stack(renderObject: RenderStack)

I/flutter ( 6559): ├_OverlayEntry([GlobalKey 612888877]; state: _OverlayEntryState(739137453))

I/flutter ( 6559): │└IgnorePointer(ignoring: false; renderObject: RenderIgnorePointer)

I/flutter ( 6559): │ └ModalBarrier()

I/flutter ( 6559): │ └Semantics(container: true; renderObject: RenderSemanticsAnnotations)

I/flutter ( 6559): │ └GestureDetector()

I/flutter ( 6559): │ └RawGestureDetector(state: RawGestureDetectorState(39068508; gestures: tap; behavior: opaque))

I/flutter ( 6559): │ └_GestureSemantics(renderObject: RenderSemanticsGestureHandler)

I/flutter ( 6559): │ └Listener(listeners: down; behavior: opaque; renderObject: RenderPointerListener)

I/flutter ( 6559): │ └ConstrainedBox(BoxConstraints(biggest); renderObject: RenderConstrainedBox)

I/flutter ( 6559): └_OverlayEntry([GlobalKey 727622716]; state: _OverlayEntryState(279971240))

I/flutter ( 6559): └_ModalScope([GlobalKey 816151164]; state: _ModalScopeState(875510645))

I/flutter ( 6559): └Focus([GlobalObjectKey MaterialPageRoute(875520219)]; state: _FocusState(331487674))

I/flutter ( 6559): └Semantics(container: true; renderObject: RenderSemanticsAnnotations)

I/flutter ( 6559): └_FocusScope(this scope has focus)

I/flutter ( 6559): └Offstage(offstage: false; renderObject: RenderOffstage)

I/flutter ( 6559): └IgnorePointer(ignoring: false; renderObject: RenderIgnorePointer)

I/flutter ( 6559): └_MountainViewPageTransition(animation: AnimationController(⏭ 1.000; paused; for MaterialPageRoute(/))➩ProxyAnimation➩Cubic(0.40, 0.00, 0.20, 1.00)➩Tween(Offset(0.0, 1.0) → Offset(0.0, 0.0))➩Offset(0.0, 0.0); state: _AnimatedState(552160732))

I/flutter ( 6559): └SlideTransition(animation: AnimationController(⏭ 1.000; paused; for MaterialPageRoute(/))➩ProxyAnimation➩Cubic(0.40, 0.00, 0.20, 1.00)➩Tween(Offset(0.0, 1.0) → Offset(0.0, 0.0))➩Offset(0.0, 0.0); state: _AnimatedState(714726495))

I/flutter ( 6559): └FractionalTranslation(renderObject: RenderFractionalTranslation)

I/flutter ( 6559): └RepaintBoundary(renderObject: RenderRepaintBoundary)

I/flutter ( 6559): └PageStorage([GlobalKey 619728754])

I/flutter ( 6559): └_ModalScopeStatus(active)

I/flutter ( 6559): └AppHome()

I/flutter ( 6559): └Material(MaterialType.canvas; elevation: 0; state: _MaterialState(780114997))

I/flutter ( 6559): └AnimatedContainer(duration: 200ms; has background; state: _AnimatedContainerState(616063822; ticker inactive; has background))

I/flutter ( 6559): └Container(bg: BoxDecoration())

I/flutter ( 6559): └DecoratedBox(renderObject: RenderDecoratedBox)

I/flutter ( 6559): └Container(bg: BoxDecoration(backgroundColor: Color(0xfffafafa)))

I/flutter ( 6559): └DecoratedBox(renderObject: RenderDecoratedBox)

I/flutter ( 6559): └NotificationListener()

I/flutter ( 6559): └_InkFeature([GlobalKey ink renderer]; renderObject: _RenderInkFeatures)

I/flutter ( 6559): └AnimatedDefaultTextStyle(duration: 200ms; inherit: false; color: Color(0xdd000000); family: "Roboto"; size: 14.0; weight: 400; baseline: alphabetic; state: _AnimatedDefaultTextStyleState(427742350; ticker inactive))

I/flutter ( 6559): └DefaultTextStyle(inherit: false; color: Color(0xdd000000); family: "Roboto"; size: 14.0; weight: 400; baseline: alphabetic)

I/flutter ( 6559): └Center(alignment: Alignment.center; renderObject: RenderPositionedBox)

I/flutter ( 6559): └FlatButton()

I/flutter ( 6559): └MaterialButton(state: _MaterialButtonState(398724090))

I/flutter ( 6559): └ConstrainedBox(BoxConstraints(88.0<=w<=Infinity, h=36.0); renderObject: RenderConstrainedBox relayoutBoundary=up1)

I/flutter ( 6559): └AnimatedDefaultTextStyle(duration: 200ms; inherit: false; color: Color(0xdd000000); family: "Roboto"; size: 14.0; weight: 500; baseline: alphabetic; state: _AnimatedDefaultTextStyleState(315134664; ticker inactive))

I/flutter ( 6559): └DefaultTextStyle(inherit: false; color: Color(0xdd000000); family: "Roboto"; size: 14.0; weight: 500; baseline: alphabetic)

I/flutter ( 6559): └IconTheme(color: Color(0xdd000000))

I/flutter ( 6559): └InkWell(state: _InkResponseState(369160267))

I/flutter ( 6559): └GestureDetector()

I/flutter ( 6559): └RawGestureDetector(state: RawGestureDetectorState(175370983; gestures: tap; behavior: opaque))

I/flutter ( 6559): └_GestureSemantics(renderObject: RenderSemanticsGestureHandler relayoutBoundary=up2)

I/flutter ( 6559): └Listener(listeners: down; behavior: opaque; renderObject: RenderPointerListener relayoutBoundary=up3)

I/flutter ( 6559): └Container(padding: EdgeInsets(16.0, 0.0, 16.0, 0.0))

I/flutter ( 6559): └Padding(renderObject: RenderPadding relayoutBoundary=up4)

I/flutter ( 6559): └Center(alignment: Alignment.center; widthFactor: 1.0; renderObject: RenderPositionedBox relayoutBoundary=up5)

I/flutter ( 6559): └Text("Dump App")

I/flutter ( 6559): └RichText(renderObject: RenderParagraph relayoutBoundary=up6)

這(zhè)是一個“扁平化”的樹,顯示了(le)通過各種構建函數投影的所有widget(如果你(nǐ)在widget樹的根中調用(yòng)toStringDeepwidget,這(zhè)是你(nǐ)獲得的樹)。 你(nǐ)會(huì)看(kàn)到(dào)很(hěn)多在你(nǐ)的應用(yòng)源代碼中沒有出現(xiàn)的widget,因爲它們被框架的widget的build()函數插入的。例如,InkFeature是Material widget的一個實現(xiàn)細節 。

由于debugDumpApp()當按鈕從(cóng)被按下(xià)變爲被釋放(fàng)時(shí)被調用(yòng),FlatButton對(duì)象同時(shí)調用(yòng)setState(),所以并因此将自(zì)己标記爲髒。 這(zhè)就是爲什(shén)麽如果你(nǐ)看(kàn)轉儲,你(nǐ)會(huì)看(kàn)到(dào)特定的對(duì)象标記爲“dirty”。您還可以查看(kàn)已注冊了(le)哪些(xiē)手勢監聽器; 在這(zhè)種情況下(xià),一個單一的GestureDetector被列出,并且監聽“tap”手勢(“tap”是TapGestureDetector的toStringShort函數輸出的)

如果您編寫自(zì)己的widget,則可以通過覆蓋debugFillProperties()來(lái)添加信息。 将DiagnosticsProperty對(duì)象作(zuò)爲方法參數,并調用(yòng)父類方法。 該函數是該toString方法用(yòng)來(lái)填充小(xiǎo)部件描述信息的。

渲染層

如果您嘗試調試布局問題,那麽Widgets層的樹可能(néng)不夠詳細。在這(zhè)種情況下(xià),您可以通過調用(yòng)debugDumpRenderTree()轉儲渲染樹。 正如debugDumpApp(),除布局或繪制階段外(wài),您可以随時(shí)調用(yòng)此函數。作(zuò)爲一般規則,從(cóng)frame 回調 或事(shì)件處理(lǐ)器中調用(yòng)它是最佳解決方案。

要調用(yòng)debugDumpRenderTree(),您需要添加import'package:flutter/rendering.dart';到(dào)您的源文(wén)件。

上(shàng)面這(zhè)個小(xiǎo)例子的輸出結果如下(xià)所示:

I/flutter ( 6559): RenderView

I/flutter ( 6559): │ debug mode enabled - android

I/flutter ( 6559): │ window size: Size(1080.0, 1794.0) (in physical pixels)

I/flutter ( 6559): │ device pixel ratio: 2.625 (physical pixels per logical pixel)

I/flutter ( 6559): │ configuration: Size(411.4, 683.4) at 2.625x (in logical pixels)

I/flutter ( 6559): │

I/flutter ( 6559): └─child: RenderCustomPaint

I/flutter ( 6559): │ creator: CustomPaint ← Banner ← CheckedModeBanner ←

I/flutter ( 6559): │ WidgetsApp-[GlobalObjectKey _MaterialAppState(1009803148)] ←

I/flutter ( 6559): │ Theme ← AnimatedTheme ← ScrollConfiguration ← MaterialApp ←

I/flutter ( 6559): │ [root]

I/flutter ( 6559): │ parentData:

I/flutter ( 6559): │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): │ size: Size(411.4, 683.4)

I/flutter ( 6559): │

I/flutter ( 6559): └─child: RenderPointerListener

I/flutter ( 6559): │ creator: Listener ← Navigator-[GlobalObjectKey

I/flutter ( 6559): │ _WidgetsAppState(552902158)] ← Title ← LocaleQuery ← MediaQuery

I/flutter ( 6559): │ ← DefaultTextStyle ← CustomPaint ← Banner ← CheckedModeBanner ←

I/flutter ( 6559): │ WidgetsApp-[GlobalObjectKey _MaterialAppState(1009803148)] ←

I/flutter ( 6559): │ Theme ← AnimatedTheme ← ⋯

I/flutter ( 6559): │ parentData:

I/flutter ( 6559): │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): │ size: Size(411.4, 683.4)

I/flutter ( 6559): │ behavior: defer-to-child

I/flutter ( 6559): │ listeners: down, up, cancel

I/flutter ( 6559): │

I/flutter ( 6559): └─child: RenderAbsorbPointer

I/flutter ( 6559): │ creator: AbsorbPointer ← Listener ←

I/flutter ( 6559): │ Navigator-[GlobalObjectKey

I/flutter ( 6559): │ _WidgetsAppState(552902158)] ← Title ← LocaleQuery ← MediaQuery

I/flutter ( 6559): │ ← DefaultTextStyle ← CustomPaint ← Banner ← CheckedModeBanner ←

I/flutter ( 6559): │ WidgetsApp-[GlobalObjectKey _MaterialAppState(1009803148)] ←

I/flutter ( 6559): │ Theme ← ⋯

I/flutter ( 6559): │ parentData:

I/flutter ( 6559): │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): │ size: Size(411.4, 683.4)

I/flutter ( 6559): │ absorbing: false

I/flutter ( 6559): │

I/flutter ( 6559): └─child: RenderSemanticsAnnotations

I/flutter ( 6559): │ creator: Semantics ← Focus-[GlobalKey 489139594] ← AbsorbPointer

I/flutter ( 6559): │ ← Listener ← Navigator-[GlobalObjectKey

I/flutter ( 6559): │ _WidgetsAppState(552902158)] ← Title ← LocaleQuery ← MediaQuery

I/flutter ( 6559): │ ← DefaultTextStyle ← CustomPaint ← Banner ← CheckedModeBanner ←

I/flutter ( 6559): │ ⋯

I/flutter ( 6559): │ parentData:

I/flutter ( 6559): │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): │ size: Size(411.4, 683.4)

I/flutter ( 6559): │

I/flutter ( 6559): └─child: _RenderTheatre

I/flutter ( 6559): │ creator: _Theatre ← Overlay-[GlobalKey 199833992] ← _FocusScope ←

I/flutter ( 6559): │ Semantics ← Focus-[GlobalKey 489139594] ← AbsorbPointer ←

I/flutter ( 6559): │ Listener ← Navigator-[GlobalObjectKey

I/flutter ( 6559): │ _WidgetsAppState(552902158)] ← Title ← LocaleQuery ← MediaQuery

I/flutter ( 6559): │ ← DefaultTextStyle ← ⋯

I/flutter ( 6559): │ parentData:

I/flutter ( 6559): │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): │ size: Size(411.4, 683.4)

I/flutter ( 6559): │

I/flutter ( 6559): ├─onstage: RenderStack

I/flutter ( 6559): ╎ │ creator: Stack ← _Theatre ← Overlay-[GlobalKey 199833992] ←

I/flutter ( 6559): ╎ │ _FocusScope ← Semantics ← Focus-[GlobalKey 489139594] ←

I/flutter ( 6559): ╎ │ AbsorbPointer ← Listener ←

I/flutter ( 6559): ╎ │ Navigator-[GlobalObjectKey

I/flutter ( 6559): ╎ │ _WidgetsAppState(552902158)] ← Title ← LocaleQuery ← MediaQuery

I/flutter ( 6559): ╎ │ ← ⋯

I/flutter ( 6559): ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0)

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ ├─child 1: RenderIgnorePointer

I/flutter ( 6559): ╎ │ │ creator: IgnorePointer ← _OverlayEntry-[GlobalKey 612888877] ←

I/flutter ( 6559): ╎ │ │ Stack ← _Theatre ← Overlay-[GlobalKey 199833992] ← _FocusScope

I/flutter ( 6559): ╎ │ │ ← Semantics ← Focus-[GlobalKey 489139594] ← AbsorbPointer ←

I/flutter ( 6559): ╎ │ │ Listener ← Navigator-[GlobalObjectKey

I/flutter ( 6559): ╎ │ │ _WidgetsAppState(552902158)] ← Title ← ⋯

I/flutter ( 6559): ╎ │ │ parentData: not positioned; offset=Offset(0.0, 0.0)

I/flutter ( 6559): ╎ │ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │ │ ignoring: false

I/flutter ( 6559): ╎ │ │ ignoringSemantics: implicitly false

I/flutter ( 6559): ╎ │ │

I/flutter ( 6559): ╎ │ └─child: RenderSemanticsAnnotations

I/flutter ( 6559): ╎ │ │ creator: Semantics ← ModalBarrier ← IgnorePointer ←

I/flutter ( 6559): ╎ │ │ _OverlayEntry-[GlobalKey 612888877] ← Stack ← _Theatre ←

I/flutter ( 6559): ╎ │ │ Overlay-[GlobalKey 199833992] ← _FocusScope ← Semantics ←

I/flutter ( 6559): ╎ │ │ Focus-[GlobalKey 489139594] ← AbsorbPointer ← Listener ← ⋯

I/flutter ( 6559): ╎ │ │ parentData:

I/flutter ( 6559): ╎ │ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │ │

I/flutter ( 6559): ╎ │ └─child: RenderSemanticsGestureHandler

I/flutter ( 6559): ╎ │ │ creator: _GestureSemantics ← RawGestureDetector ← GestureDetector

I/flutter ( 6559): ╎ │ │ ← Semantics ← ModalBarrier ← IgnorePointer ←

I/flutter ( 6559): ╎ │ │ _OverlayEntry-[GlobalKey 612888877] ← Stack ← _Theatre ←

I/flutter ( 6559): ╎ │ │ Overlay-[GlobalKey 199833992] ← _FocusScope ← Semantics ← ⋯

I/flutter ( 6559): ╎ │ │ parentData:

I/flutter ( 6559): ╎ │ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │ │

I/flutter ( 6559): ╎ │ └─child: RenderPointerListener

I/flutter ( 6559): ╎ │ │ creator: Listener ← _GestureSemantics ← RawGestureDetector ←

I/flutter ( 6559): ╎ │ │ GestureDetector ← Semantics ← ModalBarrier ← IgnorePointer ←

I/flutter ( 6559): ╎ │ │ _OverlayEntry-[GlobalKey 612888877] ← Stack ← _Theatre ←

I/flutter ( 6559): ╎ │ │ Overlay-[GlobalKey 199833992] ← _FocusScope ← ⋯

I/flutter ( 6559): ╎ │ │ parentData:

I/flutter ( 6559): ╎ │ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │ │ behavior: opaque

I/flutter ( 6559): ╎ │ │ listeners: down

I/flutter ( 6559): ╎ │ │

I/flutter ( 6559): ╎ │ └─child: RenderConstrainedBox

I/flutter ( 6559): ╎ │ creator: ConstrainedBox ← Listener ← _GestureSemantics ←

I/flutter ( 6559): ╎ │ RawGestureDetector ← GestureDetector ← Semantics ← ModalBarrier

I/flutter ( 6559): ╎ │ ← IgnorePointer ← _OverlayEntry-[GlobalKey 612888877] ← Stack ←

I/flutter ( 6559): ╎ │ _Theatre ← Overlay-[GlobalKey 199833992] ← ⋯

I/flutter ( 6559): ╎ │ parentData:

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │ additionalConstraints: BoxConstraints(biggest)

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child 2: RenderSemanticsAnnotations

I/flutter ( 6559): ╎ │ creator: Semantics ← Focus-[GlobalObjectKey

I/flutter ( 6559): ╎ │ MaterialPageRoute(875520219)] ← _ModalScope-[GlobalKey

I/flutter ( 6559): ╎ │ 816151164] ← _OverlayEntry-[GlobalKey 727622716] ← Stack ←

I/flutter ( 6559): ╎ │ _Theatre ← Overlay-[GlobalKey 199833992] ← _FocusScope ←

I/flutter ( 6559): ╎ │ Semantics ← Focus-[GlobalKey 489139594] ← AbsorbPointer ←

I/flutter ( 6559): ╎ │ Listener ← ⋯

I/flutter ( 6559): ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0)

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child: RenderOffstage

I/flutter ( 6559): ╎ │ creator: Offstage ← _FocusScope ← Semantics ←

I/flutter ( 6559): ╎ │ Focus-[GlobalObjectKey MaterialPageRoute(875520219)] ←

I/flutter ( 6559): ╎ │ _ModalScope-[GlobalKey 816151164] ← _OverlayEntry-[GlobalKey

I/flutter ( 6559): ╎ │ 727622716] ← Stack ← _Theatre ← Overlay-[GlobalKey 199833992] ←

I/flutter ( 6559): ╎ │ _FocusScope ← Semantics ← Focus-[GlobalKey 489139594] ← ⋯

I/flutter ( 6559): ╎ │ parentData:

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │ offstage: false

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child: RenderIgnorePointer

I/flutter ( 6559): ╎ │ creator: IgnorePointer ← Offstage ← _FocusScope ← Semantics ←

I/flutter ( 6559): ╎ │ Focus-[GlobalObjectKey MaterialPageRoute(875520219)] ←

I/flutter ( 6559): ╎ │ _ModalScope-[GlobalKey 816151164] ← _OverlayEntry-[GlobalKey

I/flutter ( 6559): ╎ │ 727622716] ← Stack ← _Theatre ← Overlay-[GlobalKey 199833992] ←

I/flutter ( 6559): ╎ │ _FocusScope ← Semantics ← ⋯

I/flutter ( 6559): ╎ │ parentData:

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │ ignoring: false

I/flutter ( 6559): ╎ │ ignoringSemantics: implicitly false

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child: RenderFractionalTranslation

I/flutter ( 6559): ╎ │ creator: FractionalTranslation ← SlideTransition ←

I/flutter ( 6559): ╎ │ _MountainViewPageTransition ← IgnorePointer ← Offstage ←

I/flutter ( 6559): ╎ │ _FocusScope ← Semantics ← Focus-[GlobalObjectKey

I/flutter ( 6559): ╎ │ MaterialPageRoute(875520219)] ← _ModalScope-[GlobalKey

I/flutter ( 6559): ╎ │ 816151164] ← _OverlayEntry-[GlobalKey 727622716] ← Stack ←

I/flutter ( 6559): ╎ │ _Theatre ← ⋯

I/flutter ( 6559): ╎ │ parentData:

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │ translation: Offset(0.0, 0.0)

I/flutter ( 6559): ╎ │ transformHitTests: true

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child: RenderRepaintBoundary

I/flutter ( 6559): ╎ │ creator: RepaintBoundary ← FractionalTranslation ←

I/flutter ( 6559): ╎ │ SlideTransition ← _MountainViewPageTransition ← IgnorePointer ←

I/flutter ( 6559): ╎ │ Offstage ← _FocusScope ← Semantics ← Focus-[GlobalObjectKey

I/flutter ( 6559): ╎ │ MaterialPageRoute(875520219)] ← _ModalScope-[GlobalKey

I/flutter ( 6559): ╎ │ 816151164] ← _OverlayEntry-[GlobalKey 727622716] ← Stack ← ⋯

I/flutter ( 6559): ╎ │ parentData:

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │ metrics: 83.3% useful (1 bad vs 5 good)

I/flutter ( 6559): ╎ │ diagnosis: this is a useful repaint boundary and should be kept

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child: RenderDecoratedBox

I/flutter ( 6559): ╎ │ creator: DecoratedBox ← Container ← AnimatedContainer ← Material

I/flutter ( 6559): ╎ │ ← AppHome ← _ModalScopeStatus ← PageStorage-[GlobalKey

I/flutter ( 6559): ╎ │ 619728754] ← RepaintBoundary ← FractionalTranslation ←

I/flutter ( 6559): ╎ │ SlideTransition ← _MountainViewPageTransition ← IgnorePointer ←

I/flutter ( 6559): ╎ │ ⋯

I/flutter ( 6559): ╎ │ parentData:

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │ decoration:

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ │ configuration: ImageConfiguration(bundle:

I/flutter ( 6559): ╎ │ PlatformAssetBundle@367106502(), devicePixelRatio: 2.625,

I/flutter ( 6559): ╎ │ platform: android)

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child: RenderDecoratedBox

I/flutter ( 6559): ╎ │ creator: DecoratedBox ← Container ← DecoratedBox ← Container ←

I/flutter ( 6559): ╎ │ AnimatedContainer ← Material ← AppHome ← _ModalScopeStatus ←

I/flutter ( 6559): ╎ │ PageStorage-[GlobalKey 619728754] ← RepaintBoundary ←

I/flutter ( 6559): ╎ │ FractionalTranslation ← SlideTransition ← ⋯

I/flutter ( 6559): ╎ │ parentData:

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │ decoration:

I/flutter ( 6559): ╎ │ backgroundColor: Color(0xfffafafa)

I/flutter ( 6559): ╎ │ configuration: ImageConfiguration(bundle:

I/flutter ( 6559): ╎ │ PlatformAssetBundle@367106502(), devicePixelRatio: 2.625,

I/flutter ( 6559): ╎ │ platform: android)

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child: _RenderInkFeatures

I/flutter ( 6559): ╎ │ creator: _InkFeature-[GlobalKey ink renderer] ←

I/flutter ( 6559): ╎ │ NotificationListener ← DecoratedBox

I/flutter ( 6559): ╎ │ ← Container ← DecoratedBox ← Container ← AnimatedContainer ←

I/flutter ( 6559): ╎ │ Material ← AppHome ← _ModalScopeStatus ← PageStorage-[GlobalKey

I/flutter ( 6559): ╎ │ 619728754] ← RepaintBoundary ← ⋯

I/flutter ( 6559): ╎ │ parentData:

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child: RenderPositionedBox

I/flutter ( 6559): ╎ │ creator: Center ← DefaultTextStyle ← AnimatedDefaultTextStyle ←

I/flutter ( 6559): ╎ │ _InkFeature-[GlobalKey ink renderer] ←

I/flutter ( 6559): ╎ │ NotificationListener ← DecoratedBox

I/flutter ( 6559): ╎ │ ← Container ← DecoratedBox ← Container ← AnimatedContainer ←

I/flutter ( 6559): ╎ │ Material ← AppHome ← ⋯

I/flutter ( 6559): ╎ │ parentData:

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(w=411.4, h=683.4)

I/flutter ( 6559): ╎ │ size: Size(411.4, 683.4)

I/flutter ( 6559): ╎ │ alignment: Alignment.center

I/flutter ( 6559): ╎ │ widthFactor: expand

I/flutter ( 6559): ╎ │ heightFactor: expand

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child: RenderConstrainedBox relayoutBoundary=up1

I/flutter ( 6559): ╎ │ creator: ConstrainedBox ← MaterialButton ← FlatButton ← Center ←

I/flutter ( 6559): ╎ │ DefaultTextStyle ← AnimatedDefaultTextStyle ←

I/flutter ( 6559): ╎ │ _InkFeature-[GlobalKey ink renderer] ←

I/flutter ( 6559): ╎ │ NotificationListener ← DecoratedBox

I/flutter ( 6559): ╎ │ ← Container ← DecoratedBox ← Container ← ⋯

I/flutter ( 6559): ╎ │ parentData: offset=Offset(156.7, 323.7)

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(0.0<=w<=411.4, 0.0<=h<=683.4)

I/flutter ( 6559): ╎ │ size: Size(98.0, 36.0)

I/flutter ( 6559): ╎ │ additionalConstraints: BoxConstraints(88.0<=w<=Infinity, h=36.0)

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child: RenderSemanticsGestureHandler relayoutBoundary=up2

I/flutter ( 6559): ╎ │ creator: _GestureSemantics ← RawGestureDetector ← GestureDetector

I/flutter ( 6559): ╎ │ ← InkWell ← IconTheme ← DefaultTextStyle ←

I/flutter ( 6559): ╎ │ AnimatedDefaultTextStyle ← ConstrainedBox ← MaterialButton ←

I/flutter ( 6559): ╎ │ FlatButton ← Center ← DefaultTextStyle ← ⋯

I/flutter ( 6559): ╎ │ parentData:

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(88.0<=w<=411.4, h=36.0)

I/flutter ( 6559): ╎ │ size: Size(98.0, 36.0)

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child: RenderPointerListener relayoutBoundary=up3

I/flutter ( 6559): ╎ │ creator: Listener ← _GestureSemantics ← RawGestureDetector ←

I/flutter ( 6559): ╎ │ GestureDetector ← InkWell ← IconTheme ← DefaultTextStyle ←

I/flutter ( 6559): ╎ │ AnimatedDefaultTextStyle ← ConstrainedBox ← MaterialButton ←

I/flutter ( 6559): ╎ │ FlatButton ← Center ← ⋯

I/flutter ( 6559): ╎ │ parentData:

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(88.0<=w<=411.4, h=36.0)

I/flutter ( 6559): ╎ │ size: Size(98.0, 36.0)

I/flutter ( 6559): ╎ │ behavior: opaque

I/flutter ( 6559): ╎ │ listeners: down

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child: RenderPadding relayoutBoundary=up4

I/flutter ( 6559): ╎ │ creator: Padding ← Container ← Listener ← _GestureSemantics ←

I/flutter ( 6559): ╎ │ RawGestureDetector ← GestureDetector ← InkWell ← IconTheme ←

I/flutter ( 6559): ╎ │ DefaultTextStyle ← AnimatedDefaultTextStyle ← ConstrainedBox ←

I/flutter ( 6559): ╎ │ MaterialButton ← ⋯

I/flutter ( 6559): ╎ │ parentData:

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(88.0<=w<=411.4, h=36.0)

I/flutter ( 6559): ╎ │ size: Size(98.0, 36.0)

I/flutter ( 6559): ╎ │ padding: EdgeInsets(16.0, 0.0, 16.0, 0.0)

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child: RenderPositionedBox relayoutBoundary=up5

I/flutter ( 6559): ╎ │ creator: Center ← Padding ← Container ← Listener ←

I/flutter ( 6559): ╎ │ _GestureSemantics ← RawGestureDetector ← GestureDetector ←

I/flutter ( 6559): ╎ │ InkWell ← IconTheme ← DefaultTextStyle ←

I/flutter ( 6559): ╎ │ AnimatedDefaultTextStyle ← ConstrainedBox ← ⋯

I/flutter ( 6559): ╎ │ parentData: offset=Offset(16.0, 0.0)

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(56.0<=w<=379.4, h=36.0)

I/flutter ( 6559): ╎ │ size: Size(66.0, 36.0)

I/flutter ( 6559): ╎ │ alignment: Alignment.center

I/flutter ( 6559): ╎ │ widthFactor: 1.0

I/flutter ( 6559): ╎ │ heightFactor: expand

I/flutter ( 6559): ╎ │

I/flutter ( 6559): ╎ └─child: RenderParagraph relayoutBoundary=up6

I/flutter ( 6559): ╎ │ creator: RichText ← Text ← Center ← Padding ← Container ←

I/flutter ( 6559): ╎ │ Listener ← _GestureSemantics ← RawGestureDetector ←

I/flutter ( 6559): ╎ │ GestureDetector ← InkWell ← IconTheme ← DefaultTextStyle ← ⋯

I/flutter ( 6559): ╎ │ parentData: offset=Offset(0.0, 10.0)

I/flutter ( 6559): ╎ │ constraints: BoxConstraints(0.0<=w<=379.4, 0.0<=h<=36.0)

I/flutter ( 6559): ╎ │ size: Size(66.0, 16.0)

I/flutter ( 6559): ╎ ╘═╦══ text ═══

I/flutter ( 6559): ╎ ║ TextSpan:

I/flutter ( 6559): ╎ ║ inherit: false

I/flutter ( 6559): ╎ ║ color: Color(0xdd000000)

I/flutter ( 6559): ╎ ║ family: "Roboto"

I/flutter ( 6559): ╎ ║ size: 14.0

I/flutter ( 6559): ╎ ║ weight: 500

I/flutter ( 6559): ╎ ║ baseline: alphabetic

I/flutter ( 6559): ╎ ║ "Dump App"

I/flutter ( 6559): ╎ ╚═══════════

I/flutter ( 6559): ╎

I/flutter ( 6559): └╌no offstage children

這(zhè)是根RenderObject對(duì)象的toStringDeep函數的輸出。

當調試布局問題時(shí),關鍵要看(kàn)的是size和(hé)constraints字段。約束沿着樹向下(xià)傳遞,尺寸向上(shàng)傳遞。

例如,在上(shàng)面的轉儲中,您可以看(kàn)到(dào)窗口大(dà)小(xiǎo),Size(411.4, 683.4),它用(yòng)于強制RenderPositionedBox下(xià)的所有渲染框到(dào)屏幕的大(dà)小(xiǎo), 約束條件爲 BoxConstraints(w=411.4, h=683.4)。從(cóng)RenderPositionedBox的轉儲中看(kàn)到(dào)是由Center widget創建的(如creator字段所描述的), 設置其孩子的約束爲:BoxConstraints(0.0<=w<=411.4,0.0<=h<=683.4)。一個子widget RenderPadding進一步插入這(zhè)些(xiē)約束以添加填充空(kōng)間,padding值爲EdgeInsets(16.0, 0.0, 16.0, 0.0),因此RenderConstrainedBox具有約束BoxConstraints(0.0<=w<=395.4, 0.0<=h<=667.4)。該creator字段告訴我們的這(zhè)個對(duì)象可能(néng)是其FlatButton定義的一部分,它在其内容上(shàng)設置最小(xiǎo)寬度爲88像素,并且設置高(gāo)度爲36.0像素(這(zhè)是Material Design設計(jì)規範中FlatButton類的尺寸标準)。

最内部RenderPositionedBox再次松開(kāi)約束,這(zhè)次是将按鈕中的文(wén)本居中。 在RenderParagraph中基于它的内容來(lái)決定其大(dà)小(xiǎo)。 如果您現(xiàn)在按照size鏈繼續往下(xià)查看(kàn),您會(huì)看(kàn)到(dào)文(wén)本的大(dà)小(xiǎo)是如何影響其按鈕的框的寬度的,它們都是根據孩子的尺寸自(zì)行調整大(dà)小(xiǎo)。

另一種需要注意的是每個盒子描述的”relayoutSubtreeRoot”部分,它告訴你(nǐ)有多少祖先以某種方式依賴于這(zhè)個元素的大(dà)小(xiǎo)。 因此,RenderParagraph有一個relayoutSubtreeRoot=up8,這(zhè)意味着當它RenderParagraph被标及爲”dirty”時(shí),它的八個祖先也(yě)必須被标記爲”dirty”,因爲它們可能(néng)受到(dào)新尺寸的影響。

如果您編寫自(zì)己的渲染對(duì)象,則可以通過覆蓋debugFillProperties()将信息添加到(dào)轉儲。 将DiagnosticsProperty對(duì)象作(zuò)爲方法的參數,并調用(yòng)父類方法。

如果您嘗試調試合成問題,則可以使用(yòng)debugDumpLayerTree()。對(duì)于上(shàng)面的例子,它會(huì)輸出:

I/flutter : TransformLayer

I/flutter : │ creator: [root]

I/flutter : │ offset: Offset(0.0, 0.0)

I/flutter : │ transform:

I/flutter : │ [0] 3.5,0.0,0.0,0.0

I/flutter : │ [1] 0.0,3.5,0.0,0.0

I/flutter : │ [2] 0.0,0.0,1.0,0.0

I/flutter : │ [3] 0.0,0.0,0.0,1.0

I/flutter : │

I/flutter : ├─child 1: OffsetLayer

I/flutter : │ │ creator: RepaintBoundary ← _FocusScope ← Semantics ← Focus-[GlobalObjectKey MaterialPageRoute(560156430)] ← _ModalScope-[GlobalKey 328026813] ← _OverlayEntry-[GlobalKey 388965355] ← Stack ← Overlay-[GlobalKey 625702218] ← Navigator-[GlobalObjectKey _MaterialAppState(859106034)] ← Title ← ⋯

I/flutter : │ │ offset: Offset(0.0, 0.0)

I/flutter : │ │

I/flutter : │ └─child 1: PictureLayer

I/flutter : │

I/flutter : └─child 2: PictureLayer

這(zhè)是根Layer的toStringDeep輸出的。

根部的變換是應用(yòng)設備像素比的變換; 在這(zhè)種情況下(xià),每個邏輯像素代表3.5個設備像素。

RepaintBoundary widget在渲染樹的層中創建了(le)一個RenderRepaintBoundary。這(zhè)用(yòng)于減少需要重繪的需求量。

語義

您還可以調用(yòng)debugDumpSemanticsTree()獲取語義樹(呈現(xiàn)給系統可訪問性API的樹)的轉儲。 要使用(yòng)此功能(néng),必須首先啓用(yòng)輔助功能(néng),例如啓用(yòng)系統輔助工(gōng)具或Semanticswindow (下(xià)面讨論)。

對(duì)于上(shàng)面的例子,它會(huì)輸出:

I/flutter : SemanticsNode(0; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))

I/flutter : ├SemanticsNode(1; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))

I/flutter : │ └SemanticsNode(2; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4); canBeTapped)

I/flutter : └SemanticsNode(3; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))

I/flutter : └SemanticsNode(4; Rect.fromLTRB(0.0, 0.0, 82.0, 36.0); canBeTapped; "Dump App")

調度

要找出相對(duì)于幀的開(kāi)始/結束事(shì)件發生的位置,可以切換debugPrintBeginFrameBanner和(hé)debugPrintEndFrameBanner布爾值以将幀的開(kāi)始和(hé)結束打印到(dào)控制台。

例如:

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還可以用(yòng)來(lái)打印導緻當前幀被調度的調用(yòng)堆棧。

可視(shì)化調試

您也(yě)可以通過設置debugPaintSizeEnabled爲true以可視(shì)方式調試布局問題。 這(zhè)是來(lái)自(zì)rendering庫的布爾值。它可以在任何時(shí)候啓用(yòng),并在爲true時(shí)影響繪制。 設置它的最簡單方法是在void main()的頂部設置。

當它被啓用(yòng)時(shí),所有的盒子都會(huì)得到(dào)一個明(míng)亮(liàng)的深青色邊框,padding(來(lái)自(zì)widget如Padding)顯示爲淺藍色,子widget周圍有一個深藍色框, 對(duì)齊方式(來(lái)自(zì)widget如Center和(hé)Align)顯示爲黃色箭頭. 空(kōng)白(bái)(如沒有任何子節點的Container)以灰色顯示。

debugPaintBaselinesEnabled做了(le)類似的事(shì)情,但(dàn)對(duì)于具有基線的對(duì)象,文(wén)字基線以綠色顯示,表意(ideographic)基線以橙色顯示。

debugPaintPointersEnabled标志打開(kāi)一個特殊模式,任何正在點擊的對(duì)象都會(huì)以深青色突出顯示。 這(zhè)可以幫助您确定某個對(duì)象是否以某種不正确地方式進行hit測試(Flutter檢測點擊的位置是否有能(néng)響應用(yòng)戶操作(zuò)的widget),例如,如果它實際上(shàng)超出了(le)其父項的範圍,首先不會(huì)考慮通過hit測試。

如果您嘗試調試合成圖層,例如以确定是否以及在何處添加RepaintBoundary widget,則可以使用(yòng)debugPaintLayerBordersEnabled 标志, 該标志用(yòng)橙色或輪廓線标出每個層的邊界,或者使用(yòng)debugRepaintRainbowEnabled标志, 隻要他(tā)們重繪時(shí),這(zhè)會(huì)使該層被一組旋轉色所覆蓋。

所有這(zhè)些(xiē)标志隻能(néng)在調試模式下(xià)工(gōng)作(zuò)。通常,Flutter框架中以“debug...” 開(kāi)頭的任何内容都隻能(néng)在調試模式下(xià)工(gōng)作(zuò)。

調試動畫(huà)

調試動畫(huà)最簡單的方法是減慢它們的速度。爲此,請(qǐng)将timeDilation變量(在scheduler庫中)設置爲大(dà)于1.0的數字,例如50.0。 最好(hǎo)在應用(yòng)程序啓動時(shí)隻設置一次。如果您在運行中更改它,尤其是在動畫(huà)運行時(shí)将其值減小(xiǎo),則框架的觀察時(shí)可能(néng)會(huì)倒退,這(zhè)可能(néng)會(huì)導緻斷言并且通常會(huì)幹擾您的工(gōng)作(zuò)。

調試性能(néng)問題

要了(le)解您的應用(yòng)程序導緻重新布局或重新繪制的原因,您可以分别設置debugPrintMarkNeedsLayoutStacks和(hé) debugPrintMarkNeedsPaintStacks标志。 每當渲染盒被要求重新布局和(hé)重新繪制時(shí),這(zhè)些(xiē)都會(huì)将堆棧跟蹤記錄到(dào)控制台。如果這(zhè)種方法對(duì)您有用(yòng),您可以使用(yòng)services庫中的debugPrintStack()方法按需打印堆棧痕迹。

衡量應用(yòng)啓動時(shí)間

要收集有關Flutter應用(yòng)程序啓動所需時(shí)間的詳細信息,可以在運行flutter run時(shí)使用(yòng)trace-startup和(hé)profile選項。

$ flutter run --trace-startup --profile

跟蹤輸出保存爲start_up_info.json,在Flutter工(gōng)程目錄在build目錄下(xià)。輸出列出了(le)從(cóng)應用(yòng)程序啓動到(dào)這(zhè)些(xiē)跟蹤事(shì)件(以微秒捕獲)所用(yòng)的時(shí)間:

進入Flutter引擎時(shí).

展示應用(yòng)第一幀時(shí).

初始化Flutter框架時(shí).

完成Flutter框架初始化時(shí).

如 :

{

"engineEnterTimestampMicros": 96025565262,

"timeToFirstFrameMicros": 2171978,

"timeToFrameworkInitMicros": 514585,

"timeAfterFrameworkInitMicros": 1657393

}

跟蹤Dart代碼性能(néng)

要執行自(zì)定義性能(néng)跟蹤和(hé)測量Dart任意代碼段的wall/CPU時(shí)間(類似于在Android上(shàng)使用(yòng)systrace)。 使用(yòng)dart:developer的Timeline工(gōng)具來(lái)包含你(nǐ)想測試的代碼塊,例如:

Timeline.startSync('interesting function');

// iWonderHowLongThisTakes();

Timeline.finishSync();

然後打開(kāi)你(nǐ)應用(yòng)程序的Observatory timeline頁面,在”Recorded Streams”中選擇’Dart’複選框,并執行你(nǐ)想測量的功能(néng)。

刷新頁面将在Chrome的跟蹤工(gōng)具中顯示應用(yòng)按時(shí)間順序排列的timeline記錄。

請(qǐng)确保運行flutter run時(shí)帶有--profile标志,以确保運行時(shí)性能(néng)特征與您的最終産品差異最小(xiǎo)。

Performance Overlay

要獲得應用(yòng)程序性能(néng)圖,請(qǐng)将MaterialApp構造函數的showPerformanceOverlay參數設置爲true。 WidgetsApp構造函數也(yě)有類似的參數(如果你(nǐ)沒有使用(yòng)MaterialApp或者WidgetsApp,你(nǐ)可以通過将你(nǐ)的應用(yòng)程序包裝在一個stack中, 并将一個widget放(fàng)在通過new PerformanceOverlay.allEnabled()創建的stack上(shàng)來(lái)獲得相同的效果)。

這(zhè)将顯示兩個圖表。第一個是GPU線程花(huā)費的時(shí)間,最後一個是CPU線程花(huā)費的時(shí)間。 圖中的白(bái)線以16ms增量沿縱軸顯示; 如果圖中超過這(zhè)三條線之一,那麽您的運行頻率低(dī)于60Hz。橫軸代表幀。 該圖僅在應用(yòng)程序繪制時(shí)更新,因此如果它處于空(kōng)閑狀态,該圖将停止移動。

這(zhè)應該始終在發布模式(release mode)下(xià)測試,因爲在調試模式下(xià),故意犧牲性能(néng)來(lái)換取有助于開(kāi)發調試的功能(néng),如assert聲明(míng),這(zhè)些(xiē)都是非常耗時(shí)的,因此結果将會(huì)産生誤導。

Material grid

在開(kāi)發實現(xiàn)Material Design的應用(yòng)程序時(shí), 将Material Design基線網格覆蓋在應用(yòng)程序上(shàng)可能(néng)有助于驗證對(duì)齊。 爲此,MaterialApp 構造函數 有一個debugShowMaterialGrid參數, 當在調試模式設置爲true時(shí),它将覆蓋這(zhè)樣一個網格。

您也(yě)可以直接使用(yòng)GridPaperwidget将這(zhè)種網格覆蓋在非Material應用(yòng)程序上(shàng) 。

網站(zhàn)建設開(kāi)發|APP設計(jì)開(kāi)發|小(xiǎo)程序建設開(kāi)發
下(xià)一篇:Flutter 檢查用(yòng)戶界面
上(shàng)一篇:Flutter 測試應用(yòng)