likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 7

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

转载自:https://juejin.cn/post/7312699297634910218
评论
请登录