熱重載
Flutter 的熱重載功能可協助您快速輕鬆地進行實驗、建構 UI、新增功能和修正錯誤。熱重載的運作方式是將更新後的原始碼檔案注入正在執行的 Dart 虛擬機器 (VM) 中。在 VM 使用新版本的欄位和函式更新類別之後,Flutter 架構會自動重建 widget 樹,讓您能夠快速檢視變更的效果。
如何執行熱重載
#要熱重載 Flutter 應用程式
從支援的 Flutter 編輯器或終端機視窗執行應用程式。實體或虛擬裝置都可以是目標。只有除錯模式中的 Flutter 應用程式才能進行熱重載或熱重新啟動。
修改專案中的其中一個 Dart 檔案。大多數類型的程式碼變更都可以熱重載;如需需要熱重新啟動的變更清單,請參閱特殊情況。
如果您正在使用支援 Flutter IDE 工具的 IDE/編輯器,請選取全部儲存 (
cmd-s
/ctrl-s
),或按一下工具列上的熱重載按鈕。如果您正在使用
flutter run
在命令列中執行應用程式,請在終端機視窗中輸入r
。
成功執行熱重載操作後,您會在主控台中看到類似如下的訊息
Performing hot reload...
Reloaded 1 of 448 libraries in 978ms.
應用程式會更新以反映您的變更,並且會保留應用程式的目前狀態。您的應用程式會從執行熱重載命令之前的位置繼續執行。程式碼會更新,執行會繼續。
Android Studio 中的執行、執行除錯、熱重載和熱重新啟動控制項
只有在變更後重新執行修改後的 Dart 程式碼時,程式碼變更才會產生可見的效果。具體來說,熱重載會導致所有現有的 widget 重建。只有參與重建 widget 的程式碼會自動重新執行。例如,main()
和 initState()
函式不會再次執行。
特殊情況
#接下來的章節將說明涉及熱重載的特定情境。在某些情況下,對 Dart 程式碼進行小幅變更可讓您繼續將熱重載用於應用程式。在其他情況下,則需要熱重新啟動或完整重新啟動。
應用程式被終止
#當應用程式被終止時,熱重載可能會中斷。例如,如果應用程式在背景中停留太久。
編譯錯誤
#當程式碼變更導致編譯錯誤時,熱重載會產生類似如下的錯誤訊息
Hot reload was rejected:
'/path/to/project/lib/main.dart': warning: line 16 pos 38: unbalanced '{' opens here
Widget build(BuildContext context) {
^
'/path/to/project/lib/main.dart': error: line 33 pos 5: unbalanced ')'
);
^
在這種情況下,只要修正 Dart 程式碼中指定行的錯誤,即可繼續使用熱重載。
CupertinoTabView 的 builder
#熱重載不會套用對 CupertinoTabView
的 builder
所做的變更。如需詳細資訊,請參閱 Issue 43574。
枚舉類型
#當枚舉類型變更為一般類別,或一般類別變更為枚舉類型時,熱重載將無法運作。
例如
變更之前
enum Color {
red,
green,
blue,
}
變更之後
class Color {
Color(this.i, this.j);
final int i;
final int j;
}
泛型類型
#修改泛型類型宣告時,熱重載將無法運作。例如,下列方式將無法運作
變更之前
class A<T> {
T? i;
}
變更之後
class A<T, V> {
T? i;
V? v;
}
原生程式碼
#如果您已變更原生程式碼(例如 Kotlin、Java、Swift 或 Objective-C),則必須執行完整重新啟動(停止並重新啟動應用程式)才能看到變更生效。
先前的狀態與新程式碼合併
#Flutter 的具狀態熱重載會保留應用程式的狀態。此方法可讓您只檢視最近變更的效果,而不會捨棄目前的狀態。例如,如果您的應用程式需要使用者登入,您可以修改並熱重載導覽階層中較下層的頁面,而無需重新輸入您的登入憑證。狀態會保留,這通常是預期的行為。
如果程式碼變更影響應用程式的狀態(或其依附元件),則應用程式必須處理的資料可能與從頭執行時的資料不完全一致。熱重載之後的行為可能與熱重新啟動不同。
包含最近的程式碼變更,但不包含應用程式狀態
#在 Dart 中,靜態欄位會延遲初始化。這表示當您第一次執行 Flutter 應用程式且讀取靜態欄位時,它會設定為其初始化程式所評估的值。全域變數和靜態欄位會被視為狀態,因此不會在熱重載期間重新初始化。
如果您變更全域變數和靜態欄位的初始化程式,則必須熱重新啟動或重新啟動持有初始化程式的狀態,才能看到變更。例如,請考慮下列程式碼
final sampleTable = [
Table(
children: const [
TableRow(
children: [Text('T1')],
)
],
),
Table(
children: const [
TableRow(
children: [Text('T2')],
)
],
),
Table(
children: const [
TableRow(
children: [Text('T3')],
)
],
),
Table(
children: const [
TableRow(
children: [Text('T4')],
)
],
),
];
執行應用程式後,您會進行下列變更
final sampleTable = [
Table(
children: const [
TableRow(
children: [Text('T1')],
)
],
),
Table(
children: const [
TableRow(
children: [Text('T2')],
)
],
),
Table(
children: const [
TableRow(
children: [Text('T3')],
)
],
),
Table(
children: const [
TableRow(
children: [Text('T10')], // modified
)
],
),
];
您進行熱重載,但變更未反映出來。
相反地,在下列範例中
const foo = 1;
final bar = foo;
void onClick() {
print(foo);
print(bar);
}
第一次執行應用程式會列印 1
和 1
。然後,您會進行下列變更
const foo = 2; // modified
final bar = foo;
void onClick() {
print(foo);
print(bar);
}
雖然對 const
欄位值的變更一律會熱重載,但不會重新執行靜態欄位初始化程式。從概念上來說,const
欄位會被視為別名而不是狀態。
Dart VM 會偵測初始化程式變更,並在需要熱重新啟動才能使一組變更生效時加以標示。標示機制會針對上述範例中的大多數初始化工作觸發,但對於以下情況則不會觸發
final bar = foo;
若要更新 foo
並在熱重載後檢視變更,請考慮將欄位重新定義為 const
或使用 getter 來傳回值,而不是使用 final
。例如,下列任一解決方案都適用
const foo = 1;
const bar = foo; // Convert foo to a const...
void onClick() {
print(foo);
print(bar);
}
const foo = 1;
int get bar => foo; // ...or provide a getter.
void onClick() {
print(foo);
print(bar);
}
如需詳細資訊,請閱讀關於 Dart 中 const
和 final
關鍵字之間差異的資訊。
排除最近的 UI 變更
#即使熱重載操作看似成功且未產生任何例外狀況,某些程式碼變更可能在重新整理的 UI 中不可見。這種行為在變更應用程式的 main()
或 initState()
方法之後很常見。
一般來說,如果修改後的程式碼位於根 widget 的 build()
方法的下游,則熱重載會如預期般運作。但是,如果修改後的程式碼不會因重建 widget 樹而重新執行,則您在熱重載後將看不到其效果。
例如,請考慮下列程式碼
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GestureDetector(onTap: () => print('tapped'));
}
}
執行此應用程式後,請依如下方式變更程式碼
import 'package:flutter/widgets.dart';
void main() {
runApp(const Center(child: Text('Hello', textDirection: TextDirection.ltr)));
}
透過熱重新啟動,程式會從頭開始,執行新版本的 main()
,並建構顯示文字 Hello
的 widget 樹。
但是,如果您在此變更後熱重載應用程式,則不會重新執行 main()
和 initState()
,並且 widget 樹會使用未變更的 MyApp
執行個體重建為根 widget。這會導致熱重載後沒有可見的變更。
運作方式
#當叫用熱重載時,主機將會查看自上次編譯以來已編輯的程式碼。將會重新編譯下列程式庫
- 具有已變更程式碼的任何程式庫
- 應用程式的主要程式庫
- 從主要程式庫導向受影響程式庫的程式庫
這些程式庫中的原始碼會編譯為 核心檔案,並傳送到行動裝置的 Dart VM。
Dart VM 會從新的核心檔案重新載入所有程式庫。到目前為止,沒有任何程式碼重新執行。
然後,熱重載機制會導致 Flutter 架構觸發所有現有 widget 和轉譯物件的重建/重新配置/重新繪製。
除非另有說明,否則本網站上的文件反映的是 Flutter 的最新穩定版本。頁面上次更新時間為 2024-05-05。 檢視來源 或 回報問題。