Flutter 使用熱重載

2020-08-27 14:48 更新

使用熱重載

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

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

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

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

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

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

應(yīng)用程序已更新以反映您的更改,并且該應(yīng)用程序的當前狀態(tài)(以上示例中的計數(shù)器變量的值)將保留。您的應(yīng)用程序?qū)⒗^續(xù)執(zhí)行之前運行熱重載命令的位置。代碼被更新并繼續(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代碼再次運行時,代碼更改才會有效果。接下來的部分將介紹常見的情況,即修改后的代碼在熱重載后不會再運行。 在某些情況下,對Dart代碼的小改動將使您能夠繼續(xù)為您的應(yīng)用程序使用熱重新加載。


編譯錯誤

當代碼更改后引入了編譯錯誤時,熱重載會生成類似于以下內(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 ')'
    );
    ^

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


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

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

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

考慮下面的代碼:

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

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

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

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

myWidget is not a subtype of StatelessWidget

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


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

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

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

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

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

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

然后熱重載,這個改變并沒有生效

相反,在下面的例子中:

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

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

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

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

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

final bar = foo;

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

const bar = foo;

or:

get bar => foo;


最近的UI更改被排除在外

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

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

例如,請考慮以下代碼:

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īng)用程序后,您可能會更改代碼,如下所示:

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

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

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


限制

您可能還會遇到極少數(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;
      }

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

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號