Flutter 实现 列表滑动过程控件停靠效果 学习

打印 上一主题 下一主题

主题 1021|帖子 1021|积分 3063

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
实现一个 Flutter 应用程序,利用 `Sliver` 系列组件来创建具有滚动效果的复杂布局。利用 `NestedScrollView` 和 `SliverPersistentHeader` 来实现固定和动态的头部效果,以及一个可滚动的列表。

前置知识点学习

SingleTickerProviderStateMixin

`SingleTickerProviderStateMixin` 是 Flutter 中一个常用的混入(mixin),重要用于 动画控制器(`AnimationController`) 的管理。它通常与 `State` 类结合利用,为动画提供 `Ticker`,从而高效管理动画的帧调用。

什么是 `Ticker`?



  • `Ticker` 是 Flutter 动画系统的根本,它会按照屏幕刷新率(通常是每秒 60 次)调用一个回调函数,帮助你在每一帧更新动画。


  • 利用 `Ticker` 可以让动画与设备的帧同步,从而实现平滑的动画效果。

`SingleTickerProviderStateMixin` 的作用?

`SingleTickerProviderStateMixin` 是 `TickerProvider` 的一个实现,专门用于只需要一个 `Ticker` 的动画场景。它避免了手动管理 `Ticker` 的麻烦,并确保动画与帧同步。

典范利用场景

`SingleTickerProviderStateMixin` 通常用于需要一个 `AnimationController` 的场景,例如:


  • 页面切换动画。


  • 简单的补间动画。


  • 小型的交互动画。

利用 `SingleTickerProviderStateMixin` 的代码示例

以下是一个完整的例子,展示如何利用 `SingleTickerProviderStateMixin` 创建一个简单的补间动画。
  1. import 'package:flutter/material.dart';
  2. class MyAnimatedWidget extends StatefulWidget {
  3.   @override
  4.   _MyAnimatedWidgetState createState() {
  5.     return _MyAnimatedWidgetState();
  6.   }
  7. }
  8. class _MyAnimatedWidgetState extends State<MyAnimatedWidget>
  9.     with SingleTickerProviderStateMixin {
  10.   late AnimationController _controller; // 动画控制器
  11.   late Animation<double> _animation; // 补间动画
  12.   @override
  13.   Widget build(BuildContext context) {
  14.     return Scaffold(
  15.       appBar: AppBar(title: const Text("SingleTickerProviderStateMixin 示例")),
  16.       body: Center(
  17.         child: Container(
  18.           width: _animation.value, // 动态更新宽度
  19.           height: _animation.value, // 动态更新高度
  20.           color: Colors.blue,
  21.         ),
  22.       ),
  23.     );
  24.   }
  25.   @override
  26.   void dispose() {
  27.     // 销毁动画控制器以释放资源
  28.     _controller.dispose();
  29.     super.dispose();
  30.   }
  31.   @override
  32.   void initState() {
  33.     super.initState();
  34.     // 初始化 AnimationController
  35.     _controller = AnimationController(
  36.       duration: const Duration(seconds: 2), // 动画持续时间
  37.       vsync: this, // 使用 SingleTickerProviderStateMixin 提供的 vsync
  38.     );
  39.     // 使用 Tween 创建补间动画
  40.     _animation = Tween<double>(begin: 0, end: 300).animate(_controller)
  41.       ..addListener(() {
  42.         setState(() {}); // 每帧更新 UI
  43.       });
  44.     // 启动动画
  45.     _controller.forward();
  46.   }
  47. }
复制代码


SliverPersistentHeader

`SliverPersistentHeader` 是 Flutter 中 `Sliver` 系列组件的一部分,用于在滚动视图中创建一个具有长期行为的头部组件。它可以大概在滚动过程中根据需要举行伸缩、冻结或其他效果。

重要特点

长期性:`SliverPersistentHeader` 保留在滚动视图的顶部或底部,即利用户滚动内容,它也可以根据设置保持可见。
动态变化:可以动态调整其高度和内容。常用于实现如折叠效果的应用栏(AppBar)。
灵活性:通过实现 `SliverPersistentHeaderDelegate`,你可以自定义头部的布局和行为。

利用场景

创建一个在用户滚动时可以折叠的应用栏。
实现滚动时固定在顶部的导航栏。
制作具有粘性效果的分段标题。

实现步骤

实现 `SliverPersistentHeaderDelegate`:


  • 这是一个抽象类,你需要实现它的方法以定义头部的行为和外观。
利用 `SliverPersistentHeader`:


  • 将你自定义的 `SliverPersistentHeaderDelegate` 实例传递给它。

代码示例

下面是一个简单的例子,展示如何利用 `SliverPersistentHeader` 创建一个滚动时可以伸缩的头部。
  1. import 'package:flutter/material.dart';
  2. class SliverPersistentHeaderExample extends StatelessWidget {
  3.   const SliverPersistentHeaderExample({super.key});
  4.   @override
  5.   Widget build(BuildContext context) {
  6.     return Scaffold(
  7.       appBar: AppBar(title: const Text("SliverPersistentHeaderExample 示例")),
  8.       body: CustomScrollView(
  9.         slivers: <Widget>[
  10.           SliverPersistentHeader(
  11.             delegate: MySliverAppBarDelegate(
  12.                 minHeight: 100.0,
  13.                 maxHeight: 200.0,
  14.                 child: Container(
  15.                   color: Colors.blue,
  16.                   child: const Center(
  17.                     child: Text(
  18.                       'SliverPersistentHeader',
  19.                       style: TextStyle(color: Colors.white, fontSize: 24),
  20.                     ),
  21.                   ),
  22.                 )),
  23.             pinned: true,
  24.           ),
  25.           SliverList(
  26.               delegate: SliverChildBuilderDelegate(
  27.             (BuildContext context, int index) {
  28.               return ListTile(
  29.                 title: Text('Item #$index'),
  30.               );
  31.             },
  32.             childCount: 50,
  33.           ))
  34.         ],
  35.       ),
  36.     );
  37.   }
  38. }
  39. class MySliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  40.   final double minHeight;
  41.   final double maxHeight;
  42.   final Widget child;
  43.   MySliverAppBarDelegate({
  44.     required this.minHeight,
  45.     required this.maxHeight,
  46.     required this.child,
  47.   });
  48.   @override
  49.   Widget build(
  50.       BuildContext context, double shrinkOffset, bool overlapsContent) {
  51.     return SizedBox.expand(child: child);
  52.   }
  53.   @override
  54.   double get maxExtent => maxHeight;
  55.   @override
  56.   double get minExtent => minHeight;
  57.   @override
  58.   bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
  59.     return true;
  60.   }
  61. }
复制代码
代码剖析

`MySliverAppBarDelegate`:


  • 继承自 `SliverPersistentHeaderDelegate`。


  • 定义了 `minExtent` 和 `maxExtent`,分别表示头部的最小和最大高度。


  • `build` 方法返回一个 `SizedBox.expand`,用于添补父级可用空间。
`SliverPersistentHeader`:


  • 接受一个 `SliverPersistentHeaderDelegate` 实例。


  • `pinned: true` 表示头部


  • `delegate`: 这是 `SliverPersistentHeader` 的焦点属性,它吸收一个 `SliverPersistentHeaderDelegate` 的子类实例。在我们的例子中,它是 `MySliverAppBarDelegate`。这个委托类负责定义头部的内容以及在滚动过程中如何表现。


  • `pinned`: 这个属性决定了头部是否在到达最小高度时固定在顶部。如果设置为 `true`,当用户滚动内容时,头部会固定在视图的顶部,不会继承滚动出屏幕。对于导航栏等需要始终可见的组件,这是一个常见的用法。
 `SliverPersistentHeaderDelegate` 方法


  • `build`: 此方法是每次需要构建头部时调用的。参数 `shrinkOffset` 表示头部已经收缩的间隔,你可以根据这个值动态调整头部的样式或内容。`overlapsContent` 表示头部是否重叠在后续内容上,这个可以用来实现一些复杂的视觉效果。


  • `shouldRebuild`: 这个方法用于确定当某些条件改变时,是否需要重新构建头部。通常情况下,如果头部的内容或布局依赖于外部状态,会返回 `true`。在简单场景中,直接返回 `true` 可以确保头部在每次状态变动时重新构建。

实际应用

通过 `SliverPersistentHeader`,你可以实现许多复杂的 UI 效果,如:


  • 动态高度的应用栏:在滚动过程中,应用栏可以从全屏高度渐渐收缩到固定的高度。


  • 粘性分段标题:在长列表中,分段标题可以在用户滚动到下一个分段时粘附到顶部,直到新的标题到达。


  • 视觉变化效果:根据 `shrinkOffset` 的值,调整头部的透明度、颜色、以致内容布局。

小结


`SliverPersistentHeader` 提供了一个非常灵活和强盛的方式来管理滚动视图中的头部内容。通过结合 `SliverPersistentHeaderDelegate`,开发者可以完全自定义头部在滚动过程中的行为,满足各种复杂的 UI 需求。无论是简单的固定头部,照旧复杂的动态变化效果,`SliverPersistentHeader` 都能提供支持。盼望这些解释和示例能帮助你更好地明确和应用这个功能强盛的组件!

SliverOverlapAbsorber

`SliverOverlapAbsorber` 是 Flutter 的一个高级布局组件,用于处理嵌套滚动视图中的重叠题目。在复杂的滚动布局中,比如有多个 `CustomScrollView` 或 `NestedScrollView` 嵌套时,大概会出现滚动内容重叠的情况。`SliverOverlapAbsorber` 旨在解决这些重叠题目,确保滚动视图可以大概正确表现和滚动。

重要功能



  • 吸收重叠:`SliverOverlapAbsorber` 重要用于吸收滚动过程中产生的重叠地域,如许嵌套的滚动视图可以正确地处理其滚动内容。


  • 协调滚动:在嵌套滚动中,帮助协调差别滚动视图之间的滚动行为。
利用场景



  • 嵌套滚动视图:当有多个滚动视图嵌套在一起时,利用 `SliverOverlapAbsorber` 来处理滚动视图之间的重叠。


  • `NestedScrollView`:通常与 `NestedScrollView` 一起利用,`NestedScrollView` 是一个专门用于处理嵌套滚动视图的组件。

典范布局



在典范的 `NestedScrollView` 利用中,`SliverOverlapAbsorber` 通常共同 `SliverOverlapInjector` 来利用:


  • `SliverOverlapAbsorber`:放置在上层滚动视图,用于吸收重叠地域。


  • `SliverOverlapInjector`:用于在下层滚动视图中重新插入吸收的重叠地域。

代码示例


以下是一个简单的例子,展示如何在 `NestedScrollView` 中利用 `SliverOverlapAbsorber` 和 `SliverOverlapInjector`:
  1. import 'package:flutter/material.dart';
  2. class NestedScrollViewExample extends StatelessWidget {
  3.   const NestedScrollViewExample({super.key});
  4.   @override
  5.   Widget build(BuildContext context) {
  6.     return Scaffold(
  7.       appBar: AppBar(title: const Text("NestedScrollViewExample 示例")),
  8.       body: NestedScrollView(
  9.         headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
  10.           return <Widget>[
  11.             const SliverAppBar(
  12.               expandedHeight: 200.0,
  13.               floating: false,
  14.               pinned: true,
  15.               flexibleSpace: FlexibleSpaceBar(
  16.                 title: Text("NestedScrollView Example"),
  17.               ),
  18.             ),
  19.             SliverOverlapAbsorber(
  20.               handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
  21.               sliver: const SliverAppBar(
  22.                 pinned: true,
  23.                 title: Text("Overlap Absorber"),
  24.               ),
  25.             )
  26.           ];
  27.         },
  28.         body: Builder(
  29.           builder: (BuildContext context) {
  30.             return CustomScrollView(
  31.               slivers: <Widget>[
  32.                 SliverOverlapInjector(
  33.                   handle:
  34.                       NestedScrollView.sliverOverlapAbsorberHandleFor(context),
  35.                 ),
  36.                 SliverList(
  37.                   delegate: SliverChildBuilderDelegate(
  38.                     (BuildContext context, int index) {
  39.                       return ListTile(
  40.                         title: Text('Item #$index'),
  41.                       );
  42.                     },
  43.                     childCount: 30,
  44.                   ),
  45.                 ),
  46.               ],
  47.             );
  48.           },
  49.         ),
  50.       ),
  51.     );
  52.   }
  53. }
复制代码


代码剖析

`SliverOverlapAbsorber`:


  • `handle`:`SliverOverlapAbsorber` 利用一个 `OverlapAbsorberHandle` 来管理重叠地域的状态。这个句柄是通过 `NestedScrollView.sliverOverlapAbsorberHandleFor(context)` 方法获取的。


  • `sliver`:这个参数是吸收重叠的 `Sliver`,通常是一个 `SliverAppBar` 或其他 `Sliver` 组件。
`SliverOverlapInjector`:


  • 用于在下层滚动视图中重新插入由 `SliverOverlapAbsorber` 吸收的重叠地域。


  • 也利用相同的 `OverlapAbsorberHandle`,确保重叠地域在差别滚动视图之间正确协调。

如何工作

布局过程:


  • `SliverOverlapAbsorber` 在上层滚动视图中捕捉重叠地域。这通常发生在 `NestedScrollView` 的 `headerSliverBuilder` 中。


  • `SliverOverlapInjector` 在下层滚动视图中重新插入这些重叠地域,确保布局正确。这通常在 `NestedScrollView` 的 `body` 中。
滚动协调:


  • 在嵌套滚动视图中,`SliverOverlapAbsorber` 和 `SliverOverlapInjector` 协同工作,确保滚动事件在差别层级的滚动视图中正确流传。


  • 这对于实现复杂的滚动效果(如嵌套的 `ListView` 或 `GridView`)非常重要。

典范应用



  • 复杂的应用栏:当应用栏中包含多个层级的滚动内容时,利用 `SliverOverlapAbsorber` 可以确保内容在滚动时不会被错误地覆盖或隐藏。


  • 嵌套列表:在 `NestedScrollView` 中嵌套 `ListView` 或 `GridView` 时,利用这些组件可以管理滚动和布局之间的复杂交互。


小结


`SliverOverlapAbsorber` 和 `SliverOverlapInjector` 提供了一个强盛的机制来处理复杂的嵌套滚动场景。在需要协调多个滚动视图并确保内容不会被错误覆盖时,这些工具非常有效。通过正确利用这些组件,你可以创建流通且功能丰富的用户界面,顺应各种复杂的布局需求。盼望这个解释可以大概帮助你更好地明确和利用这些组件!


FloatingHeaderSnapConfiguration

`FloatingHeaderSnapConfiguration` 是 Flutter 中 `SliverAppBar` 的一个设置项,用于定义浮动头部在滚动视图中的行为。特殊是在利用 `SliverAppBar` 实现滚动效果时,它用于控制头部的浮动和捕捉行为。

 重要功能



  • 浮动行为:当用户快速向上或向下滚动时,头部可以在滚动停止后主动浮动到一个捕捉位置。这种行为通常用于创建更自然的用户体验。


  • 捕捉(Snap):在滚动停止时,头部会主动捕捉到一个预定义的位置,比如完全展开或完全折叠。
利用场景



  • 用户体验优化:在应用中利用 `FloatingHeaderSnapConfiguration`,可以使得 `SliverAppBar` 在滚动时具有更平滑的捕捉效果,提拔用户体验。


  • 复杂的滚动布局:当你需要在复杂的滚动布局中确保头部在滚动停止时处于一个可预测的位置时,非常有效。

如何利用

`FloatingHeaderSnapConfiguration` 通常与 `SliverAppBar` 一起利用,特殊是当 `SliverAppBar` 的 `floating` 属性设置为 `true` 时。为了让捕捉行为收效,`snap` 属性也需要设置为 `true`。

代码示例

以下是一个利用 `SliverAppBar` 实现浮动和捕捉行为的示例:
  1. import 'package:flutter/material.dart';
  2. class SliverAppBarWithSnap extends StatelessWidget {
  3.   const SliverAppBarWithSnap({super.key});
  4.   @override
  5.   Widget build(BuildContext context) {
  6.     return Scaffold(
  7.       body: CustomScrollView(
  8.         slivers: <Widget>[
  9.           SliverAppBar(
  10.             title: const Text('Floating Header with Snap'),
  11.             floating: true,
  12.             snap: true,
  13.             expandedHeight: 200.0,
  14.             flexibleSpace: FlexibleSpaceBar(
  15.                 background: Image.asset(
  16.               "static/demo.png",
  17.               fit: BoxFit.cover,
  18.             )),
  19.           ),
  20.           SliverList(
  21.               delegate: SliverChildBuilderDelegate(
  22.             (BuildContext context, int index) {
  23.               return ListTile(
  24.                 title: Text('Item #$index'),
  25.               );
  26.             },
  27.             childCount: 50,
  28.           ))
  29.         ],
  30.       ),
  31.     );
  32.   }
  33. }
复制代码
 代码剖析



  • `floating: true`:启用 `SliverAppBar` 的浮动行为,这意味着当用户向上滚动时,`AppBar` 会立即表现。


  • `snap: true`:启用捕捉行为,确保当用户快速滚动并释放时,`AppBar` 会主动捕捉到完全展开或完全收缩的状态。


  • `FlexibleSpaceBar`:用于定义 `SliverAppBar` 的可扩展背景地域。
 注意事项



  • 前提条件:`snap` 属性只能在 `floating` 为 `true` 时利用,因为捕捉行为依赖于 `AppBar` 的浮动特性。


  • 用户体验:利用 `FloatingHeaderSnapConfiguration` 可以使滚动体验更加流通自然,但需要根据应用的具体需求来决定是否启用此功能。
通过利用 `FloatingHeaderSnapConfiguration`,你可以在 Flutter 应用中实现一个具有浮动和捕捉行为的 `SliverAppBar`,从而提拔用户的滚动体验。


SizedBox

`SizedBox` 是 Flutter 中一个非经常用的布局组件,用于在布局中创建具有特定宽度和高度的盒子。它可以用于添加特定的空缺间距、限制子组件的尺寸,大概充当占位符。
 重要功能

1.设置尺寸:`SizedBox` 可以指定其宽度和高度,以控制其在布局中的大小。
2.限制子组件尺寸:当 `SizedBox` 包含子组件时,它会限制子组件的尺寸为 `SizedBox` 的大小。
3.占位作用:在没有子组件的情况下,`SizedBox` 可以用作占位符,占据特定的空间。
4.间距:通过设置宽度或高度为零的 `SizedBox`,可以在布局中创建水平或垂直的间距。
 利用场景

1.调整布局间距:在布局中插入 `SizedBox` 以创建一致的间距。
2.限制子组件尺寸:逼迫子组件在特定的宽度和高度内表现。
3.创建占位符:在布局中保留一个特定大小的空间,临时不表现内容。


示例代码

以下是一些常见的 `SizedBox` 用法示例:
  1. import 'package:flutter/material.dart';
  2. class SizedBoxExample extends StatelessWidget {
  3.   const SizedBoxExample({super.key});
  4.   @override
  5.   Widget build(BuildContext context) {
  6.     return Scaffold(
  7.       appBar: AppBar(
  8.         title: const Text('SizedBox Example'),
  9.       ),
  10.       body: Column(
  11.         children: <Widget>[
  12.           // 使用 SizedBox 设置固定的宽度和高度
  13.           SizedBox(
  14.             width: 100.0,
  15.             height: 100.0,
  16.             child: Container(
  17.               color: Colors.blue,
  18.               child: const Center(child: Text('100x100')),
  19.             ),
  20.           ),
  21.           // 使用 SizedBox 作为间距
  22.           const SizedBox(
  23.             height: 20.0,
  24.           ),
  25.           // 限制子组件宽度
  26.           SizedBox(
  27.             width: 200.0,
  28.             child: Container(
  29.               color: Colors.greenAccent,
  30.               child: const Text('Width limited to 200',
  31.                   textAlign: TextAlign.center),
  32.             ),
  33.           ),
  34.           // 使用 SizedBox 作为占位符
  35.           const SizedBox(height: 50.0),
  36.           const Text('Below is a 50px space'),
  37.         ],
  38.       ),
  39.     );
  40.   }
  41. }
复制代码

代码剖析



  • 尺寸设置:`SizedBox(width: 100.0, height: 100.0)` 创建一个 100x100 的盒子,此中的子组件会被限制在这个尺寸内。


  • 间距:`SizedBox(height: 20.0)` 用于在两个组件之间创建 20 像素的垂直间距。


  • 宽度限制:`SizedBox(width: 200.0)` 限制子组件的宽度为 200 像素,高度不受限制。


  • 占位符:`SizedBox(height: 50.0)` 保留一个 50 像素的垂直空间。
注意事项



  • 无子组件时尺寸:如果 `SizedBox` 没有子组件,它将只占据设置的宽度和高度。


  • 无穷尺寸:`SizedBox.expand()` 可以创建一个尽大概大的盒子,添补父组件允许的空间。


  • 零尺寸:`SizedBox.shrink()` 可以创建一个尺寸为零的盒子,通常用于需要占位但不盼望占据实际空间。

Expanded

`Expanded` 是 Flutter 中的一个布局小部件,通常用于 `Row`, `Column`, 或 `Flex` 布局中。它的重要作用是调整子组件的尺寸,以添补父组件中的可用空间。在利用 `Expanded` 时,子组件会在主轴方向上被拉伸,以占据尽大概多的空间。

 重要功能

1.添补可用空间:`Expanded` 会让其子组件在布局的主轴方向上添补尽大概多的可用空间。
2.灵活分配空间:当多个 `Expanded` 小部件出现在同一个父布局中时,它们会根据各自的权重分配空间。
3.简化布局:通过利用 `Expanded`,可以轻松实现相应式布局,无需精确盘算尺寸。

利用场景



  • 创建相应式布局:在需要根据屏幕大小动态调整组件大小时,`Expanded` 非常有效。


  • 均匀分布空间:在 `Row` 或 `Column` 中需要均匀分布子组件时。


  • 占据剩余空间:当需要一个组件占据父布局中所有剩余的可用空间时。

示例代码

以下是一些利用 `Expanded` 的示例:
  1. import 'package:flutter/material.dart';
  2. class ExpandedExampleDemo extends StatelessWidget {
  3.   const ExpandedExampleDemo({super.key});
  4.   @override
  5.   Widget build(BuildContext context) {
  6.     return Scaffold(
  7.       appBar: AppBar(
  8.         title: const Text('Expanded Example'),
  9.       ),
  10.       body: Column(
  11.         children: <Widget>[
  12.           Container(
  13.             color: Colors.redAccent,
  14.             height: 100.0,
  15.             child: const Center(child: Text('Fixed Height')),
  16.           ),
  17.           Expanded(
  18.               child: Container(
  19.                   color: Colors.blue,
  20.                   child: const Center(child: Text('Expanded')))),
  21.           Container(
  22.             color: Colors.green,
  23.             height: 100.0,
  24.             child: const Center(child: Text('Fixed Height')),
  25.           ),
  26.         ],
  27.       ),
  28.     );
  29.   }
  30. }
复制代码

代码剖析



  • 固定大小的组件:顶部和底部的 `Container` 组件具有固定的高度(100.0),它们不会被 `Expanded` 影响。


  • `Expanded` 的应用:中间的 `Container` 被 `Expanded` 包裹,这意味着它将占据父 `Column` 中所有剩余的可用空间。

多个 `Expanded` 的情况

当有多个 `Expanded` 小部件时,它们会均匀分配父布局中的可用空间,大概根据 `flex` 参数的值按比例分配:
  1. Column(
  2.   children: <Widget>[
  3.     Expanded(
  4.       flex: 1,
  5.       child: Container(color: Colors.blue, child: Text('1 Flex')),
  6.     ),
  7.     Expanded(
  8.       flex: 2,
  9.       child: Container(color: Colors.green, child: Text('2 Flex')),
  10.     ),
  11.   ],
  12. )
复制代码
解释



  • `flex` 属性:用于定义 `Expanded` 在主轴方向上占据的比例空间。上例中,绿色容器将占据两倍于蓝色容器的空间。
注意事项



  • 只能在 `Flex` 布局中利用:`Expanded` 只能用在 `Row`, `Column`, 或 `Flex` 中,因为它们在布局时思量主轴方向。


  • 交叉轴尺寸:`Expanded` 只会影响其子组件在主轴方向上的尺寸。对于交叉轴(即 `Row` 中的垂直方向和 `Column` 中的水平方向),你需要明确地设置尺寸属性(如 `width` 或 `height`)来控制。


  • 共同 `Spacer` 利用:`Spacer` 是一个特殊的 `Expanded`,用于在布局中创建空缺空间。它可以帮助你在不需要具体组件内容的地方,灵活调整组件之间的间隔。


示例代码:`Spacer` 的利用

  1. import 'package:flutter/material.dart';
  2. class ExpandedWithSpacerExample extends StatelessWidget {
  3.   const ExpandedWithSpacerExample({super.key});
  4.   @override
  5.   Widget build(BuildContext context) {
  6.     return Scaffold(
  7.       appBar: AppBar(
  8.         title: const Text('Expanded with Spacer Example'),
  9.       ),
  10.       body: Row(
  11.         children: <Widget>[
  12.           Container(
  13.             width: 100.0,
  14.             color: Colors.red,
  15.             child: const Center(child: Text('Left')),
  16.           ),
  17.           const Spacer(), // 通过 Spacer 创建灵活的空白空间
  18.           Container(
  19.             width: 100.0,
  20.             color: Colors.green,
  21.             child: const Center(child: Text('Right')),
  22.           ),
  23.         ],
  24.       ),
  25.     );
  26.   }
  27. }
复制代码


代码剖析



  • `Spacer` 的作用:在 `Row` 中利用 `Spacer` 可以在两个容器之间创建一个弹性空缺地域,使得它们在布局中保持肯定的间隔。`Spacer` 本质上是一个 `Expanded`,但不包含任何子组件。
注意事项



  • 与 `Flexible` 的区别:`Flexible` 也是一个用于调整子组件尺寸的布局小部件。与 `Expanded` 差别的是,`Flexible` 可以让子组件在不需要填满所有可用空间时根据需要调整大小。`Expanded` 是一个 `Flexible` 的快捷实现,其 `flexFit` 属性默认为 `FlexFit.tight`。


  • 布局性能:利用 `Expanded` 和 `Spacer` 可以帮助优化布局性能,因为它们简化了空间分配逻辑,减少了手动调整和盘算的需求。
总结
 


`Expanded` 和 `Spacer` 是 Flutter 布局系统中强盛且灵活的工具。通过这些小部件,你可以轻松创建相应式布局,确保组件在差别屏幕尺寸和方向上都能公道地表现和排列。了解如何利用这些工具将帮助你更高效地设计和实现复杂的用户界面。



列表滑动过程控件停靠效果代码学习

  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/rendering.dart';
  3. import 'dart:math' as math;
  4. import 'package:flutter/material.dart' as W;
  5. class SliverListDemoPage2222 extends StatefulWidget {
  6.   const SliverListDemoPage2222({super.key});
  7.   @override
  8.   _MySliverListDemoPageState createState() {
  9.     return _MySliverListDemoPageState();
  10.   }
  11. }
  12. class _MySliverListDemoPageState extends State<SliverListDemoPage2222>
  13.     with SingleTickerProviderStateMixin {
  14.   int listCount = 30;
  15.   @override
  16.   Widget build(BuildContext context) {
  17.     return Scaffold(
  18.       appBar: AppBar(
  19.         title: const Text("SliverListDemoPage"),
  20.       ),
  21.       body: NestedScrollView(
  22.         physics: const AlwaysScrollableScrollPhysics(),
  23.         headerSliverBuilder: _sliverBuilder,
  24.         body: CustomScrollView(
  25.           slivers: [
  26.             W.Builder(
  27.               builder: (context) {
  28.                 return SliverOverlapInjector(
  29.                     handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
  30.                         context));
  31.               },
  32.             ),
  33.             SliverList(
  34.               delegate: SliverChildBuilderDelegate(
  35.                 (context, index) {
  36.                   return Card(
  37.                     child: Container(
  38.                       height: 60,
  39.                       padding: const EdgeInsets.only(left: 10),
  40.                       alignment: Alignment.centerLeft,
  41.                       child: Text("Item $index"),
  42.                     ),
  43.                   );
  44.                 },
  45.                 childCount: 100,
  46.               ),
  47.             )
  48.           ],
  49.         ),
  50.       ),
  51.     );
  52.   }
  53.   List<Widget> _sliverBuilder(BuildContext context, bool innerBoxIsScrolled) {
  54.     return <Widget>[
  55.       SliverPersistentHeader(
  56.         delegate: MySliverHeaderDelegate(
  57.             maxHeight: 200,
  58.             minHeight: 200,
  59.             vSync: this,
  60.             snapConfig: FloatingHeaderSnapConfiguration(
  61.               curve: Curves.bounceInOut,
  62.               duration: const Duration(milliseconds: 10),
  63.             ),
  64.             child: Container(
  65.               color: Colors.redAccent,
  66.             )),
  67.       ),
  68.       SliverOverlapAbsorber(
  69.         handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
  70.         sliver: SliverPersistentHeader(
  71.             pinned: true,
  72.             delegate: MySliverHeaderDelegate(
  73.                 maxHeight: 60,
  74.                 minHeight: 60,
  75.                 changeSize: true,
  76.                 vSync: this,
  77.                 snapConfig: FloatingHeaderSnapConfiguration(
  78.                   curve: Curves.bounceInOut,
  79.                   duration: const Duration(milliseconds: 10),
  80.                 ),
  81.                 builder: (BuildContext context, double shrinkOffset,
  82.                     bool overlapsContent) {
  83.                   ///根据数值计算偏差
  84.                   var lr = 10 - shrinkOffset / 60 * 10;
  85.                   return SizedBox.expand(
  86.                     child: Padding(
  87.                       padding: EdgeInsets.only(
  88.                           bottom: 10, left: lr, right: lr, top: lr),
  89.                       child: Row(
  90.                         mainAxisSize: MainAxisSize.max,
  91.                         children: <Widget>[
  92.                           Expanded(
  93.                             child: Container(
  94.                               alignment: Alignment.center,
  95.                               color: Colors.orangeAccent,
  96.                               child: TextButton(
  97.                                 onPressed: () {
  98.                                   setState(() {
  99.                                     listCount = 30;
  100.                                   });
  101.                                 },
  102.                                 child: const Text("按键1"),
  103.                               ),
  104.                             ),
  105.                           ),
  106.                           Expanded(
  107.                             child: Container(
  108.                               alignment: Alignment.center,
  109.                               color: Colors.orangeAccent,
  110.                               child: TextButton(
  111.                                 onPressed: () {
  112.                                   setState(() {
  113.                                     listCount = 4;
  114.                                   });
  115.                                 },
  116.                                 child: const Text("按键2"),
  117.                               ),
  118.                             ),
  119.                           ),
  120.                         ],
  121.                       ),
  122.                     ),
  123.                   );
  124.                 })),
  125.       )
  126.     ];
  127.   }
  128. }
  129. class MySliverHeaderDelegate extends SliverPersistentHeaderDelegate {
  130.   MySliverHeaderDelegate(
  131.       {required this.minHeight,
  132.       required this.maxHeight,
  133.       required this.snapConfig,
  134.       required this.vSync,
  135.       this.child,
  136.       this.builder,
  137.       this.changeSize = false});
  138.   final double minHeight;
  139.   final double maxHeight;
  140.   final Widget? child;
  141.   final Builder? builder;
  142.   final bool changeSize;
  143.   final TickerProvider vSync;
  144.   final FloatingHeaderSnapConfiguration snapConfig;
  145.   AnimationController? animationController;
  146.   @override
  147.   TickerProvider get vsync => vSync;
  148.   @override
  149.   Widget build(
  150.       BuildContext context, double shrinkOffset, bool overlapsContent) {
  151.     if (builder != null) {
  152.       return builder!(context, shrinkOffset, overlapsContent);
  153.     }
  154.     return child!;
  155.   }
  156.   @override
  157.   double get maxExtent => math.max(maxHeight, minHeight);
  158.   @override
  159.   double get minExtent => minHeight;
  160.   @override
  161.   bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
  162.     return true;
  163.   }
  164.   @override
  165.   FloatingHeaderSnapConfiguration get snapConfiguration => snapConfig;
  166. }
  167. typedef Builder = Widget Function(
  168.     BuildContext context, double shrinkOffset, bool overlapsContent);
复制代码



typedef

在 Flutter 中,`typedef` 是用于定义函数类型别名的关键字。它可以让你的代码更具可读性和可维护性,尤其是在需要传递复杂函数作为参数时。你提到的这个 `typedef` 定义了一个名为 `Builder` 的函数类型别名。

`typedef` 剖析

  1. typedef Builder = Widget Function(
  2.     BuildContext context, double shrinkOffset, bool overlapsContent);
复制代码
这个 `typedef` 定义了一个函数类型别名 `Builder`,它代表一个函数,该函数:


  • 返回类型:`Widget`,表示此函数返回一个 Flutter 小部件。


  • 参数:
`BuildContext context`: 这是 Flutter 中常见的参数,用来获取树中位置相关的信息,比如主题、方向、媒体查询等。
`double shrinkOffset`: 这个参数通常用于描述某种滚动或动画的偏移量。它是一个双精度浮点数,大概用于表示滚动视图中已滚动的间隔。
`bool overlapsContent`: 这是一个布尔值,通常用来指示某个组件是否与其他内容重叠。例如,在实现自定义滚动效果时,大概需要知道当前组件是否覆盖了其他内容。

利用场景

这个 `typedef` 常用于需要根据滚动或其他动态变化来构建 UI 的场景。例如,在实现自定义的 `Sliver` 组件或其他需要根据滚动偏移量调整表现效果的组件时,这种类型的 `Builder` 函数非常有效。

示例用法

假设你在创建一个自定义的 `Sliver` 组件,需要根据滚动偏移量和是否重叠来动态构建其内容,你大概会如许利用:
  1. import 'package:flutter/material.dart';class CustomSliver extends StatelessWidget {  final Builder builder;  CustomSliver({required this.builder});  @override  Widget build(BuildContext context) {    return SliverPersistentHeader(      delegate: _CustomSliverDelegate(builder),    );  }}class _CustomSliverDelegate extends SliverPersistentHeaderDelegate {  final Builder builder;  _CustomSliverDelegate(this.builder);  @override  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {    // 浣跨敤鎻愪緵鐨?Builder 鏋勫缓 Widget    return builder(context, shrinkOffset, overlapsContent);  }  @override  double get maxExtent => 200.0;  @override  double get minExtent => 100.0;  @override  bool shouldRebuild(covariant _CustomSliverDelegate oldDelegate) {    return true;  }}typedef Builder = Widget Function(
  2.     BuildContext context, double shrinkOffset, bool overlapsContent);
复制代码

解释



  • `CustomSliver`:这是一个简单的自定义组件,它接受一个 `Builder` 类型的参数,用于构建其内容。


  • `_CustomSliverDelegate`:这是一个 `SliverPersistentHeaderDelegate` 的实现,用于构建一个长期化的头部。它利用传入的 `builder` 来构建实际的 UI。
通过这种方式,你可以在创建组件时传入差别的 `Builder` 函数,以便在差别的滚动状态下构建差别的 UI,这为你提供了很大的灵活性和可扩展性。


TickerProvider

在 Flutter 中,`TickerProvider` 是一个接口,用于提供 `Ticker` 对象。`Ticker` 是一个能发出信号以驱动动画的对象。它的工作原理雷同于一个时钟,每当屏幕刷新时(通常是每秒60次),它就会调用一个回调函数。这在 Flutter 中的动画系统中是非常重要的,它帮助动画在每一帧中更新状态。

Ticker 的作用



`Ticker` 在 Flutter 中的重要作用是:
1.驱动动画:通过提供每秒钟多次的时钟滴答来驱动动画。
2.同步帧速率:确保动画与设备的屏幕刷新率同步。
3.管理动画生命周期:在需要时,可以停息、规复或停止动画。

TickerProvider 的利用场景



`TickerProvider` 通常与 `AnimationController` 一起利用,因为 `AnimationController` 需要一个 `Ticker` 来驱动动画的更新。

常见的 TickerProvider 实现


1.`SingleTickerProviderStateMixin`:用于一个组件中只需要一个 `Ticker` 的场景。通常与 `StatefulWidget` 共同利用。
2.`TickerProviderStateMixin`:用于一个组件需要多个 `Ticker` 的场景。如果你有多个动画需要在同一组件中管理,可以利用这个 mixin。

示例代码

以下是如何在 `StatefulWidget` 中利用 `SingleTickerProviderStateMixin` 来驱动一个简单动画的示例:
  1. import 'package:flutter/material.dart';
  2. class MyAnimatedWidget extends StatefulWidget {
  3.   @override
  4.   _MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
  5. }
  6. class _MyAnimatedWidgetState extends State<MyAnimatedWidget> with SingleTickerProviderStateMixin {
  7.   late AnimationController _controller;
  8.   @override
  9.   void initState() {
  10.     super.initState();
  11.     // 初始化 AnimationController
  12.     _controller = AnimationController(
  13.       duration: const Duration(seconds: 2),
  14.       vsync: this, // 提供 Ticker
  15.     )..repeat(); // 循环动画
  16.   }
  17.   @override
  18.   void dispose() {
  19.     _controller.dispose(); // 销毁 AnimationController
  20.     super.dispose();
  21.   }
  22.   @override
  23.   Widget build(BuildContext context) {
  24.     return Scaffold(
  25.       appBar: AppBar(title: Text('TickerProvider Example')),
  26.       body: Center(
  27.         child: RotationTransition(
  28.           turns: _controller, // 使用 AnimationController
  29.           child: Container(
  30.             width: 100.0,
  31.             height: 100.0,
  32.             color: Colors.blue,
  33.           ),
  34.         ),
  35.       ),
  36.     );
  37.   }
  38. }
复制代码
 代码剖析



  • `SingleTickerProviderStateMixin`:通过在 `State` 类中混入这个 mixin,当前类就成为了一个 `TickerProvider`,可以为 `AnimationController` 提供 `Ticker`。


  • `AnimationController`:负责管理动画的生命周期,包括启动、停止和反向移动等。需要 `TickerProvider` 来同步动画。


  • `vsync: this`:`vsync` 参数要求一个 `TickerProvider`,用于减少不须要的动画帧以提高性能。
注意事项



  • 管理生命周期:确保在 `dispose` 方法中调用 `_controller.dispose()` 来释放资源,防止内存走漏。


  • 多个动画:如果需要管理多个动画,思量利用 `TickerProviderStateMixin` 代替 `SingleTickerProviderStateMixin`。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

火影

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表