Flutter 使用熱重載

2020-08-27 14:48 更新

使用熱重載

Flutter的熱重載(hot reload)功能可以幫助您在無(wú)需重新啟動(dòng)應(yīng)用的情況下快速、輕松地進(jìn)行測(cè)試、構(gòu)建用戶界面、添加功能以及修復(fù)錯(cuò)誤。 通過(guò)將更新后的源代碼文件注入正在運(yùn)行的Dart虛擬機(jī)(VM)中來(lái)實(shí)現(xiàn)熱重載。在虛擬機(jī)使用新的的字段和函數(shù)更新類后,F(xiàn)lutter框架會(huì)自動(dòng)重新構(gòu)建widget樹(shù),以便您快速查看更改的效果。

要熱重載一個(gè)Flutter應(yīng)用程序:

  1. 從受支持的IntelliJ IDE或終端窗口運(yùn)行應(yīng)用程序。物理機(jī)或虛擬器都可以運(yùn)行。
  2. 修改項(xiàng)目中的一個(gè)Dart文件。大多數(shù)類型的代碼更改可以重新加載; 
  3. 如果您使用的是IntelliJ IDE,請(qǐng)選擇Save All (cmd-s/ctrl-s)),或者單擊工具欄上的Hot Reload按鈕。alt_text

如果您正在使用命令行flutter run運(yùn)行應(yīng)用程序,請(qǐng)?jiān)诮K端窗口輸入r

成功執(zhí)行熱重載后,您將在控制臺(tái)中看到類似于以下內(nèi)容的消息:

Performing hot reload...
Reloaded 1 of 448 libraries in 2,777ms.

應(yīng)用程序已更新以反映您的更改,并且該應(yīng)用程序的當(dāng)前狀態(tài)(以上示例中的計(jì)數(shù)器變量的值)將保留。您的應(yīng)用程序?qū)⒗^續(xù)執(zhí)行之前運(yùn)行熱重載命令的位置。代碼被更新并繼續(xù)執(zhí)行。

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代碼再次運(yùn)行時(shí),代碼更改才會(huì)有效果。接下來(lái)的部分將介紹常見(jiàn)的情況,即修改后的代碼在熱重載后不會(huì)再運(yùn)行。 在某些情況下,對(duì)Dart代碼的小改動(dòng)將使您能夠繼續(xù)為您的應(yīng)用程序使用熱重新加載。


編譯錯(cuò)誤

當(dāng)代碼更改后引入了編譯錯(cuò)誤時(shí),熱重載會(huì)生成類似于以下內(nèi)容的錯(cuò)誤消息

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 ')'
    );
    ^

在這種情況下,只需糾正代碼錯(cuò)誤,就可以繼續(xù)使用熱重載。


之前的狀態(tài)與新代碼結(jié)合在一起

Flutter的熱重載功能(有時(shí)稱為有_狀態(tài)熱重載_)可保留您的應(yīng)用程序的狀態(tài)。這種設(shè)計(jì)使您只能查看最近更改的效果,而不會(huì)丟棄當(dāng)前狀態(tài)。 例如,如果您的應(yīng)用需要用戶登錄,則可以在導(dǎo)航層次結(jié)構(gòu)中向下幾個(gè)級(jí)別修改并重新加載頁(yè)面,而無(wú)需重新輸入登錄憑據(jù)。狀態(tài)保持不變,這通常是期望的行為。

如果代碼更改會(huì)影響應(yīng)用程序(或其依賴)的狀態(tài),則應(yīng)用程序必須使用的數(shù)據(jù)可能與它從頭開(kāi)始執(zhí)行的數(shù)據(jù)不完全一致。在熱重載和完全重啟之后,結(jié)果可能是不同的行為。 例如,如果您將某個(gè)StatelessWidget類改為StatefulWidget(或相反),則在熱重載之后,應(yīng)用程序的以前狀態(tài)將保留。但是,該狀態(tài)可能與新的更改不兼容。

考慮下面的代碼:

class myWidget extends StatelessWidget {
  Widget build(BuildContext context) {
    return new GestureDetector(onTap: () => print('T'));
  }
}

運(yùn)行該應(yīng)用程序后,如果進(jìn)行以下更改:

class myWidget extends StatefulWidget {
  @override
  State createState() => new myWidgetState();
}
class myWidgetState {
...
...
}

然后熱重載,控制臺(tái)將顯示類似于以下內(nèi)容的斷言失敗:

myWidget is not a subtype of StatelessWidget

在這些情況下,需要完全重新啟動(dòng)以查看更新的應(yīng)用程序.


包含最近的代碼更改,但排除了應(yīng)用程序狀態(tài)

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

如果更改全局變量和靜態(tài)字段的初始值設(shè)定項(xiàng),則需要完全重啟以查看更改。例如,請(qǐng)考慮以下代碼

final sampleTable = [
  new Table("T1"),
  new Table("T2"),
  new Table("T3"),
  new Table("T4"),
];

運(yùn)行該應(yīng)用程序后,如果進(jìn)行以下更改:

final sampleTable = [
  new Table("T1"),
  new Table("T2"),
  new Table("T3"),
  new Table("T10"),    //modified
];

然后熱重載,這個(gè)改變并沒(méi)有生效

相反,在下面的例子中:

const foo = 1;
final bar = foo;
void onClick(){
  print(foo);
  print(bar);
}

第一次運(yùn)行應(yīng)用程序打印1和1。然后,如果您進(jìn)行以下更改:

const foo = 2;    //modified
final bar = foo;
void onClick(){
  print(foo);
  print(bar);
}

然后熱重載,它現(xiàn)在打印2和1。對(duì)const字段值的更改始終會(huì)重新加載,但不會(huì)重新運(yùn)行靜態(tài)字段初始值設(shè)定語(yǔ)句(初始值可能是一個(gè)表達(dá)式的值)。 從概念上講,const字段被視為別名而不是狀態(tài)。

Dart VM在一組更改需要完全重啟才能生效的時(shí)候,會(huì)檢測(cè)初始化程序更改和標(biāo)志。上述示例中的大部分初始化工作都會(huì)觸發(fā)標(biāo)記機(jī)制,但不適用于以下情況:

final bar = foo;

要能夠在熱重載后更新和查看foo的更改,請(qǐng)考慮將字段重新定義為const或使用getter來(lái)返回值,而不是使用final。例如:

const bar = foo;

or:

get bar => foo;


最近的UI更改被排除在外

即使熱重載操作看起來(lái)成功了并且沒(méi)有拋出異常,但某些代碼更改可能在刷新的UI中不可見(jiàn)。這種行為在更改應(yīng)用程序的main()方法后很常見(jiàn)。

作為一般規(guī)則,如果修改后的代碼位于根widget的構(gòu)建方法的下游,則熱重載將按預(yù)期運(yùn)行。但是,如果修改后的代碼不會(huì)因重建構(gòu)建widget樹(shù)而重新執(zhí)行的話,那么在熱重載后您將看不到其效果。

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

import 'package:flutter/material.dart';

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  Widget build(BuildContext context) {
    return new GestureDetector(
      onTap: () => print('tapped'));
  }
}

運(yùn)行這個(gè)應(yīng)用程序后,您可能會(huì)更改代碼,如下所示:

import 'package:flutter/widgets.dart';
void main() {
  runApp(
    const Center(
      child: const Text('Hello', textDirection: TextDirection.ltr)));
  }

完全重啟后,程序從頭開(kāi)始執(zhí)行新版本main(),并構(gòu)建一個(gè)widget樹(shù)顯示文本”Hello”。

但是,如果您在此更改后重新加載應(yīng)用程序,main()則不會(huì)重新執(zhí)行,并且會(huì)使用未修改的實(shí)例MyApp作為新構(gòu)建的widget樹(shù)的根,熱重載后結(jié)果沒(méi)有變化。


限制

您可能還會(huì)遇到極少數(shù)情況下根本不支持熱重載的情況。這些包括:

  • 枚舉類型更改為常規(guī)類或常規(guī)類更改為枚舉類型。例如,如果您更改: 
      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;
      }

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

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)