Flutter.源码分析.flutter/packages/flutter/lib/src/widgets/scroll_view.dart/ListView

Flutter.源码分析
ListView
flutter/packages/flutter/lib/src/widgets/scroll_view.dart/ListView

李俊才(jcLee95) 的个人博客:https://blog.csdn.net/qq_28550263

本文地址:https://blog.csdn.net/qq_28550263/article/details/134374857


本文提供 Flutter 框架中 ListView 类源码注释的中文翻译以及必要的分析解说。


1. 类注释部分

/// 一个线性排列的可滚动组件列表。
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=KJpkjHGiI5A}
///
/// [ListView] 是最常用的滚动组件。它在滚动方向上一个接一个地显示其子组件。在交叉轴上,
/// 子组件需要填充 [ListView]。
///
/// 如果非空,[itemExtent] 会强制子组件在滚动方向上具有给定的尺寸。
///
/// 如果非空,[prototypeItem] 会强制子组件在滚动方向上具有与给定组件相同的尺寸。
///
/// 指定 [itemExtent] 或 [prototypeItem] 比让子组件确定自己的尺寸更高效,因为滚动机制可以利用
/// 子组件尺寸的预知来节省工作,例如当滚动位置发生剧变时。
///
/// 你不能同时指定 [itemExtent] 和 [prototypeItem],只能指定其中一个或都不指定。
///
/// 构造 [ListView] 有四种选项:
///
/// 1. 默认构造函数接受一个明确的 [List<Widget>] 子组件。这个构造函数适用于具有少量子组件的列表视图,
/// 因为构造 [List] 需要为可能在列表视图中显示的每个子组件做工作,而不仅仅是那些实际可见的子组件。
///
/// 2. [ListView.builder] 构造函数接受一个 [IndexedWidgetBuilder],它根据需求构建子组件。这个构造函数适用于
/// 具有大量(或无限)子组件的列表视图,因为构建器只为那些实际可见的子组件调用。
///
/// 3. [ListView.separated] 构造函数接受两个 [IndexedWidgetBuilder]:itemBuilder 根据需求构建子项,
/// separatorBuilder 类似地构建出现在子项之间的分隔符子项。这个构造函数适用于具有固定数量子组件的列表视图。
///
/// 4. [ListView.custom] 构造函数接受一个 [SliverChildDelegate],它提供了自定义子模型的其他方面的能力。例如,
/// [SliverChildDelegate] 可以控制用于估计实际不可见的子组件的大小的算法。
///
/// 要控制滚动视图的初始滚动偏移量,提供一个设置了其 [ScrollController.initialScrollOffset] 属性的 [controller]。
///
/// 默认情况下,[ListView] 会自动填充列表的可滚动极限,以避免 [MediaQuery] 的填充指示的部分阻塞。要避免此行为,
/// 使用零 [padding] 属性覆盖。
///
/// {@tool snippet}
// 这个示例使用 [ListView] 的默认构造函数,它接受一个明确的 [List<Widget>] 子组件。这个 [ListView] 的子组件由
/// 带有 [Text] 的 [Container] 组成。
///
/// ![一个包含3个琥珀色容器和示例文本的 ListView。](https://flutter.github.io/assets-for-api-docs/assets/widgets/list_view.png)
///
/// ```dart
/// ListView(
///   padding: const EdgeInsets.all(8),
///   children: <Widget>[
///     Container(
///       height: 50,
///       color: Colors.amber[600],
///       child: const Center(child: Text('Entry A')),
///     ),
///     Container(
///       height: 50,
///       color: Colors.amber[500],
///       child: const Center(child: Text('Entry B')),
///     ),
///     Container(
///       height: 50,
///       color: Colors.amber[100],
///       child: const Center(child: Text('Entry C')),
///     ),
///   ],
/// )
/// ```
/// {@end-tool}
///
/// {@tool snippet}
/// 这个示例与前一个相同,使用 [ListView.builder] 构造函数创建相同的列表。使用 [IndexedWidgetBuilder],子组件可以懒加载,数量可以无限。
///
/// ![一个包含3个琥珀色容器和示例文本的 ListView。](https://flutter.github.io/assets-for-api-docs/assets/widgets/list_view_builder.png)
///
/// ```dart
/// final List<String> entries = <String>['A', 'B', 'C'];
/// final List<int> colorCodes = <int>[600, 500, 100];
///
/// Widget build(BuildContext context) {
///   return ListView.builder(
///     padding: const EdgeInsets.all(8),
///     itemCount: entries.length,
///     itemBuilder: (BuildContext context, int index) {
///       return Container(
///         height: 50,
///         color: Colors.amber[colorCodes[index]],
///         child: Center(child: Text('Entry ${entries[index]}')),
///       );
///     }
///   );
/// }
/// ```
/// {@end-tool}
///
/// {@tool snippet}
/// 这个示例继续从前面的示例构建,使用 [ListView.separated] 创建类似的列表。这里,[Divider] 被用作分隔符。
///
/// ![一个包含3个琥珀色容器和示例文本以及它们之间的 Divider 的 ListView。](https://flutter.github.io/assets-for-api-docs/assets/widgets/list_view_separated.png)
///
/// ```dart
/// final List<String> entries = <String>['A', 'B', 'C'];
/// final List<int> colorCodes = <int>[600, 500, 100];
///
/// Widget build(BuildContext context) {
///   return ListView.separated(
///     padding: const EdgeInsets.all(8),
///     itemCount: entries.length,
///     itemBuilder: (BuildContext context, int index) {
///       return Container(
///         height: 50,
///         color: Colors.amber[colorCodes[index]],
///         child: Center(child: Text('Entry ${entries[index]}')),
///       );
///     },
///     separatorBuilder: (BuildContext context, int index) => const Divider(),
///   );
/// }
/// ```
/// {@end-tool}
///
/// ## 子元素的生命周期
///
/// ### 创建
///
/// 在布局列表时,可见子元素的元素、状态和渲染对象将根据现有组件(例如使用默认构造函数时)或懒加载提供的组件(例如使用 [ListView.builder] 构造函数时)懒加载创建。
///
/// ### 销毁
///
/// 当一个子组件滚动出视图时,关联的元素子树、状态和渲染对象被销毁。当它滚动回来时,将懒加载创建一个新的子组件,以及新的元素、状态和渲染对象。
///
/// ### 减轻销毁
///
/// 为了在子元素滚动进出视图时保留状态,有以下可能的选项:
///
///  * M将非琐碎的驱动 UI 状态的业务逻辑的所有权移出列表子组件子树。例如,如果列表包含帖子,其点赞数来自缓存的网络响应,将帖子列表和点赞数存储在列表外的数据模型中。让列表子组件 UI 子树可以轻松地从真实源模型对象重新创建。在子组件子树中使用 [StatefulWidget] 来存储瞬时 UI 状态。
///
///  * 让 [KeepAlive] 成为需要保留的列表子组件子树的根组件。[KeepAlive] 组件将子树的顶部渲染对象子项标记为 keepalive。当关联的顶部渲染对象滚动出视图时,列表将子组件的渲染对象(以及相应的元素和状态)保存在缓存列表中,而不是销毁它们。当滚动回视图时,渲染对象会原样重绘(如果在此期间没有被标记为脏)。
///
///    这只有在 `addAutomaticKeepAlives` 和 `addRepaintBoundaries` 为 false 时才有效,因为这些参数会导致 [ListView] 用其他组件包装每个子组件子树。
///
///  * 使用 [AutomaticKeepAlive] 组件(当 `addAutomaticKeepAlives` 为 true 时默认插入)。[AutomaticKeepAlive] 允许后代组件控制子树是否真的保持活动状态。
///    这种行为与 [KeepAlive] 形成对比,后者会无条件地保持子树活动(alive)。
///
///    例如,[EditableText] 组件在其文本字段具有输入焦点时,会发出信号让其列表子元素子树保持活动。如果它没有焦点,且没有其他后代通过 [KeepAliveNotification] 发出保持活动的信号,
///    列表子元素子树将在滚动出视图时被销毁。
///
///    [AutomaticKeepAlive] 的后代通常通过使用 [AutomaticKeepAliveClientMixin] 发出保持活动的信号,然后实现 [AutomaticKeepAliveClientMixin.wantKeepAlive] getter 
///    并调用 [AutomaticKeepAliveClientMixin.updateKeepAlive]。
///
/// ## 转换为 [CustomScrollView]
///
/// [ListView] 基本上是一个 [CustomScrollView],其 [CustomScrollView.slivers] 属性中有一个 [SliverList]。
///
/// 如果 [ListView] 不再足够,例如因为滚动视图既要有列表又要有网格,或者因为列表要与 [SliverAppBar] 结合等,那么直接使用 [CustomScrollView] 替换 [ListView] 的代码是直接的。
///
/// [ListView] 上的 [key]、[scrollDirection]、[reverse]、[controller]、[primary]、[physics] 和 [shrinkWrap] 属性直接映射到 [CustomScrollView] 上的同名属性。
///
/// [CustomScrollView.slivers] 属性应该是一个列表,包含以下内容:
///  * 如果 [itemExtent] 和 [prototypeItem] 都为 null,则为 [SliverList];
///  * 如果 [itemExtent] 不为 null,则为 [SliverFixedExtentList];
///  * 如果 [prototypeItem] 不为 null,则为 [SliverPrototypeExtentList]。
///
/// [ListView] 上的 [childrenDelegate] 属性对应于 [SliverList.delegate](或 [SliverFixedExtentList.delegate])属性。[ListView] 构造函数的 `children` 参数对应于
/// [childrenDelegate] 是一个带有相同参数的 [SliverChildListDelegate]。[ListView.builder] 构造函数的 `itemBuilder` 和 `itemCount` 参数对应于
/// [childrenDelegate] 是一个带有等效参数的 [SliverChildBuilderDelegate]。
///
/// [padding] 属性对应于在 [CustomScrollView.slivers] 属性中有一个 [SliverPadding],而不是列表本身,并且 [SliverList] 是 [SliverPadding] 的子组件。
///
/// [CustomScrollView] 不会像 [ListView] 那样自动避免 [MediaQuery] 的阻塞。要复制这种行为,将 slivers 包装在 [SliverSafeArea] 中。
///
/// 一旦代码已经被移植为使用 [CustomScrollView],其他的 slivers,如 [SliverGrid] 或 [SliverAppBar],可以放入 [CustomScrollView.slivers] 列表中。
///
/// {@tool snippet}
///
/// 这里有两个简短的片段,显示了一个 [ListView] 及其使用 [CustomScrollView] 的等效代码:
///
/// ```dart
/// ListView(
///   padding: const EdgeInsets.all(20.0),
///   children: const <Widget>[
///     Text("I'm dedicating every day to you"),
///     Text('Domestic life was never quite my style'),
///     Text('When you smile, you knock me out, I fall apart'),
///     Text('And I thought I was so smart'),
///   ],
/// )
/// ```
/// {@end-tool}
/// {@tool snippet}
///
/// ```dart
/// CustomScrollView(
///   slivers: <Widget>[
///     SliverPadding(
///       padding: const EdgeInsets.all(20.0),
///       sliver: SliverList(
///         delegate: SliverChildListDelegate(
///           <Widget>[
///             const Text("I'm dedicating every day to you"),
///             const Text('Domestic life was never quite my style'),
///             const Text('When you smile, you knock me out, I fall apart'),
///             const Text('And I thought I was so smart'),
///           ],
///         ),
///       ),
///     ),
///   ],
/// )
/// ```
/// {@end-tool}
///
/// ## 空列表的特殊处理
///
/// 一个常见的设计模式是为空列表有一个自定义的 UI。在 Flutter 中实现这个的最好方式就是在构建时根据条件替换 [ListView],显示你需要的空列表状态的组件:
///
/// {@tool snippet}
///
/// 简单的空列表界面示例:
///
/// ```dart
/// Widget build(BuildContext context) {
///   return Scaffold(
///     appBar: AppBar(title: const Text('Empty List Test')),
///     body: itemCount > 0
///       ? ListView.builder(
///           itemCount: itemCount,
///           itemBuilder: (BuildContext context, int index) {
///             return ListTile(
///               title: Text('Item ${index + 1}'),
///             );
///           },
///         )
///       : const Center(child: Text('No items')),
///   );
/// }
/// ```
/// {@end-tool}
///
/// ## 空列表的特殊处理
///
/// 一个常见的设计模式是为空列表有一个自定义的 UI。在 Flutter 中实现这个的最好方式就是在构建时根据条件替换 [ListView],显示你需要的空列表状态的组件:
///
/// {@tool dartpad}
/// 这个示例展示了在 [ListView] 或 [GridView] 中 [ListTile] 选择的自定义实现。
/// 长按任何 [ListTile] 以启用选择模式。
///
/// **参见 examples/api/lib/widgets/scroll_view/list_view.0.dart 中的代码**
/// {@end-tool}
///
/// {@macro flutter.widgets.BoxScroll.scrollBehaviour}
///
/// 另请参阅:
///
///  * [SingleChildScrollView],这是一个有单个子组件的可滚动组件。
///  * [PageView],这是一个滚动的子组件列表,每个子组件都是视口(viewport)的大小。
///  * [GridView],这是一个可滚动的,2D 组件数组。
///  * [CustomScrollView],这是一个使用 slivers 创建自定义滚动效果的可滚动组件。
///  * [ListBody],它以类似的方式排列其子组件,但没有滚动。
///  * [ScrollNotification] 和 [NotificationListener],可以用来观察滚动位置,而无需使用 [ScrollController]。
///  * [布局组件目录](https://flutter.dev/widgets/layout/)。
///  * Cookbook: [使用列表](https://flutter.dev/docs/cookbook/lists/basic-list)
///  * Cookbook: [处理长列表](https://flutter.dev/docs/cookbook/lists/long-lists)
///  * Cookbook: [创建水平列表](https://flutter.dev/docs/cookbook/lists/horizontal-list)
///  * Cookbook: [创建包含不同类型项目的列表](https://flutter.dev/docs/cookbook/lists/mixed-list)
///  * Cookbook: [实现滑动以解除](https://flutter.dev/docs/cookbook/gestures/dismissible)
class ListView extends BoxScrollView{// ...
}

2. 默认构造函数注释部分

  /// 从显式的 [List] 创建一个可滚动的线性组件数组。////// 当列表视图的子组件数量较少时,此构造函数是合适的,因为构造 [List] 需要为可能在列表视图中显示的每个子组件做工作,/// 而不仅仅是那些实际可见的子组件。////// 与框架中的其他组件一样,此组件期望 [children] 列表在此处传入后不会发生变化。/// 有关更多详细信息,请参阅 [SliverChildListDelegate.children] 的文档。////// 通常,使用 [ListView.builder] 按需创建子组件更高效,因为它会在必要时懒加载组件子组件。////// `addAutomaticKeepAlives` 参数对应于 [SliverChildListDelegate.addAutomaticKeepAlives] 属性。/// `addRepaintBoundaries` 参数对应于 [SliverChildListDelegate.addRepaintBoundaries] 属性。/// `addSemanticIndexes` 参数对应于 [SliverChildListDelegate.addSemanticIndexes] 属性。这些参数都不能为 null。ListView({super.key,super.scrollDirection,super.reverse,super.controller,super.primary,super.physics,super.shrinkWrap,super.padding,this.itemExtent,this.prototypeItem,bool addAutomaticKeepAlives = true,bool addRepaintBoundaries = true,bool addSemanticIndexes = true,super.cacheExtent,List<Widget> children = const <Widget>[],int? semanticChildCount,super.dragStartBehavior,super.keyboardDismissBehavior,super.restorationId,super.clipBehavior,}) : assert(itemExtent == null || prototypeItem == null,// 你只能传入 itemExtent 或 prototypeItem,不能两者都传。'You can only pass itemExtent or prototypeItem, not both.', ),childrenDelegate = SliverChildListDelegate(children,addAutomaticKeepAlives: addAutomaticKeepAlives,addRepaintBoundaries: addRepaintBoundaries,addSemanticIndexes: addSemanticIndexes,),super(semanticChildCount: semanticChildCount ?? children.length,);

3. ListView.builder构造函数注释部分

  /// 创建一个可滚动的线性组件数组,这些组件是按需创建的。////// 对于具有大量(或无限)子组件的列表视图,此构造函数是合适的,因为构建器只会为实际可见的子组件调用。////// 提供非空的 `itemCount` 可以提高 [ListView] 估计最大滚动范围的能力。////// `itemBuilder` 回调只会被调用大于等于零且小于 `itemCount` 的索引。////// {@template flutter.widgets.ListView.builder.itemBuilder}/// `itemBuilder` 返回 `null` 是合法的。如果它这样做了,滚动视图将停止调用 `itemBuilder`,即使它尚未达到 `itemCount`。/// 通过返回 `null`,除非用户已经到达了 [ScrollView] 的末尾,否则 [ScrollPosition.maxScrollExtent] 将不准确。/// 这也可能导致用户滚动时 [Scrollbar] 的增长。////// 对于更准确的 [ScrollMetrics],请考虑指定 `itemCount`。/// {@endtemplate}////// `itemBuilder` 应始终在被调用时创建组件实例。/// 避免使用返回先前构造的组件的构建器;如果列表视图的子组件是提前创建的,或者在创建 [ListView] 本身时一次性创建的,/// 使用 [ListView] 构造函数会更高效。然而,更高效的方式是使用此构造函数的 `itemBuilder` 回调按需创建实例。////// {@macro flutter.widgets.PageView.findChildIndexCallback}////// `addAutomaticKeepAlives` 参数对应于 [SliverChildBuilderDelegate.addAutomaticKeepAlives] 属性。/// `addRepaintBoundaries` 参数对应于 [SliverChildBuilderDelegate.addRepaintBoundaries] 属性。/// `addSemanticIndexes` 参数对应于 [SliverChildBuilderDelegate.addSemanticIndexes] 属性。这些参数都不能为 null。ListView.builder({super.key,super.scrollDirection,super.reverse,super.controller,super.primary,super.physics,super.shrinkWrap,super.padding,this.itemExtent,this.prototypeItem,required NullableIndexedWidgetBuilder itemBuilder,ChildIndexGetter? findChildIndexCallback,int? itemCount,bool addAutomaticKeepAlives = true,bool addRepaintBoundaries = true,bool addSemanticIndexes = true,super.cacheExtent,int? semanticChildCount,super.dragStartBehavior,super.keyboardDismissBehavior,super.restorationId,super.clipBehavior,}) : assert(itemCount == null || itemCount >= 0),assert(semanticChildCount == null || semanticChildCount <= itemCount!),assert(itemExtent == null || prototypeItem == null,'You can only pass itemExtent or prototypeItem, not both.',),childrenDelegate = SliverChildBuilderDelegate(itemBuilder,findChildIndexCallback: findChildIndexCallback,childCount: itemCount,addAutomaticKeepAlives: addAutomaticKeepAlives,addRepaintBoundaries: addRepaintBoundaries,addSemanticIndexes: addSemanticIndexes,),super(semanticChildCount: semanticChildCount ?? itemCount,);

4. ListView.separated构造函数注释部分

  /// 创建一个固定长度的可滚动线性数组,列表的"项"由列表项的"分隔符"分隔。////// 对于具有大量项和分隔符子组件的列表视图,此构造函数是合适的,因为构建器只会为实际可见的子组件调用。////// `itemBuilder` 回调将被调用大于等于零且小于 `itemCount` 的索引。////// 分隔符只出现在列表项之间:分隔符 0 出现在项 0 之后,最后一个分隔符出现在最后一项之前。////// `separatorBuilder` 回调将被调用大于等于零且小于 `itemCount - 1` 的索引。////// `itemBuilder` 和 `separatorBuilder` 回调应始终在被调用时创建组件实例。/// 避免使用返回先前构造的组件的构建器;如果列表视图的子组件是提前创建的,或者在创建 [ListView] 本身时一次性创建的,/// 使用 [ListView] 构造函数会更高效。////// {@macro flutter.widgets.ListView.builder.itemBuilder}////// {@macro flutter.widgets.PageView.findChildIndexCallback}////// {@tool snippet}////// 这个示例展示了如何创建一个 [ListView],其 [ListTile] 列表项由 [Divider] 分隔。////// ```dart/// ListView.separated(///   itemCount: 25,///   separatorBuilder: (BuildContext context, int index) => const Divider(),///   itemBuilder: (BuildContext context, int index) {///     return ListTile(///       title: Text('item $index'),///     );///   },/// )/// ```/// {@end-tool}////// `addAutomaticKeepAlives` 参数对应于 [SliverChildBuilderDelegate.addAutomaticKeepAlives] 属性。/// `addRepaintBoundaries` 参数对应于 [SliverChildBuilderDelegate.addRepaintBoundaries] 属性。/// `addSemanticIndexes` 参数对应于 [SliverChildBuilderDelegate.addSemanticIndexes] 属性。这些参数都不能为 null。ListView.separated({super.key,super.scrollDirection,super.reverse,super.controller,super.primary,super.physics,super.shrinkWrap,super.padding,required NullableIndexedWidgetBuilder itemBuilder,ChildIndexGetter? findChildIndexCallback,required IndexedWidgetBuilder separatorBuilder,required int itemCount,bool addAutomaticKeepAlives = true,bool addRepaintBoundaries = true,bool addSemanticIndexes = true,super.cacheExtent,super.dragStartBehavior,super.keyboardDismissBehavior,super.restorationId,super.clipBehavior,}) : assert(itemCount >= 0),itemExtent = null,prototypeItem = null,childrenDelegate = SliverChildBuilderDelegate((BuildContext context, int index) {final int itemIndex = index ~/ 2;if (index.isEven) {return itemBuilder(context, itemIndex);}return separatorBuilder(context, itemIndex);},findChildIndexCallback: findChildIndexCallback,childCount: _computeActualChildCount(itemCount),addAutomaticKeepAlives: addAutomaticKeepAlives,addRepaintBoundaries: addRepaintBoundaries,addSemanticIndexes: addSemanticIndexes,semanticIndexCallback: (Widget widget, int index) {return index.isEven ? index ~/ 2 : null;},),super(semanticChildCount: itemCount,);

5. ListView.custom构造函数注释部分

  /// 使用自定义子模型创建一个可滚动的线性组件数组。////// 例如,自定义子模型可以控制用于估计实际不可见的子组件大小的算法。////// {@tool snippet}////// 这个 [ListView] 使用自定义的 [SliverChildBuilderDelegate] 来支持子组件的重新排序。////// ```dart/// class MyListView extends StatefulWidget {///   const MyListView({super.key});//////   @override///   State<MyListView> createState() => _MyListViewState();/// }////// class _MyListViewState extends State<MyListView> {///   List<String> items = <String>['1', '2', '3', '4', '5'];//////   void _reverse() {///     setState(() {///       items = items.reversed.toList();///     });///   }//////   @override///   Widget build(BuildContext context) {///     return Scaffold(///       body: SafeArea(///         child: ListView.custom(///           childrenDelegate: SliverChildBuilderDelegate(///             (BuildContext context, int index) {///               return KeepAlive(///                 data: items[index],///                 key: ValueKey<String>(items[index]),///               );///             },///             childCount: items.length,///             findChildIndexCallback: (Key key) {///               final ValueKey<String> valueKey = key as ValueKey<String>;///               final String data = valueKey.value;///               return items.indexOf(data);///             }///           ),///         ),///       ),///       bottomNavigationBar: BottomAppBar(///         child: Row(///           mainAxisAlignment: MainAxisAlignment.center,///           children: <Widget>[///             TextButton(///               onPressed: () => _reverse(),///               child: const Text('Reverse items'),///             ),///           ],///         ),///       ),///     );///   }/// }////// class KeepAlive extends StatefulWidget {///   const KeepAlive({///     required Key key,///     required this.data,///   }) : super(key: key);//////   final String data;//////   @override///   State<KeepAlive> createState() => _KeepAliveState();/// }////// class _KeepAliveState extends State<KeepAlive> with AutomaticKeepAliveClientMixin{///   @override///   bool get wantKeepAlive => true;//////   @override///   Widget build(BuildContext context) {///     super.build(context);///     return Text(widget.data);///   }/// }/// ```/// {@end-tool}const ListView.custom({super.key,super.scrollDirection,super.reverse,super.controller,super.primary,super.physics,super.shrinkWrap,super.padding,this.itemExtent,this.prototypeItem,required this.childrenDelegate,super.cacheExtent,super.semanticChildCount,super.dragStartBehavior,super.keyboardDismissBehavior,super.restorationId,super.clipBehavior,}) : assert(itemExtent == null || prototypeItem == null,// 你只能传入 itemExtent 或 prototypeItem,不能两者都传'You can only pass itemExtent or prototypeItem, not both',);

6. itemExtent属性

/// {@template flutter.widgets.list_view.itemExtent}
/// 如果非空,则强制子组件在滚动方向上具有给定的范围。
///
/// 指定 [itemExtent] 比让子组件确定自己的范围更高效,因为滚动机制可以利用对子组件范围的预知来节省工作,
/// 例如当滚动位置发生剧变时。
///
/// 另请参阅:
///
///  * [SliverFixedExtentList],当提供此属性时内部使用的 sliver。它约束其盒子子组件在主轴上具有特定的给定范围。
///  * [prototypeItem] 属性,它允许强制子组件的范围与给定的组件相同。
/// {@endtemplate}
final double? itemExtent;

7. prototypeItem属性

/// {@template flutter.widgets.list_view.prototypeItem}
/// 如果非空,则强制子组件在滚动方向上具有与给定组件相同的范围。
///
/// 指定 [prototypeItem] 比让子组件确定自己的范围更高效,因为滚动机制可以利用对子组件范围的预知来节省工作,例如当滚动位置发生剧变时。
///
/// 另请参阅:
///
///  * [SliverPrototypeExtentList],当提供此属性时内部使用的 sliver。它约束其盒子子组件在主轴上具有与原型项相同的范围。
///  * [itemExtent] 属性,它允许强制子组件的范围为给定的值
/// {@endtemplate}
final Widget? prototypeItem;

8. childrenDelegate属性

  /// 为 [ListView] 提供子组件的委托。////// [ListView.custom] 构造函数让你可以明确地指定此委托。[ListView] 和 [ListView.builder] 构造函数创建一个 [childrenDelegate],/// 分别包装给定的 [List] 和 [IndexedWidgetBuilder]。final SliverChildDelegate childrenDelegate;

9. buildChildLayout

  Widget buildChildLayout(BuildContext context) {if (itemExtent != null) {return SliverFixedExtentList(delegate: childrenDelegate,itemExtent: itemExtent!,);} else if (prototypeItem != null) {return SliverPrototypeExtentList(delegate: childrenDelegate,prototypeItem: prototypeItem!,);}return SliverList(delegate: childrenDelegate);}

buildChildLayout 方法是 ListView 类中的一个重要方法,它负责构建 ListView 的子布局。

这个方法接收一个 BuildContext 对象作为参数,然后根据 ListView 的属性来决定使用哪种类型的滑动列表。

  • 如果 itemExtent 属性不为 null,则使用 SliverFixedExtentListSliverFixedExtentList 是一种所有子项都有固定长度的滑动列表。itemExtent 属性表示每个子项的长度。
  • 如果 itemExtentnull,但 prototypeItem 不为 null,则使用 SliverPrototypeExtentListSliverPrototypeExtentList 是一种所有子项都根据原型项 prototypeItem 来决定长度的滑动列表。
  • 如果 itemExtentprototypeItem 都为 null,则使用 SliverListSliverList 是一种子项长度可以不同的滑动列表。

debugFillProperties 方法用于在调试时提供有关 ListView 的信息。这个方法会将 itemExtent 属性添加到 DiagnosticPropertiesBuilder 对象中。

_computeActualChildCount 是一个静态辅助方法,用于计算 ListView.separated 构造函数的实际子项数。这个方法接收一个 itemCount 参数,然后返回 itemCount * 2 - 10 中的较大值。这是因为在 ListView.separated 中,每两个子项之间都有一个 分隔器,所以实际的子项数是 itemCount 的两倍减一。

10. 其它部分

void debugFillProperties(DiagnosticPropertiesBuilder properties) {super.debugFillProperties(properties);properties.add(DoubleProperty('itemExtent', itemExtent, defaultValue: null));}// 来计算separated构造函数的实际 child 数的帮助方法。static int _computeActualChildCount(int itemCount) {return math.max(0, itemCount * 2 - 1);}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/141391.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在电脑和手机设备上编辑只读 PDF

我们大多数人更喜欢以 PDF 格式共享和查看文件&#xff0c;因为它更专业、更便携。但是&#xff0c;通常情况下您被拒绝访问除查看之外的内容编辑、复制或评论。如果您希望更好地控制您的 PDF 或更灵活地编辑它&#xff0c;请弄清楚为什么您的 PDF 是只读的&#xff0c;然后使用…

STM32常见符号解释定义(持续更新)

什么是FIFO&#xff1f; FIFO是一种先进先出的数据结构&#xff0c;全称为First In First Out&#xff0c;即先进先出。在计算机中&#xff0c;FIFO通常用于缓存和队列中&#xff0c;它可以确保先进入队列的数据先被处理&#xff0c;后进入队列的数据后被处理。在硬件电路…

无监督学习的集成方法:相似性矩阵的聚类

在机器学习中&#xff0c;术语Ensemble指的是并行组合多个模型&#xff0c;这个想法是利用群体的智慧&#xff0c;在给出的最终答案上形成更好的共识。 这种类型的方法已经在监督学习领域得到了广泛的研究和应用&#xff0c;特别是在分类问题上&#xff0c;像RandomForest这样…

解决npm报错Error: error:0308010C:digital envelope routines::unsupported

解决npm报错Error: error:0308010C:digital envelope routines::unsupported。 解决办法&#xff1b;终端执行以下命令&#xff08;windows&#xff09;&#xff1a; set NODE_OPTIONS--openssl-legacy-provider然后再执行 npm命令成功&#xff1a;

使用dgl库实现GCN【官方案例】

学习目的 学习使用gnn进行节点分类的基本工作流程&#xff0c;即预测图中节点的类别。 关于GCN节点分类的综述 在图数据上最流行和广泛采用的任务之一是节点分类&#xff0c;其中模型需要预测每个节点的真实类别。 在图神经网络之前&#xff0c;许多被提出的方法要么单独使用…

Spark3.0中的AOE、DPP和Hint增强

1 Spark3.0 AQE Spark 在 3.0 版本推出了 AQE&#xff08;Adaptive Query Execution&#xff09;&#xff0c;即自适应查询执行。AQE 是 Spark SQL 的一种动态优化机制&#xff0c;在运行时&#xff0c;每当 Shuffle Map 阶段执行完毕&#xff0c;AQE 都会结合这个阶段的统计信…

【KVM-7】KVM管理工具

前言 大家好&#xff0c;我是秋意零。 &#x1f47f; 简介 &#x1f3e0; 个人主页&#xff1a; 秋意零&#x1f525; 账号&#xff1a;全平台同名&#xff0c; 秋意零 账号创作者、 云社区 创建者&#x1f9d1; 个人介绍&#xff1a;在校期间参与众多云计算相关比赛&#x…

微信小程序广告banner、滚动屏怎么做?

使用滑块视图容器swiper和swiper-item可以制作滚动屏&#xff0c;代码如下&#xff1a; wxml: <swiper indicator-dots indicator-color"rgba(255,255,255,0.5)" indicator-active-color"white" autoplay interval"3000"><swiper-ite…

【人工智能Ⅰ】6-机器学习之分类

【人工智能Ⅰ】6-机器学习之分类 6-1 机器学习在人工智能中的地位 学习能力是智能的本质 人工智能 > 机器学习 > 深度学习 什么是机器学习&#xff1f; baidu&#xff1a;多领域交叉学科&#xff08;做什么&#xff09; wiki&#xff1a;the study of algorithms and…

OpenCV踩坑笔记使用笔记入门笔记整合SpringBoot笔记大全

springboot开启摄像头抓拍照片并上传实现&问题记录 NotAllowedErrot: 请求的媒体源不能使用&#xff0c;以下情况会返回该错误: 当前页面内容不安全&#xff0c;没有使用HTTPS没有通过用户授权NotFoundError: 没有找到指定的媒体通道NoReadableError: 访问硬件设备出错Ov…

计算机基础知识50

数据的增删改查(insert update delete select) # 用户列表的展示&#xff1a; # 把数据表中得用户数据都给查询出来展示在页面上 1. 查询 from app01 import models models.UserInfo.objects.all() # 查询所有的字段信息和数据 resmodels.UserInfo.objects.first() # 查询…

什么是状态机?

什么是状态机&#xff1f; 定义 我们先来给出状态机的基本定义。一句话&#xff1a; 状态机是有限状态自动机的简称&#xff0c;是现实事物运行规则抽象而成的一个数学模型。 先来解释什么是“状态”&#xff08; State &#xff09;。现实事物是有不同状态的&#xff0c;例…

Spark SQL 每年的1月1日算当年的第一个自然周, 给出日期,计算是本年的第几周

一、问题 按每年的1月1日算当年的第一个自然周 (遇到跨年也不管&#xff0c;如果1月1日是周三&#xff0c;那么到1月5号&#xff08;周日&#xff09;算是本年的第一个自然周, 如果按周一是一周的第一天) 计算是本年的第几周&#xff0c;那么 spark sql 如何写 ? 二、分析 …

P6入门:项目初始化9-项目详情之资源 Resource

前言 使用项目详细信息查看和编辑有关所选项目的详细信息&#xff0c;在项目创建完成后&#xff0c;初始化项目是一项非常重要的工作&#xff0c;涉及需要设置的内容包括项目名&#xff0c;ID,责任人&#xff0c;日历&#xff0c;预算&#xff0c;资金&#xff0c;分类码等等&…

npm install导致的OOM解决方案

文章目录 问题记录解决方法Linux重启排查方法 如何排查Linux自动重启的原因 问题记录 我在华为云服务器配置npm开发环境的时候&#xff0c; SSH远程连接一直掉线&#xff0c;无奈提了工单&#xff0c;被告知是NPM install导致的OOM问题。无语了&#xff0c;破NPM还有这个问题呢…

SOME/IP学习笔记2

1. SOME/IP 协议 SOME/IP目前支持UDP&#xff08;用户传输协议&#xff09;和TCP&#xff08;传输控制协议&#xff09;&#xff0c; PS:UDP和TCP区别如下 TCP面向连接的&#xff0c;可靠的数据传输服务&#xff1b;UDP面向无连接的&#xff0c;尽最大努力的数据传输服务&…

详细推导MOSFET的跨导、小信号模型、输出阻抗、本征增益

目录 前言 什么是跨导 什么是小信号模型 什么是输入阻抗和输出阻抗 什么是MOS管的输出阻抗 什么是MOS管的本征增益 共源极放大电路的输入和输出阻抗 一些其它MOS拓扑电路的增益 负载为恒流源 负载为二极管 前言 相信很多人在学习集成电路领域的时候 都对MOS管的…

Python 框架学习 Django篇 (十) Redis 缓存

开发服务器系统的时候&#xff0c;程序的性能是至关重要的。经过我们前面框架的学习&#xff0c;得知一个请求的处理基本分为接受http请求、数据库处理、返回json数据&#xff0c;而这3个部分中就属链接数据库请求的响应速度最慢&#xff0c;因为数据库操作涉及到数据库服务处理…

怎么在uni-app中使用Vuex(第一篇)

Vuex简介 vuex的官方网址如下 https://vuex.vuejs.org/zh/ 阅读官网请带着几个问题去阅读&#xff1a; vuex用于什么场景&#xff1f;vuex能给我们带来什么好处&#xff1f;我们为什么要用vuex?vuex如何实现状态集中管理&#xff1f; Vuex用于哪些场景&#xff1f; 组件之…

[量化投资-学习笔记012]Python+TDengine从零开始搭建量化分析平台-策略回测

上一章节《MACD金死叉策略回测》中&#xff0c;对平安银行这只股票&#xff0c;按照金死叉策略进行了回测。 但通常我们的股票池中有许多股票&#xff0c;每完成一个交易策略都需要对整个股票池进行回测。 下面使用简单的轮询&#xff0c;对整个股票池进行回测。 # 计算单只…