Flutter.源码分析BoxScrollViewflutter/packages/flutter/lib/src/widgets/scroll_view.dart/BoxScrollView
1. 类注释部分
/// 使用单一子布局模型的 [ScrollView]。 /// /// {@template flutter.widgets.BoxScroll.scrollBehaviour} /// [ScrollView] 通常会装饰有 [Scrollbar] 和过度滚动指示器, /// 这些都由继承的 [ScrollBehavior] 管理。在 ScrollView 上方放置一个 /// [ScrollConfiguration] 可以修改该 ScrollView 的这些行为, /// 或者可以通过向 [MaterialApp.scrollBehavior] 或 [CupertinoApp.scrollBehavior] /// 提供 ScrollBehavior 来在应用范围内管理这些行为。 /// {@endtemplate} /// /// 另请参阅: /// /// * [ListView],这是一个使用线性布局模型的 [BoxScrollView]。 /// * [GridView],这是一个使用二维布局模型的 [BoxScrollView]。 /// * [CustomScrollView],它可以将多个子布局模型组合成一个单一的滚动视图。 abstract class BoxScrollView extends ScrollView { // ... }
2. 构造方法部分
/// 创建一个使用单一子布局模型的 [ScrollView]。 /// /// 如果 [primary] 参数为 true,则 [controller] 必须为 null。 const BoxScrollView({ super.key, // 用于控制是否应替换现有的同类型的 widget super.scrollDirection, // 滚动方向 super.reverse, // 是否反转滚动方向 super.controller, // 控制滚动位置的对象 super.primary, // 是否使用主滚动控制器 super.physics, // 如何应对用户停止拖动后的滚动 super.shrinkWrap, // 是否根据子项的总长度来设置滚动视图的长度 this.padding, // 插入子项的空间量 super.cacheExtent, // 预缓存超出滚动视图的区域的长度 super.semanticChildCount, // 用于语义通知的子项数量 super.dragStartBehavior, // 拖动开始行为 super.keyboardDismissBehavior, // 键盘消失行为 super.restorationId, // 用于保存滚动位置的 ID super.clipBehavior, // 内容超出滚动视图的可视区域时的剪裁行为 });
3. padding属性
/// 插入子项的空间量。 final EdgeInsetsGeometry? padding;
4. buildSlivers方法部分
@override List<Widget> buildSlivers(BuildContext context) { Widget sliver = buildChildLayout(context); EdgeInsetsGeometry? effectivePadding = padding; if (padding == null) { final MediaQueryData? mediaQuery = MediaQuery.maybeOf(context); if (mediaQuery != null) { // 使用 MediaQuery 的 padding 自动填充 sliver。 final EdgeInsets mediaQueryHorizontalPadding = mediaQuery.padding.copyWith(top: 0.0, bottom: 0.0); final EdgeInsets mediaQueryVerticalPadding = mediaQuery.padding.copyWith(left: 0.0, right: 0.0); // 使用 SliverPadding 消耗主轴 padding。 effectivePadding = scrollDirection == Axis.vertical ? mediaQueryVerticalPadding : mediaQueryHorizontalPadding; // 留下交叉轴 padding。 sliver = MediaQuery( data: mediaQuery.copyWith( padding: scrollDirection == Axis.vertical ? mediaQueryHorizontalPadding : mediaQueryVerticalPadding, ), child: sliver, ); } } if (effectivePadding != null) { sliver = SliverPadding(padding: effectivePadding, sliver: sliver); } return <Widget>[ sliver ]; }
可以看出 buildSlivers 方法主要负责构建滚动视图的子组件,并处理可能存在的 padding。
- 首先,它调用
buildChildLayout
(context) 方法来构建滚动视图的子组件,这个方法在 BoxScrollView 的子类中实现,例如 ListView 或 GridView。 - 然后,它检查是否已经设置了 padding。如果没有设置 padding,它会尝试从 MediaQuery 获取 padding。如果 MediaQuery 存在,它会根据滚动方向分别获取垂直和水平的 padding。
- 如果 MediaQuery 存在,它会创建一个新的 MediaQuery 组件,将 MediaQuery 的 padding 设置为根据滚动方向得到的 padding,并将原始的子组件作为新的 MediaQuery 组件的子组件。
- 如果
effectivePadding
(可能是从 MediaQuery 获取的padding
或者是直接设置的padding
)存在,它会创建一个 SliverPadding 组件,将 effectivePadding 设置为 SliverPadding 的 padding,并将原始的子组件(可能已经被包装在 MediaQuery 组件中)作为 SliverPadding 的子组件。 - 最后,它返回一个只包含一个组件(可能是 SliverPadding 或 MediaQuery 或原始的子组件)的列表。
这个实现确保了滚动视图的子组件可以正确地处理 padding,并且如果存在 MediaQuery,还可以自动适应 MediaQuery 的 padding。
5. buildChildLayout方法部分
/// 子类应重写此方法以构建布局模型。 @protected Widget buildChildLayout(BuildContext context);
参见《Flutter.源码分析.ScrollView》 buildChildLayout 部分,地址 https://blog.csdn.net/qq_28550263/article/details/134377965
6. 其它代码
@override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null)); }
这里的 debugFillProperties 方法是用于支持 Flutter 的诊断工具的。当开发者要求查看 BoxScrollView 的属性时,这个方法会被调用。在这个方法中,BoxScrollView 将自己的 padding 属性添加到了诊断属性中。这样,开发者就可以看到 padding 属性的值,以及它是否被设置了默认值。