Hot reload

Flutter的热重载功能可帮助您快速轻松地进行实验,构建UI,添加功能并修复错误. 通过将更新的源代码文件注入到正在运行的Dart虚拟机(VM)中,可以进行热重装. VM使用新版本的字段和函数更新类后,Flutter框架会自动重建小部件树,使您可以快速查看更改的效果.

How to perform a hot reload

要热重新加载Flutter应用,请执行以下操作:

  1. 从受支持的Flutter编辑器或终端窗口中运行该应用程序. 物理设备或虚拟设备都可以成为目标. 只有处于调试模式的Flutter应用才能被热加载.
  2. 修改项目中的Dart文件之一. 大多数类型的代码更改都可以热加载. 有关需要热重启的更改列表,请参见特殊情况 .
  3. 如果在支持Flutter IDE工具的IDE /编辑器中工作,请选择全部保存cmd-s / ctrl-s ),或单击工具栏上的热重载按钮.

    如果您使用flutter run在命令行上运行应用程序,请在终端窗口中输入r .

成功执行热重装操作后,您将在控制台中看到一条类似于以下内容的消息:

Performing hot reload...
Reloaded 1 of 448 libraries in 978ms.

该应用程序会更新以反映您的更改,并且会保留该应用程序的当前状态. 您的应用程序将从运行hot reload命令之前的位置继续执行. 代码更新并继续执行.

Android Studio用户界面
在Android Studio中控制运行,运行调试,热重装和热重启

仅当更改后再次运行修改后的Dart代码时,代码更改才具有可见效果. 具体来说,热重载会导致所有现有的小部件都重建. 仅会自动重新执行与重建小部件有关的代码. 例如, main()initState()函数不会再次运行.

Special cases

下一节将介绍涉及热重装的特定方案. 在某些情况下,只需对Dart代码进行少量更改,就可以继续为应用程序使用热重载. 在其他情况下,需要热重启或完全重启.

An app is killed

当应用被终止时,热重装可能会中断. 例如,如果应用在后台停留的时间过长.

Compilation errors

当代码更改引入编译错误时,热重载会生成类似于以下内容的错误消息:

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

在这种情况下,只需更正Dart代码指定行上的错误即可继续使用热重装.

CupertinoTabView’s builder

热装不会将更改应用于CupertinoTabViewbuilder . 有关更多信息,请参见发行43574 .

Enumerated types

将枚举类型更改为常规类或将常规类更改为枚举类型时,热重载不起作用.

例如:

更改之前:

enum Color {
  red,
  green,
  blue
}

更改后:

class Color {
  Color(this.i, this.j);
  final int i;
  final int j;
}

Changing fonts

热装大部分支持资产变更. 但是,如果更改字体,则需要热重启.

Generic types

修改通用类型声明后,热重装将无法工作. 例如,以下操作将无效:

更改之前:

class A<T> {
  T i;
}

更改后:

class A<T, V> {
  T i;
  V v;
}

Native code

如果您更改了本机代码(例如Kotlin,Java,Swift或Objective-C),则必须执行完全重新启动(停止并重新启动应用程序)才能看到更改生效.

Previous state is combined with new code

Flutter的有状态热重载可保留您应用的状态. 这种方法使您仅查看最新更改的效果,而不会丢弃当前状态. 例如,如果您的应用程序要求用户登录,则可以在导航层次结构中向下几个级别修改并热重新加载页面,而无需重新输入登录凭据. 保持状态,这通常是所需的行为.

如果代码更改影响了应用程序的状态(或其依赖项),则应用程序必须使用的数据可能与从头开始执行的数据不完全一致. 热重加载与热重启后的结果可能是不同的行为.

Recent code change is included but app state is excluded

在Dart中, 静态字段被延迟初始化 . 这意味着您首次运行Flutter应用并读取静态字段时,会将其设置为初始化程序求值到的任何值. 全局变量和静态字段被视为状态,因此在热重载期间不会重新初始化.

如果更改全局变量和静态字段的初始化程序,则必须完全重新启动才能查看更改. 例如,考虑以下代码:

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

运行该应用程序后,进行以下更改:

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

You hot reload, but the change is not reflected.

相反,在以下示例中:

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

首次运行该应用程序将打印11 . 然后,您进行以下更改:

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

虽然对const字段值const更改总是热重新加载,但不会重新运行静态字段初始化程序. 从概念上讲, const字段被视为别名而不是状态.

当一组更改需要热重启才能生效时,Dart VM会检测到初始化程序更改并进行标记. 在上面的示例中,大多数初始化工作都会触发标记机制,但不会发生以下情况:

final bar = foo;

要更新foo并在热重载后查看更改,请考虑将字段重新定义为const或使用getter返回值,而不是使用final . 例如,以下两种解决方案之一均可工作:

const bar = foo;    // Convert foo to a const...
get bar => foo;     // ...or provide a getter.

有关更多信息,请阅读Dart中constfinal关键字之间的区别 .

Recent UI change is excluded

即使热重装操作看起来成功并且没有生成异常,某些代码更改也可能在刷新的UI中不可见. 更改应用程序的main()initState()方法后,此行为很常见.

通常,如果修改后的代码在根窗口小部件的build()方法的下游,则热重装将按预期进行. 但是,如果由于重建小部件树而不会重新执行修改后的代码,那么在热重载后您将看不到它的效果.

例如,考虑以下代码:

import 'package:flutter/material.dart';

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

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

运行此应用程序后,按如下所示更改代码:

import 'package:flutter/widgets.dart';

void main() {
  runApp(const Center(
      child: const Text('Hello', textDirection: TextDirection.ltr)));
}

通过热重启,程序将从头开始启动,执行main()的新版本,并构建一个显示文本Hello的小部件树.

但是,如果在此更改后热重新加载应用程序,则不会重新执行main()initState() ,并且将以MyApp的未更改实例作为根窗口小部件来重建窗口小部件树. 在热重载后,这不会导致可见变化.

How it works

调用热重装时,主机将查看自上次编译以来的已编辑代码. 重新编译以下库:

  • 任何具有更改代码的库
  • 应用程序的主库
  • 主库中的库导致受影响的库

这些库中的源代码被编译成内核文件,然后发送到移动设备的Dart VM.

Dart VM从新的内核文件重新加载所有库. 到目前为止,没有代码被重新执行.

然后,热重新加载机制使Flutter框架触发所有现有小部件和渲染对象的重建/重新布局/重新绘制.