BuildContext 实际就是就是Elements对象, BuildContext的作用是就是组织直接操作Element对象,来自官方的解释:
/// [BuildContext] objects are actually [Element] objects. The [BuildContext]
/// interface is used to discourage direct manipulation of [Element] objects.
通过BuildContext 获取屏幕的大小
print(((context as Element).findRenderObject() as RenderBox).size);
RenderObject
每个Element
都对应一个RenderObject
,我们可以通过Element.renderObject
来获取。RenderObject
的主要职责是Layout和绘制,所有的RenderObject会组成一棵渲染树Render Tree。
RenderBox
Flutter提供了一个类RenderBox,它继承自RenderObject
,布局坐标系统采用笛卡尔坐标系,这和Android和iOS原生坐标系是一致的,都是屏幕的top、left是原点,然后分宽高两个轴,大多数情况下,我们直接使用RenderBox就可以了
AS类型转换
当且仅当 obj
实现了 T
的接口,obj is T
才是 true。例如 obj is Object
总为 true,因为所有类都是 Object 的子类。
仅当你确定这个对象是该类型的时候,你才可以使用 as
操作符可以把对象转换为特定的类型。例如:
(emp as Person).firstName = 'Bob';
如果你不确定这个对象类型是不是 T
,请在转型前使用 is T
检查类型。
if (emp is Person) {
// 类型检查
emp.firstName = 'Bob';
}
你可以使用 as
运算符进行缩写:
(emp as Person).firstName = 'Bob';
上面的例子中findAncestorRenderObjectOfType返回的RenderObject
T? findAncestorRenderObjectOfType<T extends RenderObject>();
而RenderObject是一个接口
abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {
只有具体的实现类 RenderBox有size的属性,通过as的向下转换RenderObject 为 RenderBox,其实就是JAVA通过u前置括号进行类型的转换了
setState
@protected
void setState(VoidCallback fn) {
assert(fn != null);
assert(() {
if (_debugLifecycleState == _StateLifecycle.defunct) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() called after dispose(): $this'),
ErrorDescription(
'This error happens if you call setState() on a State object for a widget that '
'no longer appears in the widget tree (e.g., whose parent widget no longer '
'includes the widget in its build). This error can occur when code calls '
'setState() from a timer or an animation callback.'
),
ErrorHint(
'The preferred solution is '
'to cancel the timer or stop listening to the animation in the dispose() '
'callback. Another solution is to check the "mounted" property of this '
'object before calling setState() to ensure the object is still in the '
'tree.'
),
ErrorHint(
'This error might indicate a memory leak if setState() is being called '
'because another object is retaining a reference to this State object '
'after it has been removed from the tree. To avoid memory leaks, '
'consider breaking the reference to this object during dispose().'
),
]);
}
if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() called in constructor: $this'),
ErrorHint(
'This happens when you call setState() on a State object for a widget that '
"hasn't been inserted into the widget tree yet. It is not necessary to call "
'setState() in the constructor, since the state is already assumed to be dirty '
'when it is initially created.'
),
]);
}
return true;
}());
final Object? result = fn() as dynamic;
assert(() {
if (result is Future) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() callback argument returned a Future.'),
ErrorDescription(
'The setState() method on $this was called with a closure or method that '
'returned a Future. Maybe it is marked as "async".'
),
ErrorHint(
'Instead of performing asynchronous work inside a call to setState(), first '
'execute the work (without updating the widget state), and then synchronously '
'update the state inside a call to setState().'
),
]);
}
// We ignore other types of return values so that you can do things like:
// setState(() => x = 3);
return true;
}());
_element!.markNeedsBuild();
}
setState 里面调用了 _element!.markNeedsBuild()
, 也就是标识为需要重新渲染,所以完全可以直接通过调用BuildContext对象的markNeedsBuild方法来触发视图的更新
void _incrementCounter() {
_counter++;
(context as Element).markNeedsBuild();
}
BuildContext
Theme的静态方法 of 接受 BuildContext 类型对象, 调用BuildContext的实现类Element 对象dependOnInheritedWidgetOfExactType方法在Element树向上查找继承的Theme
通过Context向上进行查过的过程就有点类似于JS的原型链查找方式
static ThemeData of(BuildContext context) {
final _InheritedTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedTheme>();
final MaterialLocalizations? localizations = Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
final ScriptCategory category = localizations?.scriptCategory ?? ScriptCategory.englishLike;
final ThemeData theme = inheritedTheme?.theme.data ?? _kFallbackTheme;
return ThemeData.localize(theme, theme.typography.geometryThemeFor(category));
}