为什么 下面代码Text 能够从 Material获取textStyle
Material(
textStyle: TextStyle(fontSize: 31, color: Colors.red),
child: Text('materical 中的style'),
),
1、 先看下Text 获取 textStyle:
class Text extends StatelessWidget {
Widget build(BuildContext context) {
final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context); //
TextStyle? effectiveTextStyle = style;
if (style == null || style!.inherit) {
effectiveTextStyle = defaultTextStyle.style.merge(style);
}
}
}
这里使用 DefaultTextStyle
DefaultTextStyle.of(context);实现:
DefaultTextStyle 是一个 InheritedWidget
class DefaultTextStyle extends InheritedTheme {
static DefaultTextStyle of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<DefaultTextStyle>() ?? const DefaultTextStyle.fallback();
}
}
InheritedTheme 就是一个 InheritedWidget:
abstract class InheritedTheme extends InheritedWidget
context.dependOnInheritedWidgetOfExactType() 方法的实现为 Element中,因为 context就是一个 Element,dependOnInheritedWidgetOfExactType实现如下:
abstract class Element extends DiagnosticableTree implements BuildContext {
@override
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
if (ancestor != null) {
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
}
2、在看下Material
Material
class Material extends StatefulWidget {
State<Material> createState() => _MaterialState();
}
class _MaterialState extends State<Material> with TickerProviderStateMixin {
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
///...
Widget? contents = widget.child;
if (contents != null) {
//
contents = AnimatedDefaultTextStyle(
style: widget.textStyle ?? Theme.of(context).textTheme.bodyText2!,
duration: widget.animationDuration,
child: contents,
);
}
//....
if (widget.type == MaterialType.canvas && widget.shape == null && widget.borderRadius == null) {
return AnimatedPhysicalModel(
//...
child: contents,
);
}
final ShapeBorder shape = _getShape();
if (widget.type == MaterialType.transparency) {
return _transparentInterior(
////..
contents: contents,
);
}
return _MaterialInterior(
//...
child: contents,
);
}
}
AnimatedDefaultTextStyle
class AnimatedDefaultTextStyle extends ImplicitlyAnimatedWidget {
AnimatedWidgetBaseState<AnimatedDefaultTextStyle> createState() => _AnimatedDefaultTextStyleState();
}
abstract class ImplicitlyAnimatedWidget extends StatefulWidget
class _AnimatedDefaultTextStyleState extends AnimatedWidgetBaseState<AnimatedDefaultTextStyle> {
@override
Widget build(BuildContext context) {
return DefaultTextStyle(
style: _style!.evaluate(animation),
child: widget.child,
);
}
}
DefaultTextStyle
Material 是由 一个DefaultTextStyle 包裹了child子widget的 小组件。
Material( textStyle: TextStyle(fontSize: 31, color: Colors.red), child: Text('materical 中的style'), ),
这样在Text的构建时,就可以通过 DefaultTextStyle.of(context);拿到父节点中的style数据了
3、扩展TextButton的child获取textStyle
可以扩展一下,Text也是可以获取TextButton的 style,下面原理也是相同的:
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
textStyle: TextStyle(color: Colors.red, fontSize: 30)),
child:
Text('TextButton---text style'), //为什么能够获取到style中的text style
)
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
textStyle: TextStyle(color: Colors.red, fontSize: 30)),
child: Column(
children: [
Text('123text style--随便套多少个子widget,都能够从父节点获取到style'),
Text('123---所有子Text都能获取父节点相同style值,并且不论套了多少层') //没有显示指定时,如果默认使用父级属性
],
)),
TextButton源码部分实现为:
class TextButton extends ButtonStyleButton
abstract class ButtonStyleButton extends StatefulWidget {
State<ButtonStyleButton> createState() => _ButtonStyleState();
}
class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStateMixin {
final Widget result = ConstrainedBox(
constraints: effectiveConstraints,
child: Material(
//..
textStyle: resolvedTextStyle?.copyWith(color: resolvedForegroundColor),
//...
);
return Semantics(
//...
child: _InputPadding(
//....
child: result,
),
);
}
}
4、inherit属性作用
回到刚开始的style和父级style获取上,
class Text extends StatelessWidget {
Widget build(BuildContext context) {
final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context); //
TextStyle? effectiveTextStyle = style;
if (style == null || style!.inherit) {
effectiveTextStyle = defaultTextStyle.style.merge(style);
}
}
}
effectiveTextStyle = defaultTextStyle.style.merge(style);
widget树中传递默认文本样式。当Text
小部件没有显式指定样式时,它会查找其父级上的DefaultTextStyle
,并应用那里设置的默认样式。
TextStyle merge(TextStyle? other) {
if (other == null) {
return this;
}
if (!other.inherit) {//是否使用上层widget样式(当本次没有显示指定时)
return other;
}
return copyWith(
color: other.color,
backgroundColor: other.backgroundColor,
//...
);
}
///copyWith
TextStyle copyWith({
bool? inherit,
Color? color,
Color? backgroundColor,
double? fontSize,
//...
}) {
//...
return TextStyle(//
inherit: inherit ?? this.inherit,
fontSize: fontSize ?? this.fontSize,
//...
);
}
总结:
在Flutter中,Text
小部件是会从其上层的DefaultTextStyle
中继承样式的,而Material
小部件本身就包含一个DefaultTextStyle
。这就是为什么Text
能够获取到Material
的样式信息的原因。
具体来说,DefaultTextStyle
是一个InheritedWidget
,它在widget树中传递默认文本样式。当Text
小部件没有显式指定样式时,它会查找其父级上的DefaultTextStyle
,并应用那里设置的默认样式。
在文章开头的例子中,Material
小部件设置了textStyle
,它实际上是在DefaultTextStyle
中设置的默认样式。因此,Text
小部件能够通过继承来获取Material
的默认文本样式。
这是Flutter中一种方便的方式,允许您在widget树中的某个位置设置默认样式,而不必手动为每个Text
小部件设置样式。
从这个DefaultTextStyle 我们又重新看到了 InheritedWidget 的作用。