Flutter如何实现一个增强的 `PageView`,支持自定义页面切换动画。
前置知识点学习
CrossAxisAlignment
`CrossAxisAlignment` 是 Flutter 中用于控制布局子组件在交叉轴(cross axis)方向上的对齐方式的一个枚举类。它主要在 `Flex` 布局模型中使用,比如 `Row` 和 `Column` 这两个常用的小部件。
基本概念
在 Flutter 的布局系统中,`Row` 和 `Column` 这两个小部件使用的是 `Flex` 布局模型。它们的布局轴分别是水平和垂直的:
- 主轴(Main Axis):这是 `Row` 中的水平方向或 `Column` 中的垂直方向。
- 交叉轴(Cross Axis):这是 `Row` 中的垂直方向或 `Column` 中的水平方向。
`CrossAxisAlignment` 控制子组件在交叉轴方向上的对齐方式。
`CrossAxisAlignment` 枚举值
`CrossAxisAlignment.start`:
- 子组件在交叉轴的起始位置对齐。
- 在 `Column` 中,这意味着子组件会对齐到左边缘;在 `Row` 中,子组件会对齐到顶部边缘。
`CrossAxisAlignment.end`:
- 子组件在交叉轴的结束位置对齐。
- 在 `Column` 中,这意味着子组件会对齐到右边缘;在 `Row` 中,子组件会对齐到底部边缘。
`CrossAxisAlignment.center`:
- 子组件在交叉轴上居中对齐。
`CrossAxisAlignment.stretch`:
- 子组件会拉伸以填满交叉轴的可用空间。
- 这要求子组件的宽度(在 `Column` 中)或高度(在 `Row` 中)没有固定的约束。
`CrossAxisAlignment.baseline`:
- 子组件根据其文本的基线对齐。这个选项需要所有子组件都有文本基线。
- 仅适用于 `Row`,因为 `Column` 没有基线概念。
使用示例
下面是一个使用 `CrossAxisAlignment` 的简单示例:
Dart
import 'package:flutter/material.dart';
class CrossAxisAlignmentExample extends StatelessWidget {
const CrossAxisAlignmentExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("CrossAxisAlignment Example")),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
color: Colors.red,
height: 50,
width: 200,
child: const Text('Start Alignment',
style: TextStyle(color: Colors.white)),
),
Container(
color: Colors.green,
height: 50,
width: 150,
child:
const Text('Middle Box', style: TextStyle(color: Colors.white)),
),
Container(
color: Colors.blue,
height: 50,
width: 100,
child: const Text('End Box', style: TextStyle(color: Colors.white)),
),
],
),
);
}
}
解释
`Column` 小部件:
- 设置 `crossAxisAlignment: CrossAxisAlignment.start`,这使得所有子组件在交叉轴(水平)上对齐到左边。
- 每个 `Container` 的宽度不同,但它们都从左侧开始对齐。
`CrossAxisAlignment` 的使用场景非常广泛,尤其是在需要精确控制子组件在交叉轴上的对齐方式时。下面是一些常见的应用场景和考虑事项:
适用场景
基本布局:
- 在构建基本的 UI 布局时,使用 `CrossAxisAlignment` 来确保子组件在交叉轴方向上的对齐方式符合设计需求。例如,在一个垂直的 `Column` 中,可能需要所有按钮左对齐以保持一致性。
响应式布局:
- 当设计需要适应不同屏幕尺寸的布局时,通过调整 `CrossAxisAlignment` 可以确保组件在不同设备上仍然保持良好的对齐和排列。
复杂界面设计:
- 在复杂的界面设计中,可能需要子组件在交叉轴上进行不同的对齐方式,比如某些组件居中对齐,而其他组件需要左对齐或右对齐。
文本对齐:
- 使用 `CrossAxisAlignment.baseline` 可以确保在 `Row` 中包含文本的小部件基于文本的基线对齐,这在处理多行文本或混合文本和图标时特别有用。
注意事项
`CrossAxisAlignment.baseline` 的限制:
- 只能在 `Row` 中使用,因为 `Column` 没有基线概念。使用时确保所有子组件都有文本基线,否则可能会引发布局错误。
`CrossAxisAlignment.stretch` 的使用:
- 适用于希望子组件填充整个交叉轴可用空间的情况。确保子组件没有指定固定的宽度(在 `Column` 中)或高度(在 `Row` 中),否则拉伸效果将无法生效。
与 `MainAxisAlignment` 的组合:
- `CrossAxisAlignment` 通常与 `MainAxisAlignment` 一起使用,以全面控制 `Row` 或 `Column` 的子组件在两个轴上的对齐方式。
影响布局性能:
- 在非常复杂的布局中,频繁调整 `CrossAxisAlignment` 和 `MainAxisAlignment` 可能会对性能产生一定影响,尤其是在构建大型或动态界面时。
通过合理使用 `CrossAxisAlignment`,开发者可以实现灵活且符合设计规范的用户界面布局,确保应用在各种设备和屏幕尺寸上都能提供良好的用户体验。
MainAxisAlignment
`MainAxisAlignment` 是 Flutter 中用于控制布局子组件在主轴(main axis)方向上的对齐方式的一个枚举类。它主要在 `Flex` 布局模型中使用,比如 `Row` 和 `Column` 这两个常用的小部件。
基本概念
在 Flutter 的布局系统中,`Row` 和 `Column` 使用的是 `Flex` 布局模型:
- 主轴(Main Axis):在 `Row` 中是水平方向,在 `Column` 中是垂直方向。
- 交叉轴(Cross Axis):在 `Row` 中是垂直方向,在 `Column` 中是水平方向。
`MainAxisAlignment` 控制子组件在主轴方向上的对齐方式。
`MainAxisAlignment` 枚举值
`MainAxisAlignment.start`:
- 子组件在主轴的起始位置对齐。
- 在 `Row` 中,这意味着子组件会从左到右排列;在 `Column` 中,子组件会从上到下排列。
`MainAxisAlignment.end`:
- 子组件在主轴的结束位置对齐。
- 在 `Row` 中,这意味着子组件会从右到左排列;在 `Column` 中,子组件会从下到上排列。
`MainAxisAlignment.center`:
- 子组件在主轴上居中对齐。
- `MainAxisAlignment.spaceBetween`:
- 子组件在主轴上均匀分布,第一个子组件靠在起始位置,最后一个子组件靠在结束位置,中间的子组件之间有相等的间隔。
`MainAxisAlignment.spaceAround`:
- 子组件在主轴上均匀分布,每个子组件周围都有相等的间隔。第一个和最后一个子组件与起始和结束位置之间的间隔是中间子组件间隔的一半。
`MainAxisAlignment.spaceEvenly`:
- 子组件在主轴上均匀分布,每个子组件之间的间隔相等,包括第一个和最后一个子组件与起始和结束位置之间的间隔。
使用示例
下面是一个使用 `MainAxisAlignment` 的简单示例:
Dart
import 'package:flutter/material.dart';
class MainAxisAlignmentExample extends StatelessWidget {
const MainAxisAlignmentExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("MainAxisAlignment Example")),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
color: Colors.redAccent,
height: 50,
width: 100,
child: const Center(
child: Text('Box 1'),
),
),
Container(
color: Colors.green,
height: 50,
width: 100,
child: const Center(child: Text('Box 2')),
),
Container(
color: Colors.blue,
height: 50,
width: 100,
child: const Center(child: Text('Box 3')),
),
],
),
);
}
}
在上述示例中,我们使用了 `MainAxisAlignment.spaceEvenly` 来确保 `Column` 中的每个 `Container` 在垂直方向上都有相等的间隔。这种布局方式在设计中非常有用,尤其是在需要保持布局对称时。
适用场景
响应式布局:
- 在响应式设计中,`MainAxisAlignment` 可以帮助在不同屏幕尺寸上保持一致的布局和间距。
对称设计:
- 使用 `spaceBetween`、`spaceAround`、和 `spaceEvenly` 等选项,可以轻松创建对称且美观的界面。
动态内容:
- 当内容数量不固定时,通过 `MainAxisAlignment` 可以确保内容在父容器中合理分布,无需手动调整每个元素的位置。
按钮和图标布局:
- 在工具栏或包含多个按钮的布局中,使用 `MainAxisAlignment` 可以确保按钮之间保持一致的间距。
注意事项
布局方向:
- `MainAxisAlignment` 的效果取决于 `Flex` 布局的方向(`Row` 或 `Column`),因此在使用时要明确布局的主轴方向。
空间分配:
- `spaceBetween`、`spaceAround`、和 `spaceEvenly` 这些选项会根据父容器的大小动态调整子组件之间的间距,因此在父容器尺寸变化时,子组件之间的间距也会相应调整。
与其他属性结合使用:
- 在实际布局中,`MainAxisAlignment` 通常与 `CrossAxisAlignment` 和 `MainAxisSize` 等属性一起使用,以实现更复杂的布局效果。
通过合理应用 `MainAxisAlignment`,开发者可以更灵活地控制子组件在主轴方向上的排列方式,创造出精致且响应良好的用户界面。
MainAxisSize
`MainAxisSize` 是 Flutter 中用于确定 `Flex` 布局模型(例如 `Row` 和 `Column`)在主轴方向上占用空间的策略。它定义了主轴尺寸是由子组件的大小决定还是由父容器的大小决定。
基本概念
在 Flutter 中,`Row` 和 `Column` 使用的都是 `Flex` 布局模型。`MainAxisSize` 控制这些布局在主轴方向上应该占据父容器的多大空间。
`MainAxisSize` 枚举值
`MainAxisSize.min`:
- 布局在主轴方向上将尽可能小,只占用子组件所需的最小空间。
- 例如,在 `Column` 中,如果使用 `MainAxisSize.min`,则 `Column` 的高度将等于其所有子组件的总高度,而不会扩展到填满父容器。
`MainAxisSize.max`:
- 布局在主轴方向上将尽可能大,占满父容器的可用空间。
- 例如,在 `Row` 中,如果使用 `MainAxisSize.max`,则 `Row` 的宽度将扩展以填满父容器,即使子组件的总宽度不足以填满。
使用示例
Dart
import 'package:flutter/material.dart';
class MainAxisSizeExample extends StatelessWidget{
const MainAxisSizeExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("MainAxisSize Example")),
body: Column(
mainAxisSize: MainAxisSize.min, // Set the main axis size to minimum
children: <Widget>[
Container(
color: Colors.red,
height: 50,
width: double.infinity,
child: const Center(child: Text('Box 1')),
),
Container(
color: Colors.green,
height: 50,
width: double.infinity,
child: const Center(child: Text('Box 2')),
),
Container(
color: Colors.blue,
height: 50,
width: double.infinity,
child: const Center(child: Text('Box 3')),
),
],
)
);
}
}
解释
`Column` 小部件:
- 设置 `mainAxisSize: MainAxisSize.min`,这使得 `Column` 的高度只等于其子组件总高度,而不是扩展到填满屏幕的高度。
效果:
- 在这个例子中,`Column` 只占用了三个 `Container` 的总高度,即 150 像素,而不是填满整个屏幕的可用高度。
适用场景
自适应布局:
- 在需要根据子组件内容动态调整布局大小时,`MainAxisSize.min` 可以确保布局不会占用多余的空间。
固定布局:
- 使用 `MainAxisSize.max` 可以确保布局占用所有可用空间,这对于需要固定填满父容器的布局非常有用。
灵活设计:
- 根据设计需求,选择适当的 `MainAxisSize` 值可以帮助实现灵活且响应良好的界面。
Column
`Column` 是 Flutter 中一个常用的小部件,用于在垂直方向上排列其子组件。它属于 `Flex` 布局模型的一部分,与 `Row`(水平排列子组件)类似,但在垂直方向上工作。
基本概念
`Column` 小部件会自动根据其子组件的大小和父容器的约束来调整自身的高度。它提供了多种属性,以灵活地控制子组件的排列方式和布局行为。
主要属性
`children`:
- 一个子组件列表,决定了 `Column` 中的内容。
- 每个子组件都按顺序从上到下排列。
`mainAxisAlignment`:
- 控制子组件在主轴(垂直方向)上的对齐方式。
- 可以使用 `MainAxisAlignment` 枚举值,如 `start`、`end`、`center`、`spaceBetween`、`spaceAround`、`spaceEvenly`。
`crossAxisAlignment`:
- 控制子组件在交叉轴(水平方向)上的对齐方式。
- 可以使用 `CrossAxisAlignment` 枚举值,如 `start`、`end`、`center`、`stretch`、`baseline`。
`mainAxisSize`:
- 控制 `Column` 在主轴上的大小。
- 使用 `MainAxisSize` 枚举值,如 `min`(根据子组件大小)和 `max`(根据父容器大小)。
`verticalDirection`:
- 指定子组件的布局方向,是从上到下还是从下到上。
- 使用 `VerticalDirection` 枚举值,如 `down`(默认)和 `up`。
`textDirection`:
- 控制子组件的文本方向,尤其在使用 `CrossAxisAlignment.start` 和 `CrossAxisAlignment.end` 时很有用。
- 使用 `TextDirection` 枚举值,如 `ltr`(从左到右)和 `rtl`(从右到左)。
使用示例
Dart
import 'package:flutter/material.dart';
class ColumnExample extends StatelessWidget {
const ColumnExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Column Example")),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
color: Colors.red,
height: 50,
width: 100,
child: const Center(child: Text('Box 1')),
),
Container(
color: Colors.green,
height: 50,
width: 100,
child: const Center(child: Text('Box 2')),
),
Container(
color: Colors.blue,
height: 50,
width: 100,
child: const Center(child: Text('Box 3')),
),
],
),
);
}
}
解释
`mainAxisAlignment: MainAxisAlignment.spaceEvenly`:
- 使得 `Column` 中的子组件在垂直方向上均匀分布,每个子组件之间的间隔相等。
`crossAxisAlignment: CrossAxisAlignment.center`:
- 使得 `Column` 中的子组件在水平方向上居中对齐。
Expanded
`Expanded` 是 Flutter 中的一个布局小部件,用于在 `Flex` 布局(如 `Row`、`Column`、`Flex`)中扩展子组件,以填充主轴上剩余的可用空间。它与 `Flexible` 小部件紧密相关,但提供了一种更简便的方式来分配剩余空间。
基本概念
`Expanded` 小部件会将子组件包裹起来,并指示 `Flex` 布局将主轴上剩余的空间分配给此组件。它通常与其他 `Expanded` 或 `Flexible` 小部件一起使用,以按比例分配可用空间。
主要属性
`child`:
- 需要扩展以填充可用空间的子组件。
`flex`:
- 一个整数值,表示如何分配可用空间。
- 默认为 `1`,可以设置为其他值以指定不同的空间分配比率。
使用示例
以下是一个简单的例子,展示了如何在 `Row` 中使用 `Expanded` 小部件:
Dart
import 'package:flutter/material.dart';
class ExpandedExample extends StatelessWidget {
const ExpandedExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Expanded Example")),
body: Row(
children: <Widget>[
Container(
color: Colors.red,
width: 100,
child: const Center(child: Text('Fixed Width')),
),
Expanded(
child: Container(
color: Colors.greenAccent,
child: const Center(child: Text('Expanded')),
)),
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
child: const Center(child: Text('Expanded Flex 2')),
))
],
),
);
}
}
解释
- 固定宽度容器:
- 第一个 `Container` 具有固定宽度 100 像素,并且颜色为红色。
- `Expanded` 容器:
- 第二个 `Container` 被 `Expanded` 包裹,因此它将扩展以填充 `Row` 中剩余的可用空间。
- 第三个 `Container` 也被 `Expanded` 包裹,并设置了 `flex: 2`,意味着它将占用两倍于第二个 `Expanded` 容器的空间。
适用场景
动态布局:
- 在需要根据可用空间动态调整子组件大小时,使用 `Expanded` 可以确保界面在不同设备上都能很好地适应。
比例布局:
- 通过设置不同的 `flex` 值,可以轻松实现子组件按比例分配可用空间的效果。
响应式设计:
- 在响应式设计中,`Expanded` 可以帮助在不同屏幕尺寸上保持布局的一致性和美观。
注意事项
只在 `Flex` 布局中使用:
- `Expanded` 只能用在 `Row`、`Column` 或 `Flex` 小部件的子组件中,因为这些布局才有主轴的概念。
与 `Flexible` 的区别:
- `Expanded` 是 `Flexible` 的一种特例,`fit` 属性自动设置为 `FlexFit.tight`,表示必须填充所有可用空间。
结合三方库实现ViewPager学习
在pubspec.yaml文件中配置依赖项
Dart
dependencies:
flutter:
sdk: flutter
another_transformer_page_view: 2.0.0
这段代码是 Flutter 项目的 `pubspec.yaml` 文件中 `dependencies` 部分的一个典型示例。`dependencies` 部分用于列出项目所依赖的包和库。让我们逐行解析这段配置:
`dependencies:`:
- 这行标识依赖项的开始。在这个部分中,列出了项目的所有直接依赖关系。
`flutter:`:
- 指定项目依赖于 Flutter SDK。Flutter 是一个用于构建跨平台应用的框架。
`sdk: flutter`:
- 表明这个依赖项是 Flutter SDK 本身。
`another_transformer_page_view: 2.0.0`:
- 指定项目依赖于 `another_transformer_page_view` 包的版本 `2.0.0`。这个包用于实现增强的页面视图效果,如自定义的页面切换动画。
解释
版本号:
- 直接指定版本号(如 `3.0.1` 和 `2.0.0`)表示项目需要这些特定版本的包。这确保了项目的依赖在不同环境中是一致的。
注释:
- 注释用于解释代码或临时禁用某些配置。这里的注释告诉开发者 `cupertino_icons` 的用途以及它的版本要求。
依赖管理:
- `pubspec.yaml` 文件是 Dart 和 Flutter 项目中管理依赖的核心文件。它允许开发者轻松添加、更新或移除项目的外部依赖。
兼容性:
- 通过明确版本号和使用版本范围符号,开发者可以控制项目依赖的兼容性,避免由于包更新导致的潜在问题。
在`pubspec.lock` 文件中配置依赖项
描述了一个名为 `another_transformer_page_view` 的 Dart 包的依赖配置。`pubspec.lock` 文件用于管理 Flutter 和 Dart 项目的包依赖,以及项目的其他元数据。
Dart
another_transformer_page_view:
dependency: "direct main"
description:
name: another_transformer_page_view
sha256: "0b06f2564a7e38c3277a01b28cf10a9c457c52dfa01ef4ff8853986131db1b95"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
让我们逐行解析这段配置:
`another_transformer_page_view:`:
- 这是包的名称,表示项目依赖于这个包。
`dependency: "direct main"`:
- 这行表示包的依赖类型为 "direct main"。在 Flutter 和 Dart 项目中,依赖可以是直接的(项目代码中直接引用的)或间接的(通过其他包引入的),"main" 表示这是一个主要依赖项。
`description:`:
- 这部分提供了有关包的详细信息。
`name: another_transformer_page_view`:
- 这是包的名称,与第一行一致。
`sha256: "0b06f2564a7e38c3277a01b28cf10a9c457c52dfa01ef4ff8853986131db1b95"`:
- 这是包的 SHA-256 校验和。它用于验证下载的包的完整性和来源的真实性,确保包未被篡改。
`url: "
"`:
-
这是包的来源网址。` https://pub.dev
` 是 Dart 和 Flutter 包的官方存储库网站,开发者可以在这里查找和发布包。
`source: hosted`:
- 指定包的来源方式为 "hosted",表示包托管在一个公共的或私有的包存储库中。在这种情况下,包托管在 `pub.dev` 上。
`version: "2.0.0"`:
- 指定项目依赖的 `another_transformer_page_view` 包的版本号为 `2.0.0`。项目将安装并使用这个版本的包。
解释
依赖管理:
- `pubspec.yaml` 文件用来定义项目的依赖项,确保所有开发人员在同一个版本上进行开发,并且项目在不同机器上有相同的运行环境。
包版本:
- 明确指定版本号可以避免由于包更新导致的意外行为变化。开发者可以通过这种方式保持依赖的稳定性。
完整性验证:
- 使用 SHA-256 校验和可以在下载包时验证其完整性,防止数据被篡改。
这种配置方式帮助开发者有效管理项目中的外部依赖,确保项目的一致性和可维护性。
三方库another_transformer_page_view
`another_transformer_page_view` 是 Flutter 的一个第三方包,提供了增强的页面视图功能,特别是丰富多样的页面切换动画效果。这个包可以用来创建具有吸引力的用户界面,尤其是在实现应用的引导页、图像幻灯片或其他需要视觉切换效果的场景中。
功能和特点
自定义页面切换动画:
- 提供了多种预定义的页面切换效果,比如旋转、缩放、淡入淡出等。
- 开发者可以基于现有效果创建自定义动画,从而更好地满足特定的设计需求。
与 `PageView` 类似的 API:
- `another_transformer_page_view` 的使用方式与 Flutter 自带的 `PageView` 类似,因此对开发者来说很容易上手。
- 支持无限循环滚动、页面指示器等常见的页面视图功能。
高度可定制:
- 可以通过实现 `PageTransformer` 来创建自定义的页面转换效果。
- 提供灵活的布局和动画选项,使其适用于多种设计风格。
使用示例
假设你有一个需要使用 `another_transformer_page_view` 的 Flutter 项目,下面是一个简单的使用示例:
Dart
import 'package:another_transformer_page_view/another_transformer_page_view.dart';
import 'package:flutter/material.dart';
import 'package:gsy_flutter_demo/widget/viewpager_demo_page.dart';
class MyPageView extends StatelessWidget {
const MyPageView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Transformer Page View')),
body: TransformerPageView(
loop: true,
transformer: ZoomOutPageTransformer(),
itemCount: 3,
itemBuilder: (context, index) {
return Container(
color: index.isEven ? Colors.blue : Colors.green,
child: Center(
child: Text(
'Page $index',
style: const TextStyle(fontSize: 32, color: Colors.white),
),
),
);
},
),
);
}
}
class ZoomOutPageTransformer extends PageTransformer {
static const double MIN_SCALE = 0.85;
static const double MIN_ALPHA = 0.5;
@override
Widget transform(Widget child, TransformInfo info) {
double position = info.position!;
double? pageWidth = info.width;
double? pageHeight = info.height;
if (position < -1) {
// [-Infinity,-1)
// This page is way off-screen to the left.
//view.setAlpha(0);
} else if (position <= 1) {
// [-1,1]
// Modify the default slide transition to
// shrink the page as well
double scaleFactor = Math.max(MIN_SCALE, 1 - position.abs());
double vertMargin = pageHeight! * (1 - scaleFactor) / 2;
double horzMargin = pageWidth! * (1 - scaleFactor) / 2;
double dx;
if (position < 0) {
dx = (horzMargin - vertMargin / 2);
} else {
dx = (-horzMargin + vertMargin / 2);
}
// Scale the page down (between MIN_SCALE and 1)
double opacity = MIN_ALPHA +
(scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA);
return Opacity(
opacity: opacity,
child: Transform.translate(
offset: Offset(dx, 0.0),
child: Transform.scale(
scale: scaleFactor,
child: child,
),
),
);
} else {
// (1,+Infinity]
// This page is way off-screen to the right.
// view.setAlpha(0);
}
return child;
}
}
解释
- `TransformerPageView`:
- 这是 `another_transformer_page_view` 的核心组件,类似于 Flutter 的 `PageView`。
- `loop: true` 表示页面视图是循环的,用户可以无限滚动。
- `transformer: ZoomOutPageTransformer()` 设置了页面切换时使用的动画效果。
- `itemBuilder`:
- 用于构建每个页面的内容。在这个例子中,不同索引的页面有不同的背景颜色。
适用场景
应用引导页:
- 可用于创建应用的引导页,提供视觉上吸引人的切换效果。
图片轮播:
- 在需要展示一系列图片或内容的情况下,这个包提供了丰富的切换效果,使内容展示更具吸引力。
幻灯片展示:
- 用于制作幻灯片或展示文档的应用,提供了灵活的动画效果。
注意事项
性能:
- 由于动画效果可能涉及复杂的图形计算,在低性能设备上可能需要注意优化。
兼容性:
- 在使用自定义动画效果时,确保其与应用的整体设计风格一致,以提供最佳的用户体验。
another_transformer_page_view实现ViewPager代码学习
Dart
import 'dart:math' as Math;
import 'package:another_transformer_page_view/another_transformer_page_view.dart';
import 'package:flutter/material.dart';
import 'package:vector_math/vector_math_64.dart' as vector;
class ViewPagerDemoPage extends StatelessWidget {
final List<Color> colorList = [
Colors.redAccent,
Colors.blueAccent,
Colors.greenAccent
];
ViewPagerDemoPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).primaryColorDark,
appBar: AppBar(
title: const Text("ViewPagerDemoPage"),
),
body: Container(
alignment: Alignment.topCenter,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: TransformerPageView(
loop: false,
controller: IndexController(),
itemBuilder: (BuildContext context, int index) {
return Container(
decoration: BoxDecoration(
color: colorList[index % colorList.length],
border: Border.all(color: Colors.white)),
child: Center(
child: Text(
"$index",
style: const TextStyle(
fontSize: 80.0, color: Colors.white),
),
),
);
},
itemCount: 3),
),
Expanded(
child: TransformerPageView(
loop: true,
controller: IndexController(),
transformer: AccordionTransformer(),
itemBuilder: (BuildContext context, int index) {
return Container(
decoration: BoxDecoration(
color: colorList[index % colorList.length],
border: Border.all(color: Colors.white)),
child: Center(
child: Text(
"$index",
style: const TextStyle(
fontSize: 80.0, color: Colors.white),
),
),
);
},
itemCount: 3),
),
Expanded(
child: TransformerPageView(
loop: true,
controller: IndexController(),
transformer: ThreeDTransformer(),
itemBuilder: (BuildContext context, int index) {
return Container(
decoration: BoxDecoration(
color: colorList[index % colorList.length],
border: Border.all(color: Colors.white)),
child: Center(
child: Text(
"$index",
style: const TextStyle(
fontSize: 80.0, color: Colors.white),
),
),
);
},
itemCount: 3),
),
Expanded(
child: TransformerPageView(
loop: true,
controller: IndexController(),
transformer: DeepthPageTransformer(),
itemBuilder: (BuildContext context, int index) {
return Container(
decoration: BoxDecoration(
color: colorList[index % colorList.length],
border: Border.all(color: Colors.white)),
child: Center(
child: Text(
"$index",
style: const TextStyle(
fontSize: 80.0, color: Colors.white),
),
),
);
},
itemCount: 3),
),
],
),
),
);
}
}
class AccordionTransformer extends PageTransformer {
@override
Widget transform(Widget child, TransformInfo info) {
double position = info.position!;
if (position < 0.0) {
return Transform.scale(
scale: 1 + position,
alignment: Alignment.topRight,
child: child,
);
} else {
return Transform.scale(
scale: 1 - position,
alignment: Alignment.bottomLeft,
child: child,
);
}
}
}
class ThreeDTransformer extends PageTransformer {
@override
Widget transform(Widget child, TransformInfo info) {
double position = info.position!;
double height = info.height!;
double? width = info.width;
double? pivotX = 0.0;
if (position < 0 && position >= -1) {
// left scrolling
pivotX = width;
}
return Transform(
transform: Matrix4.identity()
..rotate(vector.Vector3(0.0, 2.0, 0.0), position * 1.5),
origin: Offset(pivotX!, height / 2),
child: child,
);
}
}
class DeepthPageTransformer extends PageTransformer {
DeepthPageTransformer() : super(reverse: true);
@override
Widget transform(Widget child, TransformInfo info) {
double position = info.position!;
if (position <= 0) {
return Opacity(
opacity: 1.0,
child: Transform.translate(
offset: const Offset(0.0, 0.0),
child: Transform.scale(
scale: 1.0,
child: child,
),
),
);
} else if (position <= 1) {
const double minScale = 0.75;
// Scale the page down (between MIN_SCALE and 1)
double scaleFactor = minScale + (1 - minScale) * (1 - position);
return Opacity(
opacity: 1.0 - position,
child: Transform.translate(
offset: Offset(info.width! * -position, 0.0),
child: Transform.scale(
scale: scaleFactor,
child: child,
),
),
);
}
return child;
}
}