flutter热重载
flutter热重载的原理是:更新运行中的Dart虚拟机中的源代码,然后从小部件的树根开始,每个小部件的build方法都会重新运行。直到整个树被重建,并且新的框架被渲染。
例如,在 Widget build(BuildContext context) 方法中,更改小部件的title或者增加,删除小部件,都可以被重新渲染。
重新运行
如果我们在 main()方法中,修改App的根部件,由MyApp()更换为MyOtherApp()
void main() {
runApp(const MyOtherApp());
}
此时,热重载的还是原来MyApp()的小部件,此时,我们需要重新运行程序。
hot start
当我们修改 build()方法以外的方法,热重载不会检测到,此时我们不需要重新运行,但是需要 hot start。
例如,initState()或者dispose()方法,不是由build()方法调用的,这些方法不会被热重载察觉,增加一些 变量、静态变量也是需要hot start的.
Build Context
当我们使用导航器、媒体查询和各种builder创建器的时候都需要BuildContext
Navigator.of(context);
MediaQuery.of(context);
ListView.builder(itemBuilder: (context, index){});
但BuildContext是什么呢?
Widget是用来记录UI的配置信息和属性的,但它不知道自己和其他小部件的关系。
我们以StatelessWidget为例展开说明。
abstract class StatelessWidget extends Widget {
......
/// Creates a [StatelessElement] to manage this widget's location in the tree.
///
/// It is uncommon for subclasses to override this method.
@override
StatelessElement createElement() => StatelessElement(this);
}
创建Widget的时候,会创建与之对应的Element对象。作用是跟踪管理widget的位置。
Element对象会维护此小部件在运行时的所需的信息
Element对象是Element tree中的一部分,Element tree中的每一个Element都指定代表的widget。
Element是BuildContext的一种。
abstract class Element extends DiagnosticableTree implements BuildContext {
......
}
Widget中的build()方法里面的Build Context实际上是Element对象
@override
Widget build() => widget.build(this);
当我们使用BuildContext时,实际上就是允许我的小部件找出其所在位置的东西,换句话说,为你的小部件构建方法,提供所需的前后关系。
例如: Navigator.of(context)就是,找出小部件当前位置的导航器
无界限宽度/高度
示例
先来看一段代码
Column(
children: [
const Text('Test'),
ListView(
children: [],
)
],
);
如上所示,在这种情况下,当我们在Column中,添加一个ListView,就会得到 Vertical viewport was given unbounded height. 的错误。
Flutter布局
Flutter为了能够高效的确定每个Widget的位置,flutter使用了单通道布局算法:向下传递约束,向上传递大小即父组件向子组件传递约束,子组件向父组件传递大小,父组件决定子组件位置。
当我们在Column中,添加一个ListView时,此时,Column告诉子部件不能超过我本身的大小,ListView告诉 Column,要一个有多大就多大的大小,也就是无限大。
此时,如果Column限制高度,就会造成,ListView充满整个Column,导致Text小部件无法正常显示,很明显,这样的结果不是我们想要的。
总结一下,当在Column中,添加ListView时,为了避免出现无法预期的效果,Flutter官方将这种情况认定为一个无限高度的错误。
详细定义
当遇到这种无限高度或宽度的错误时,我们需要尽可能的详细定义我们的布局。
如果我们想让ListView尽可能的大且能给其他Column的子部件留出一些空间,我们可以将ListView包裹在Expanded或者Flexible小部件中。
Column(
children: [
const Text('Test'),
Flexible(
child: ListView(
children: [],
),
)
],
),
RenderObjects
当我们使用Widget进行布局时,会生成一个Widget Tree和Element Tree,Widget和Element,会共同创建RenderObject,与之对应的会有一个 Render Tree
作用
RenderObject的作用有很多,它处理布局(Layout),绘画(paint),命中测试(Hit Test)和可访问性(Accessability)。
在flutter中都是用Widget来布局的,但在屏幕显示的时候,是使用的RenderObject。
Element Tree使Widget Tree与Render Tree保持同步。大多数情况下Render Tree可以复用,以助于提高性能。
举个例子,如果一个Widget改变了颜色,可以重用和重新绘制相同的RenderObject。如果从树中移除Widget,相应的Element对象将会分离(detach),RenderObject也会分离(detach)。
当我们在Widget Tree中,交换两个小部件时,Flutter使用Key来确定Widget何时移动到新位置,并且决定是否可以重用Element Object和Render Object。
举个例子,在构建阶段,当我们交换两个拥有唯一Key的Widget时,Element Object也会被交换,如果,在Element Tree中,有能与Key匹配的Element Object对象,就会重用该Element对象,并交换。