做(zuò)自(zì)由與創造的先行(xíng)者

Flutter 使用熱(rè)重載

Flutter開(kāi)發手冊

使用熱(rè)重載

Flutter的熱(rè)重載(hot reload)功能(néng)可(kě)以幫助您在無需重新啓動應用的情況下(xià)快(kuài)速、輕松地(dì)進行(xíng)測試、構建用戶界面、添加功能(néng)以及修複錯誤。 通(tōng)過将更新後的源代碼文件注入正在運行(xíng)的Dart虛拟機(VM)中來實現熱(rè)重載。在虛拟機使用新的的字段和(hé)函數(shù)更新類後,Flutter框架會自(zì)動重新構建widget樹(shù),以便您快(kuài)速查看(kàn)更改的效果。

要(yào)熱(rè)重載一(yī)個Flutter應用程序:

從(cóng)受支持的IntelliJ IDE或終端窗口運行(xíng)應用程序。物(wù)理機或虛拟器都(dōu)可(kě)以運行(xíng)。

修改項目中的一(yī)個Dart文件。大多數(shù)類型的代碼更改可(kě)以重新加載;

如(rú)果您使用的是IntelliJ IDE,請(qǐng)選擇Save All (cmd-s/ctrl-s)),或者單擊工(gōng)具欄上(shàng)的Hot Reload按鈕。alt_text

如(rú)果您正在使用命令行(xíng)flutter run運行(xíng)應用程序,請(qǐng)在終端窗口輸入r

成功執行(xíng)熱(rè)重載後,您将在控制台中看(kàn)到類似于以下(xià)內(nèi)容的消息:

Performing hot reload...

Reloaded 1 of 448 libraries in 2,777ms.

應用程序已更新以反映您的更改,并且該應用程序的當前狀态(以上(shàng)示例中的計數(shù)器變量的值)将保留。您的應用程序将繼續執行(xíng)之前運行(xíng)熱(rè)重載命令的位置。代碼被更新并繼續執行(xíng)。

A code change has a visible effect only if the modified Dart code is run again after the change. The next sections describe common situations where the modified code will not run again after hot reload. In some cases, small changes to the Dart code will enable you to continue using hot reload for your app. 隻有修改後的Dart代碼再次運行(xíng)時(shí),代碼更改才會有效果。接下(xià)來的部分将介紹常見(jiàn)的情況,即修改後的代碼在熱(rè)重載後不會再運行(xíng)。 在某些情況下(xià),對Dart代碼的小改動将使您能(néng)夠繼續為(wèi)您的應用程序使用熱(rè)重新加載。

編譯錯誤

當代碼更改後引入了(le)編譯錯誤時(shí),熱(rè)重載會生成類似于以下(xià)內(nèi)容的錯誤消息

Hot reload was rejected:

'/Users/obiwan/Library/Developer/CoreSimulator/Devices/AC94F0FF-16F7-46C8-B4BF-218B73C547AC/data/Containers/Data/Application/4F72B076-42AD-44A4-A7CF-57D9F93E895E/tmp/ios_testWIDYdS/ios_test/lib/main.dart': warning: line 16 pos 38: unbalanced '{' opens here

Widget build(BuildContext context) {

^

'/Users/obiwan/Library/Developer/CoreSimulator/Devices/AC94F0FF-16F7-46C8-B4BF-218B73C547AC/data/Containers/Data/Application/4F72B076-42AD-44A4-A7CF-57D9F93E895E/tmp/ios_testWIDYdS/ios_test/lib/main.dart': error: line 33 pos 5: unbalanced ')'

);

^

在這(zhè)種情況下(xià),隻需糾正代碼錯誤,就可(kě)以繼續使用熱(rè)重載。

之前的狀态與新代碼結合在一(yī)起

Flutter的熱(rè)重載功能(néng)(有時(shí)稱為(wèi)有_狀态熱(rè)重載_)可(kě)保留您的應用程序的狀态。這(zhè)種設計使您隻能(néng)查看(kàn)最近更改的效果,而不會丢棄當前狀态。 例如(rú),如(rú)果您的應用需要(yào)用戶登錄,則可(kě)以在導航層次結構中向下(xià)幾個級别修改并重新加載頁面,而無需重新輸入登錄憑據。狀态保持不變,這(zhè)通(tōng)常是期望的行(xíng)為(wèi)。

如(rú)果代碼更改會影響應用程序(或其依賴)的狀态,則應用程序必須使用的數(shù)據可(kě)能(néng)與它從(cóng)頭開(kāi)始執行(xíng)的數(shù)據不完全一(yī)緻。在熱(rè)重載和(hé)完全重啓之後,結果可(kě)能(néng)是不同的行(xíng)為(wèi)。 例如(rú),如(rú)果您将某個StatelessWidget類改為(wèi)StatefulWidget(或相反),則在熱(rè)重載之後,應用程序的以前狀态将保留。但(dàn)是,該狀态可(kě)能(néng)與新的更改不兼容。

考慮下(xià)面的代碼:

class myWidget extends StatelessWidget {

Widget build(BuildContext context) {

return new GestureDetector(onTap: () => print('T'));

}

}

運行(xíng)該應用程序後,如(rú)果進行(xíng)以下(xià)更改:

class myWidget extends StatefulWidget {

@override

State createState() => new myWidgetState();

}

class myWidgetState {

...

...

}

然後熱(rè)重載,控制台将顯示類似于以下(xià)內(nèi)容的斷言失敗:

myWidget is not a subtype of StatelessWidget

在這(zhè)些情況下(xià),需要(yào)完全重新啓動以查看(kàn)更新的應用程序.

包含最近的代碼更改,但(dàn)排除了(le)應用程序狀态

在Dart中,靜态字段惰性初始化。 這(zhè)意味着你第一(yī)次運行(xíng)一(yī)個Flutter應用程序并讀取一(yī)個靜态字段時(shí),它的初始值設為(wèi)初始表達式的結果。全局變量和(hé)靜态字段被視(shì)為(wèi)狀态,因此在熱(rè)重載期間(jiān)不會重新初始化。

如(rú)果更改全局變量和(hé)靜态字段的初始值設定項,則需要(yào)完全重啓以查看(kàn)更改。例如(rú),請(qǐng)考慮以下(xià)代碼

final sampleTable = [

new Table("T1"),

new Table("T2"),

new Table("T3"),

new Table("T4"),

];

運行(xíng)該應用程序後,如(rú)果進行(xíng)以下(xià)更改:

final sampleTable = [

new Table("T1"),

new Table("T2"),

new Table("T3"),

new Table("T10"), //modified

];

然後熱(rè)重載,這(zhè)個改變并沒有生效

相反,在下(xià)面的例子中:

const foo = 1;

final bar = foo;

void onClick(){

print(foo);

print(bar);

}

第一(yī)次運行(xíng)應用程序打印1和(hé)1。然後,如(rú)果您進行(xíng)以下(xià)更改:

const foo = 2; //modified

final bar = foo;

void onClick(){

print(foo);

print(bar);

}

然後熱(rè)重載,它現在打印2和(hé)1。對const字段值的更改始終會重新加載,但(dàn)不會重新運行(xíng)靜态字段初始值設定語句(初始值可(kě)能(néng)是一(yī)個表達式的值)。 從(cóng)概念上(shàng)講,const字段被視(shì)為(wèi)别名而不是狀态。

Dart VM在一(yī)組更改需要(yào)完全重啓才能(néng)生效的時(shí)候,會檢測初始化程序更改和(hé)标志。上(shàng)述示例中的大部分初始化工(gōng)作(zuò)都(dōu)會觸發标記機制,但(dàn)不适用于以下(xià)情況:

final bar = foo;

要(yào)能(néng)夠在熱(rè)重載後更新和(hé)查看(kàn)foo的更改,請(qǐng)考慮将字段重新定義為(wèi)const或使用getter來返回值,而不是使用final。例如(rú):

const bar = foo;

or:

get bar => foo;

最近的UI更改被排除在外(wài)

即使熱(rè)重載操作(zuò)看(kàn)起來成功了(le)并且沒有抛出異常,但(dàn)某些代碼更改可(kě)能(néng)在刷新的UI中不可(kě)見(jiàn)。這(zhè)種行(xíng)為(wèi)在更改應用程序的main()方法後很(hěn)常見(jiàn)。

作(zuò)為(wèi)一(yī)般規則,如(rú)果修改後的代碼位于根widget的構建方法的下(xià)遊,則熱(rè)重載将按預期運行(xíng)。但(dàn)是,如(rú)果修改後的代碼不會因重建構建widget樹(shù)而重新執行(xíng)的話,那(nà)麽在熱(rè)重載後您将看(kàn)不到其效果。

例如(rú),請(qǐng)考慮以下(xià)代碼:

import 'package:flutter/material.dart';

void main() {

runApp(new MyApp());

}

class MyApp extends StatelessWidget {

Widget build(BuildContext context) {

return new GestureDetector(

onTap: () => print('tapped'));

}

}

運行(xíng)這(zhè)個應用程序後,您可(kě)能(néng)會更改代碼,如(rú)下(xià)所示:

import 'package:flutter/widgets.dart';

void main() {

runApp(

const Center(

child: const Text('Hello', textDirection: TextDirection.ltr)));

}

完全重啓後,程序從(cóng)頭開(kāi)始執行(xíng)新版本main(),并構建一(yī)個widget樹(shù)顯示文本”Hello”。

但(dàn)是,如(rú)果您在此更改後重新加載應用程序,main()則不會重新執行(xíng),并且會使用未修改的實例MyApp作(zuò)為(wèi)新構建的widget樹(shù)的根,熱(rè)重載後結果沒有變化。

限制

您可(kě)能(néng)還會遇到極少(shǎo)數(shù)情況下(xià)根本不支持熱(rè)重載的情況。這(zhè)些包括:

枚舉類型更改為(wèi)常規類或常規類更改為(wèi)枚舉類型。例如(rú),如(rú)果您更改:

enum Color {

red,

green,

blue

}

改為(wèi):

class Color {

Color(this.i, this.j);

final Int i;

final Int j;

}

泛型類型聲明(míng)被修改。例如(rú),如(rú)果您更改:

class A {

T i;

}

改為(wèi):

class A {

T i;

V v;

}

在這(zhè)些情況下(xià),熱(rè)重載會生成診斷消息,并會在未提交任何更改的情況下(xià)失敗。

網站建設開(kāi)發|APP設計開(kāi)發|小程序建設開(kāi)發
下(xià)一(yī)篇:Flutter 測試應用
上(shàng)一(yī)篇:Flutter 使用 Flutter IDE