• Flutter开发:Text为什么可以从父节点Material获取textStyle
  • 发布于 2个月前
  • 169 热度
    0 评论
为什么下面代码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,
      ),
    );
  }

}
总结:

在Flutter中,Text小部件是会从其上层的DefaultTextStyle中继承样式的,而Material小部件本身就包含一个DefaultTextStyle。这就是为什么Text能够获取到Material的样式信息的原因。具体来说,DefaultTextStyle是一个InheritedWidget,它在widget树中传递默认文本样式。当Text小部件没有显式指定样式时,它会查找其父级上的DefaultTextStyle,并应用那里设置的默认样式。


在文章开头的例子中,Material小部件设置了textStyle,它实际上是在DefaultTextStyle中设置的默认样式。因此,Text小部件能够通过继承来获取Material的默认文本样式。这是Flutter中一种方便的方式,允许您在widget树中的某个位置设置默认样式,而不必手动为每个Text小部件设置样式。

从这个DefaultTextStyle 我们又重新看到了 InheritedWidget 的作用。
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
用户评论