- 文章信息 -

Flutter笔记:DefaultTextStyle和DefaultTextHeightBehavior解读-LMLPHP


1. 概述

DefaultTextStyleDefaultTextHeightBehavior 是 Flutter 中用于定义和继承文本样式和文本高度行为的两个重要类。这些类允许开发者在小部件树中设置默认的文本样式和行为,这些设置会被下面的 Text 小部件继承。

本文将详细介绍这两个类的功能、原理、用法以及设计思想,并提供一些应用示例,以帮助开发者更好地理解和使用这些工具来统一和管理 Flutter 应用中的文本表现。

2. DefaultTextStyle

2.1 功能

DefaultTextStyle 是一个继承小部件,用于定义整个小部件树中 Text 小部件的默认文本样式。当 Text 小部件在其构造函数中没有指定样式时,它会从其构建上下文中查找 DefaultTextStyle 并应用找到的样式。这使得开发者可以在小部件树的任何级别上设置默认的文本样式,而所有下级 Text 小部件都会自动继承这些样式,除非它们显式定义了自己的样式。

2.2 原理

2.2.1 继承机制

DefaultTextStyle 利用 Flutter 的继承机制来传递文本样式。它是一个 InheritedWidget,这意味着它可以将数据传递给它的子树中的小部件。当 Text 小部件需要确定其样式时,它会向上遍历小部件树,寻找最近的 DefaultTextStyle 从中继承样式。要了解 DefaultTextStyle 的原理,可以从其继承关系入手,如图所示展示了该类在Flutter框架层中的继承关系:

  • Widget: 它是一个抽象的组件基类,定义了 Flutter UI 框架的基本结构和功能。所有 Flutter 小部件的基础。

  • ProxyWidget: 代理小部件,用于包装另一个小部件。它也是是一个抽象类,允许开发者在子小部件和父小部件之间插入额外的逻辑。

  • InheritedWidget: 用于在小部件树中有效地传递数据。它是一个抽象类,允许从父小部件到子小部件的数据继承,通常用于跨多个层级的状态或配置共享。

  • InheritedTheme:专门用于主题数据的继承。
    它是一个抽象类,允许主题数据在小部件树中传递,并提供 wrap 方法来将主题数据应用到子树中。

  • DefaultTextStyle:用于定义默认的文本样式,这些样式会被小部件树中的 Text 小部件继承。它继承自 InheritedTheme,使得文本样式可以作为主题的一部分在小部件树中传递和覆盖。

从上面的继承可以看到,DefaultTextStyle 是一个 InheritedWidget,这是 Flutter 中用于在小部件树中向下传递数据的一种机制。作为 DefaultTextStyle 允许在小部件树中的任何位置定义文本样式,而这些样式将自动应用到其子树中的所有 小部件上,除非这些 Text 小部件指定了自己的显式样式。

这种继承机制的工作原理是,当 Text 小部件在构建过程中需要确定其样式时,它会,寻找,并从中继承样式。如果在树中找不到 DefaultTextStyle,则会使用 Flutter 的。

这种机制极大地简化了文本样式的管理,特别是在大型应用中,可以在顶层定义一个 DefaultTextStyle,这样所有的文本小部件默认都会继承这些样式,除非特别指定了其他样式。这不仅保证了样式的一致性,还减少了重复代码的需要。

2.2.2 样式合并

DefaultTextStyle 提供了一个 merge 构造函数,允许开发者基于当前环境中的 DefaultTextStyle 创建一个新的 DefaultTextStyle,同时可以覆盖某些属性。这是通过合并现有的文本样式与新提供的文本样式属性来实现的,使得开发者可以在保留大部分默认样式的基础上进行细微调整。

在实际开发中,可能需要在某个特定的屏幕或组件中稍微调整文本的样式,而不影响全局的默认样式。使用 merge 方法可以实现这一点,例如,可能需要改变特定区域的字体大小或颜色。

merge 方法首先通过 DefaultTextStyle.of(context) 获取当前 BuildContext 中的 DefaultTextStyle。然后,它创建一个新的 DefaultTextStyle 实例,其中包含从父样式继承的属性,同时将任何非空的新属性值合并进来。

这里是 merge 方法的具体实现:

static Widget merge({
  Key? key,
  TextStyle? style,
  TextAlign? textAlign,
  bool? softWrap,
  TextOverflow? overflow,
  int? maxLines,
  TextWidthBasis? textWidthBasis,
  required Widget child,
}) {
  return Builder(
    builder: (BuildContext context) {
      final DefaultTextStyle parent = DefaultTextStyle.of(context);
      return DefaultTextStyle(
        key: key,
        style: parent.style.merge(style),
        textAlign: textAlign ?? parent.textAlign,
        softWrap: softWrap ?? parent.softWrap,
        overflow: overflow ?? parent.overflow,
        maxLines: maxLines ?? parent.maxLines,
        textWidthBasis: textWidthBasis ?? parent.textWidthBasis,
        child: child,
      );
    },
  );
}

merge 方法的工作原理如下:

  1. 继承现有样式:首先,它会从当前的 BuildContext 中获取最近的 DefaultTextStyle 实例,这个实例定义了当前文本样式的默认值。

  2. 合并新样式:然后,它将这个现有的样式与通过 merge 方法传入的新样式属性进行合并。这里的合并是指,对于每个样式属性,如果在新样式中明确指定了一个值(即非null),则使用新的值;如果新样式中该属性为null,则保留现有样式中的值。

  3. 应用结果:最终,这个合并后的样式将被应用到 DefaultTextStyle 小部件的子树中,从而影响所有未显式指定自己样式的 Text 小部件。

2.3 用法解析

DefaultTextStyle 可以在任何需要统一文本样式的场景中使用,例如设置整个应用或页面的基本字体样式、颜色、字体大小等。它特别适用于那些需要确保文本样式一致性的大型应用,通过在应用的顶层设置 DefaultTextStyle,可以确保所有子页面和组件都继承相同的基本样式。

  1. 全局文本样式设置

    在 Flutter 应用的根部(通常是在 MaterialAppCupertinoAppbuilder 属性中)设置 DefaultTextStyle,可以定义全应用通用的文本样式。这样,除非在子小部件中显式指定了不同的样式,否则所有的 Text 小部件都会继承这些样式。这种方法特别适用于那些需要确保文本样式一致性的大型应用。

const MaterialApp(
  home: DefaultTextStyle(
    style: TextStyle(color: Colors.black, fontSize: 20),
    child: HomeScreen(),
  ),
);
  1. 局部样式继承和覆盖

    在应用的某个特定部分,如一个特定的屏幕或组件中,可以使用 DefaultTextStyle 来继承并修改上层定义的样式。例如,在一个特定的页面中,你可能想要改变文本的颜色或字体大小,而不影响其他页面。通过使用 DefaultTextStyle.merge,可以在保留大部分样式的基础上进行这些调整。

DefaultTextStyle.merge(
  style: TextStyle(color: Colors.blue),
  child: Text("该文本将是蓝色的,其大小继承自父文本。"),
);
  1. 动态样式调整

    在需要根据应用状态或用户交互动态改变文本样式的场景中,DefaultTextStyle 同样非常有用。例如,可以在用户选择了一个主题偏好后,更新 DefaultTextStyle 来反映这一选择,从而改变整个应用中的文本样式。

setState(() {
  currentStyle = TextStyle(color: userPreferredColor);
});

return DefaultTextStyle(
  style: currentStyle,
  child: ChildWidget(),
);

通过这些方法,DefaultTextStyle 不仅提供了一种高效的方式来管理和继承文本样式,还极大地简化了在 Flutter 应用中实现一致性和可维护性的文本样式的过程。

2.4 设计思想

在深入了解了 DefaultTextStyle 的功能和工作原理后,我们可以从 Flutter 框架的设计层面来探讨其背后的设计思想。DefaultTextStyle 的设计主要目的是为了提供一种简便的方式来统一管理和继承文本样式,这样做有几个显著的优点:

  1. 减少重复代码:通过在小部件树的适当位置设置 DefaultTextStyle,可以确保所有子部件默认继承相同的样式,除非显式指定其他样式。这减少了在每个文本小部件中重复定义样式的需要,从而简化了代码的维护。

  2. 提高样式一致性:在应用的顶层或功能模块层设置统一的文本样式,可以保证整个应用或模块中文本的显示一致性。这对于保持品牌形象和用户界面的专业性至关重要。

  3. 易于调整和扩展:随着应用的发展,可能需要对文本样式进行调整以适应新的设计需求或用户偏好。DefaultTextStyle 的继承机制使得这些调整可以快速地在全局范围内实施,而不需要逐个修改每个文本小部件。

  4. 支持动态主题切换:Flutter 应用常常需要支持动态主题切换,如夜间模式或用户自定义主题。DefaultTextStyle 使得这种切换过程更加平滑,因为只需在顶层更改文本样式,就能影响到整个应用中的所有文本显示。

通过这些设计思想的实现,DefaultTextStyle 不仅提高了开发效率,还增强了应用的可维护性和用户体验。这种设计模式体现了 Flutter 框架对于“一次定义,到处使用”的原则,使得开发者可以更加专注于内容的创造,而不是样式的反复调整。

3. DefaultTextHeightBehavior

在实际开发中,DefaultTextStyle 使用得更为频繁,因为文本样式(如字体大小、颜色、字体等)是界面设计中经常需要调整和统一的属性。DefaultTextStyle 提供了一种方便的方式来设置和继承这些样式属性,使得开发者可以轻松地管理整个应用或特定部分的文本外观。

相比之下,DefaultTextHeightBehavior 的使用场景相对较少,这是因为文本的高度行为(如行间距和对齐方式)通常不需要频繁调整。然而,DefaultTextHeightBehavior 在处理复杂文本布局时仍然非常有用,尤其是当需要精细控制文本的行高、行间距或裁剪方式时。

例如,在创建一个具有丰富文本排版需求的阅读应用或文档编辑器时,DefaultTextHeightBehavior 可以帮助开发者统一设置文本的行为,如确保所有文本行的基线对齐或调整行间距以提高可读性。此外,如果应用需要支持多种语言,其中某些语言(如阿拉伯语或泰语)可能需要特定的文本高度行为设置以正确显示,DefaultTextHeightBehavior 就显得尤为重要。

3.1 DefaultTextHeightBehavior的功能

DefaultTextHeightBehavior 是一个专门用于控制文本高度行为的 Flutter 小部件。它的主要功能是为其子树中的 TextEditableText 小部件提供一个默认的 TextHeightBehavior。这个行为决定了文本的行高如何被计算,包括行间距和文本的对齐方式。

TextEditableText 小部件在其构造函数中没有显式指定 textHeightBehavior 时,它们会从其构建上下文中查找最近的 DefaultTextHeightBehavior 并应用找到的行为。如果在小部件树中找不到 DefaultTextHeightBehavior,则这些文本小部件将使用 Flutter 的默认行高行为。

此外,如果 DefaultTextStyle 小部件(它控制文本样式)也指定了 textHeightBehavior,并且位于 DefaultTextHeightBehavior 下方,则 Text 小部件将优先使用 DefaultTextStyle 中的 textHeightBehavior 而不是 DefaultTextHeightBehavior 中的设置。这提供了一种灵活的方式来精细控制文本的显示,特别是在复杂的布局中。

3.2 原理解析

3.2.1 继承机制

DefaultTextHeightBehavior 是一个继承小部件,类似于 DefaultTextStyle,它利用 Flutter 的继承机制来传递文本高度行为设置。作为一个 InheritedWidgetDefaultTextHeightBehavior 允许开发者在小部件树的任何位置定义文本的高度行为,这些设置会自动应用到其子树中的所有 TextEditableText 小部件上,除非这些小部件显式指定了自己的 textHeightBehavior

通过这种继承机制,DefaultTextHeightBehavior 提供了一种简便的方式来统一管理和继承文本的高度行为,从而简化了在 Flutter 应用中实现一致性和可维护性的文本显示的过程。

这种继承机制的工作原理是,当 TextEditableText 小部件在构建过程中需要确定其高度行为时,它会向上遍历小部件树,寻找最近的 DefaultTextHeightBehavior 小部件,并从中继承 textHeightBehavior。如果在树中找不到 DefaultTextHeightBehavior,则这些文本小部件将使用 Flutter 的默认行高行为。

通过这种机制,DefaultTextHeightBehavior 提供了一种简便的方式来统一管理和继承文本的高度行为,从而简化了在 Flutter 应用中实现一致性和可维护性的文本显示的过程。

3.2.2 行为覆盖

在 Flutter 的布局和继承机制中,DefaultTextHeightBehaviorDefaultTextStyle 都提供了对文本行为的控制。然而,当这两者在小部件树中同时存在时,可能会出现需要确定优先级的情况。

具体来说,如果在 DefaultTextHeightBehavior 下方的小部件树中存在一个 DefaultTextStyle,并且这个 DefaultTextStyle 实例定义了一个非空的 textHeightBehavior,那么在这个范围内的 TextEditableText 小部件将优先使用 DefaultTextStyle 中定义的 textHeightBehavior,而不是从 DefaultTextHeightBehavior 中继承的行为。

这种行为覆盖机制允许更精细的控制文本显示的行为,尤其是在复杂的布局中,开发者可能需要在某些特定区域内调整文本的行高行为,而不影响全局的设置。例如,在一个具有密集文本的新闻阅读应用界面中,可能希望摘要部分的文本行高不同于正文内容,以便提高阅读的舒适度和效率。

通过这种方式,Flutter 提供了一种灵活而强大的方法来处理文本显示的细节,使得开发者可以根据具体的界面需求和设计标准,调整和优化用户的阅读体验。

3.3 用法解析

DefaultTextHeightBehavior 的使用主要集中在需要精确控制文本行高和行为的场景中。这个小部件通过提供一个统一的行为设置,使得开发者可以在整个应用或特定部分的文本显示中实现一致的行高和对齐方式。以下是一些具体的使用场景和方法:

3.3.1 全局设置

在应用的顶层设置 DefaultTextHeightBehavior 可以确保所有下层的 TextEditableText 小部件继承相同的文本高度行为。这种方法适用于需要全局统一文本行为的应用,如电子书阅读器或文档编辑器,其中文本的一致性和可读性至关重要。

DefaultTextHeightBehavior(
  // 定义文本的高度行为,不应用高度到第一个上升和最后一个下降
  textHeightBehavior: TextHeightBehavior(
    applyHeightToFirstAscent: false, // 不应用高度到第一个上升
    applyHeightToLastDescent: false  // 不应用高度到最后一个下降
  ),
  child: MaterialApp(
    home: HomeScreen(),
  ),
);

3.3.2 局部覆盖

在特定页面或组件中,可以使用 DefaultTextHeightBehavior 来覆盖上层设置的行为,或为该部分的文本提供特定的行为。这种方法适用于某些页面或组件需要与全局设置不同的文本行为的情况。

DefaultTextHeightBehavior(
  // 定义文本的高度行为
  textHeightBehavior: TextHeightBehavior(
    applyHeightToFirstAscent: true, // 将高度应用于第一个上升
    applyHeightToLastDescent: true  // 将高度应用于最后一个下降
  ),
  // 包裹一个列布局,包含子组件
  child: Column(
    children: [
      Text("该文本遵循特定的高度行为。"), // 显示一个文本部件
      // 其他部件
    ],
  ),
);

3.3.3 动态调整

在应用运行时,根据用户的选择或其他条件动态调整文本的高度行为。例如,用户可以选择不同的阅读模式,每种模式下的文本行高和对齐方式可能不同。通过在状态更改时更新 DefaultTextHeightBehavior,可以实现这种动态调整。

setState(() {
  // 从用户首选项中获取应用的文本高度行为并更新当前文本高度行为
  currentTextHeightBehavior = TextHeightBehavior(
    applyHeightToFirstAscent: userPreference.applyHeightToFirstAscent, // 应用高度到第一个上升
    applyHeightToLastDescent: userPreference.applyHeightToLastDescent  // 应用高度到最后一个下降
  );
});

return DefaultTextHeightBehavior(
  textHeightBehavior: currentTextHeightBehavior, // 设置文本的高度行为
  child: ChildWidget(), // 返回包含子部件的小部件
);

通过这些使用方法,DefaultTextHeightBehavior 提供了一种灵活而强大的方式来控制文本的高度行为,使得开发者可以根据具体的需求和设计标准,优化用户的阅读体验和界面的视觉表现。

4. 更多应用示例

4.1 统一应用主题

在 Flutter 应用开发中,确保整个应用具有统一的文本样式和文本高度行为不仅有助于保持界面的一致性,还能提升用户的使用体验。通过在应用的根部设置 DefaultTextStyleDefaultTextHeightBehavior,我们可以确保所有的页面和组件都能继承相同的文本样式和行为,从而避免在每个单独的页面或组件中重复设置样式。现在我们来看一个比较完整性的例子。

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '统一主题示例',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: const DefaultTextStyle(
        // 设置默认的文本样式
        style: TextStyle(
          fontSize: 20,
          color: Colors.black,
          fontFamily: 'Georgia',
        ),
        // 设置默认的文本高度行为
        child: DefaultTextHeightBehavior(
          textHeightBehavior: TextHeightBehavior(
            applyHeightToFirstAscent: true,
            applyHeightToLastDescent: true,
          ),
          child: MyHomePage(title: '文本样式案例'),
        ),
      ),
    );
  }
}

可以看到,上面的代码中,MyHomePage被包裹在DefaultTextStyleDefaultTextHeightBehavior中间,分别设置了默认的文本样式和默认的文本高度行为,下面是MyHomePage类的实现。

class MyHomePage extends StatelessWidget {
  final String title;

  const MyHomePage({super.key, required this.title});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: const Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              '你好,世界!',
              style: TextStyle(
                fontSize: 24, // 这里的样式会覆盖 DefaultTextStyle 中的样式
                color: Colors.red,
              ),
            ),
            Text(
              '这是一个示例文本。',
            ),
            Text(
              '这些文本将继承应用的默认样式。',
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们首先在 MyApp 类中设置了 DefaultTextStyleDefaultTextHeightBehavior。这两个设置被放置在 MaterialApphome 属性中,确保它们能够影响到整个应用中的所有页面和组件。

  • DefaultTextStyle 设置了全局的文本样式,如字体大小、颜色和字体家族。

  • DefaultTextHeightBehavior 设置了文本的高度行为,如是否应用高度到第一行的上升和最后一行的下降。

代码的运行效果如图所示。
Flutter笔记:DefaultTextStyle和DefaultTextHeightBehavior解读-LMLPHP

任何在这之后创建的 Text 组件,如果没有指定自己的样式,都会继承这些默认设置。这样,我们就可以很容易地管理和维护整个应用的文本显示风格,确保风格的统一性。

4.2 特定页面的样式调整

在 Flutter 应用中,虽然我们可以在全局层面设置统一的文本样式,但有时候我们需要在特定页面上调整文本样式以满足特定的设计需求,而不影响全局的文本样式设置。这时,DefaultTextStyle.merge 方法就显得非常有用。

DefaultTextStyle.merge 允许我们在继承全局文本样式的基础上,对特定页面的文本样式进行调整。这种方法的优点是可以保留大部分全局样式的同时,只修改需要改变的部分。

以下是一个使用 DefaultTextStyle.merge 调整特定页面文本样式的示例:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        textTheme: const TextTheme(
          bodyMedium: TextStyle(color: Colors.black, fontSize: 16),
        ),
      ),
      home: const SpecificPage(),
    );
  }
}

class SpecificPage extends StatelessWidget {
  const SpecificPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("特定页面"),
      ),
      body: DefaultTextStyle.merge(
        style: const TextStyle(color: Colors.red, fontSize: 24), // 覆盖全局颜色和字体大小
        child: const Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text("这是特定页面的标题,红色,24号字体"),
            Text("这段文本继承了全局样式,但字体颜色和大小被覆盖"),
            Text("继续保持特定页面的样式设置"),
          ],
        ),
      ),
    );
  }
}

在这个例子中,SpecificPage 使用 DefaultTextStyle.merge 来调整页面内所有文本的颜色和字体大小。这样做的好处是,即使全局样式发生变化,这个页面的特定样式也不会受到影响,从而确保了设计的一致性和专业性。代码运行后的效果如图所示:

Flutter笔记:DefaultTextStyle和DefaultTextHeightBehavior解读-LMLPHP

在这个例子中,SpecificPage 使用 DefaultTextStyle.merge 来调整页面内所有文本的颜色和字体大小。这样做的好处是,即使全局样式发生变化,这个页面的特定样式也不会受到影响,从而确保了设计的一致性和专业性。

5. 结论

DefaultTextStyleDefaultTextHeightBehavior 提供了强大的工具来统一和继承文本样式和文本高度行为,使得 Flutter 应用的开发更加模块化和可管理。通过这些工具,开发者可以更容易地维护和更新应用的视觉表现,确保文本的一致性和可读性。

最后,发笔记纯当交个朋友。Flutter早年在相对冷门。近两年卖课的越来越多,目前有一些人的课可能还不如来翻一下我的笔记。

F. 附录

F.1 DefaultTextStyle类源码

/// 用于应用于没有显式样式的后代 [Text] 小部件的文本样式。
///
/// ```dart
///   /// 这个示例展示了如何使用 [DefaultTextStyle.merge] 创建一个默认文本样式,
///   /// 该样式继承当前默认文本样式的样式信息并覆盖一些属性。
///   /// 
///   /// ** 请参见示例代码 examples/api/lib/widgets/text/text.0.dart **
/// ```
///
/// 另请参阅:
///
///  * [AnimatedDefaultTextStyle],它可以在给定的持续时间内平滑地动画变化的文本样式。
///  * [DefaultTextStyleTransition],它接受一个提供的 [Animation],可以在一段时间内平滑地动画文本样式的变化。
class DefaultTextStyle extends InheritedTheme {
  /// 为给定的子树创建默认文本样式。
  ///
  /// 考虑使用 [DefaultTextStyle.merge] 来继承给定 [BuildContext] 的当前默认文本样式的样式信息。
  ///
  /// [maxLines] 属性可能为空(实际上默认为空),但如果它不为空,则必须大于零。
  const DefaultTextStyle({
    super.key,
    required this.style,
    this.textAlign,
    this.softWrap = true,
    this.overflow = TextOverflow.clip,
    this.maxLines,
    this.textWidthBasis = TextWidthBasis.parent,
    this.textHeightBehavior,
    required super.child,
  }) : assert(maxLines == null || maxLines > 0);

  /// 提供回退值的可常量构造的默认文本样式。
  ///
  /// 当给定的 [BuildContext] 没有封闭的默认文本样式时,从 [of] 返回。
  ///
  /// 此构造函数创建一个带有无效 [child] 的 [DefaultTextStyle],这意味着构造的值不能合并到树中。
  const DefaultTextStyle.fallback({ super.key })
    : style = const TextStyle(),
      textAlign = null,
      softWrap = true,
      maxLines = null,
      overflow = TextOverflow.clip,
      textWidthBasis = TextWidthBasis.parent,
      textHeightBehavior = null,
      super(child: const _NullWidget());

  /// 创建一个默认文本样式,覆盖小部件树中此处的文本样式。
  ///
  /// 给定的[style]与插入小部件的[BuildContext]中的默认文本样式合并,
  /// 并且任何其他参数(不为null)都将替换该默认文本样式上的相应属性。
  ///
  /// 此构造函数不能用于覆盖祖先的[maxLines]属性,该属性的值为null,因为此处的null表示“推迟到祖先”。
  /// 若要将祖先的非null[maxLines]替换为null值(以移除对行数的限制),请手动使用[DefaultTextStyle.of]获取环境[DefaultTextStyle],
  /// 然后直接使用[DefaultTextStyle.new]构造函数创建一个新的[DefaultTextStyle]。
  /// 请参阅下面的源代码示例,了解如何执行此操作(因为这基本上就是此构造函数的作用)。
  static Widget merge({
    Key? key,
    TextStyle? style,
    TextAlign? textAlign,
    bool? softWrap,
    TextOverflow? overflow,
    int? maxLines,
    TextWidthBasis? textWidthBasis,
    required Widget child,
  }) {
    return Builder(
      builder: (BuildContext context) {
        final DefaultTextStyle parent = DefaultTextStyle.of(context);
        return DefaultTextStyle(
          key: key,
          style: parent.style.merge(style),
          textAlign: textAlign ?? parent.textAlign,
          softWrap: softWrap ?? parent.softWrap,
          overflow: overflow ?? parent.overflow,
          maxLines: maxLines ?? parent.maxLines,
          textWidthBasis: textWidthBasis ?? parent.textWidthBasis,
          child: child,
        );
      },
    );
  }
  /// 要应用的文本样式。
  final TextStyle style; 

  /// 文本小部件中每行文本应如何在水平方向上对齐。
  final TextAlign? textAlign; 

  /// 文本是否应在软换行处中断。
  ///
  /// 如果为false,则文本中的字形将被定位,就好像有无限的水平空间一样。
  ///
  /// 这也决定了[overflow]属性的行为。如果这是true或null,
  /// 导致溢出的字形以及随后的字形将不会被呈现。
  final bool softWrap; 

  /// 应如何处理视觉溢出。
  ///
  /// 如果[softWrap]为true或null,则导致溢出的字形以及随后的字形将不会被呈现。
  /// 否则,将以给定的溢出选项显示。
  final TextOverflow overflow; 

  /// 文本跨度的可选最大行数,必要时进行换行。
  /// 如果文本超出给定行数,将根据[overflow]进行截断。
  ///
  /// 如果为1,则文本不会换行。否则,文本将在框的边缘处换行。
  ///
  /// 如果这是非null的,则即使是[Text.maxLines]的显式null值也会被覆盖。
  final int? maxLines;

  /// 计算文本宽度时要使用的策略。
  ///
  /// 可能的值及其含义,请参阅[TextWidthBasis]。
  final TextWidthBasis textWidthBasis;

  /// {@macro dart.ui.textHeightBehavior}
  final ui.TextHeightBehavior? textHeightBehavior;

  /// 包围给定上下文的最近的此类实例。
  ///
  /// 如果不存在这样的实例,则返回由[DefaultTextStyle.fallback]创建的实例,其中包含回退值。
  ///
  /// 典型用法如下:
  ///
  /// ```dart
  /// DefaultTextStyle style = DefaultTextStyle.of(context);
  /// ```
  static DefaultTextStyle of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<DefaultTextStyle>() ?? const DefaultTextStyle.fallback();
  }

  @override
  bool updateShouldNotify(DefaultTextStyle oldWidget) {
    return style != oldWidget.style ||
        textAlign != oldWidget.textAlign ||
        softWrap != oldWidget.softWrap ||
        overflow != oldWidget.overflow ||
        maxLines != oldWidget.maxLines ||
        textWidthBasis != oldWidget.textWidthBasis ||
        textHeightBehavior != oldWidget.textHeightBehavior;
  }

  @override
  Widget wrap(BuildContext context, Widget child) {
    return DefaultTextStyle(
      style: style,
      textAlign: textAlign,
      softWrap: softWrap,
      overflow: overflow,
      maxLines: maxLines,
      textWidthBasis: textWidthBasis,
      textHeightBehavior: textHeightBehavior,
      child: child,
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    style.debugFillProperties(properties);
    properties.add(EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
    properties.add(FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true));
    properties.add(EnumProperty<TextOverflow>('overflow', overflow, defaultValue: null));
    properties.add(IntProperty('maxLines', maxLines, defaultValue: null));
    properties.add(EnumProperty<TextWidthBasis>('textWidthBasis', textWidthBasis, defaultValue: TextWidthBasis.parent));
    properties.add(DiagnosticsProperty<ui.TextHeightBehavior>('textHeightBehavior', textHeightBehavior, defaultValue: null));
  }
}

F.1 DefaultTextHeightBehavior类源码

/// 将应用于未明确设置 [Text.textHeightBehavior] 的后代 [Text] 和 [EditableText] 小部件的 [TextHeightBehavior]。
///
/// 如果此小部件下方存在具有非空 [DefaultTextStyle.textHeightBehavior] 的 [DefaultTextStyle],
/// 则将使用 [DefaultTextStyle.textHeightBehavior] 而不是此小部件的 [TextHeightBehavior]。
///
/// 另请参阅:
///
///  * [DefaultTextStyle],它定义了要应用于后代 [Text] 小部件的 [TextStyle]。
class DefaultTextHeightBehavior extends InheritedTheme {
  /// 为给定的子树创建默认文本高度行为。
  const DefaultTextHeightBehavior({
    super.key,
    required this.textHeightBehavior,
    required super.child,
  });

  /// {@macro dart.ui.textHeightBehavior}
  final TextHeightBehavior textHeightBehavior;

  /// 包围给定上下文的最接近的 [DefaultTextHeightBehavior] 实例,如果找不到则为 null。
  ///
  /// 如果不存在这样的实例,则此方法将返回 `null`。
  ///
  /// 调用此方法将在 [context] 中创建对最近的 [DefaultTextHeightBehavior] 的依赖,如果存在。
  ///
  /// 典型用法如下:
  ///
  /// ```dart
  /// TextHeightBehavior? defaultTextHeightBehavior = DefaultTextHeightBehavior.of(context);
  /// ```
  ///
  /// See also:
  ///
  /// * [DefaultTextHeightBehavior.maybeOf], which is similar to this method,
  ///   but asserts if no [DefaultTextHeightBehavior] ancestor is found.
  static TextHeightBehavior? maybeOf(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<DefaultTextHeightBehavior>()?.textHeightBehavior;
  }
  /// 包围给定上下文的最接近的 [DefaultTextHeightBehavior] 实例。
  ///
  /// 如果不存在这样的实例,则此方法在调试模式下会断言,并在发布模式下抛出异常。
  ///
  /// 典型用法如下:
  ///
  /// ```dart
  /// TextHeightBehavior defaultTextHeightBehavior = DefaultTextHeightBehavior.of(context);
  /// ```
  ///
  /// 调用此方法将在 [context] 中创建对最近的 [DefaultTextHeightBehavior] 的依赖。
  ///
  /// 另请参阅:
  ///
  /// * [DefaultTextHeightBehavior.maybeOf],类似于此方法,但如果找不到 [DefaultTextHeightBehavior] 祖先,则返回 null。
  static TextHeightBehavior of(BuildContext context) {
    final TextHeightBehavior? behavior = maybeOf(context);
    assert(() {
      if (behavior == null) {
        throw FlutterError(
          'DefaultTextHeightBehavior.of() was called with a context that does not contain a '
          'DefaultTextHeightBehavior widget.\n'
          'No DefaultTextHeightBehavior widget ancestor could be found starting from the '
          'context that was passed to DefaultTextHeightBehavior.of(). This can happen '
          'because you are using a widget that looks for a DefaultTextHeightBehavior '
          'ancestor, but no such ancestor exists.\n'
          'The context used was:\n'
          '  $context',
        );
      }
      return true;
    }());
    return behavior!;
  }

  @override
  bool updateShouldNotify(DefaultTextHeightBehavior oldWidget) {
    return textHeightBehavior != oldWidget.textHeightBehavior;
  }

  @override
  Widget wrap(BuildContext context, Widget child) {
    return DefaultTextHeightBehavior(
      textHeightBehavior: textHeightBehavior,
      child: child,
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<ui.TextHeightBehavior>('textHeightBehavior', textHeightBehavior, defaultValue: null));
  }
}
04-28 12:10