Text为什么可以从父节点Material获取textStyle

为什么 下面代码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 的效果。