├── .gitignore ├── .travis.yml ├── CHANGELOG-ZH.md ├── CHANGELOG.md ├── LICENSE ├── README-ZH.md ├── README.md ├── analysis_options.yaml ├── banner.jpg ├── dev └── bots │ ├── travis_install.sh │ └── travis_script.sh ├── example ├── .gitignore ├── .idea │ ├── codeStyles │ │ └── Project.xml │ ├── libraries │ │ ├── Dart_Packages.xml │ │ ├── Dart_SDK.xml │ │ ├── Flutter_Plugins.xml │ │ └── Flutter_for_Android.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations │ │ └── main_dart.xml │ └── workspace.xml ├── .metadata ├── README.md ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── example │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── example.iml ├── example_android.iml ├── images │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── bg.jpeg │ ├── bg0.jpeg │ ├── bg1.jpeg │ └── bg2.jpeg ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── MyLaunch.jpg │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── main.m ├── lib │ ├── inner_swiper.dart │ ├── listener_test.dart │ ├── main.dart │ └── src │ │ ├── config.dart │ │ ├── example_custom.dart │ │ ├── example_swiper_in_scrollview.dart │ │ └── forms │ │ └── form_widget.dart ├── pubspec.yaml ├── res │ ├── 1.gif │ ├── 2.gif │ ├── 3.gif │ ├── 4.gif │ └── 5.gif └── test │ └── widget_test.dart ├── flutter_swiper.iml ├── lib ├── flutter_swiper_view.dart └── src │ ├── custom_layout.dart │ ├── flutter_page_indicator.dart │ ├── index_controller.dart │ ├── parallax.dart │ ├── swiper.dart │ ├── swiper_control.dart │ ├── swiper_controller.dart │ ├── swiper_pagination.dart │ ├── swiper_plugin.dart │ └── transformer_page_view.dart ├── pubspec.yaml └── test ├── control_test.dart ├── flutter_swiper_test.dart ├── layout_test.dart └── pagination_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | ios/.generated/ 9 | ios/Flutter/Generated.xcconfig 10 | ios/Runner/GeneratedPluginRegistrant.* 11 | coverage/lcov.info 12 | /.idea/ 13 | pubspec.lock 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: dart 2 | env: 3 | - SHARD=dartfmt 4 | - SHARD=test 5 | install: 6 | - ./dev/bots/travis_install.sh 7 | script: 8 | - ./dev/bots/travis_script.sh 9 | after_success: 10 | - coveralls-lcov coverage/lcov.info -------------------------------------------------------------------------------- /CHANGELOG-ZH.md: -------------------------------------------------------------------------------- 1 | ## [1.1.8] - [2022/05/27] 2 | 3 | * 修复 example 无法运行的问题 4 | * 修复 SwiperPagination.rect 不显示指示器的问题 5 | * 支持空安全 6 | * 支持 Flutter 3 7 | 8 | ## [1.1.7] - [2022/05/27] 9 | 10 | * 测试发布 11 | 12 | 13 | ## [1.1.1] - [2018/09/20] 14 | * 修复自动集成测试错误 15 | 16 | ## [1.1.0] - [2018/09/20] 17 | * 修复index的bug ,见 #11 18 | * 增加 `autoplayDisableOnInteraction` 选项, 如果设置为true,那么在用户滑动的时候停止自动播放,滑动之后重新自动播放 19 | 20 | 21 | ## [1.0.7] - [2018/09/02] 22 | * 在Swiper dispose的时候不调用Controller的dispose. 23 | 24 | ## [1.0.6] - [2018/08/28] 25 | * `TINDER` 和 `STACK` 这两种布局方式实现垂直滚动, #9 26 | 27 | ## [1.0.5] - [2018/08/09] 28 | * viewportFraction<1.0增加fade参数 29 | 30 | ## [1.0.4] - [2018/07/18] 31 | * 修复一些错别字,感谢[csharad](https://github.com/csharad) 32 | 33 | ## [1.0.3] - [2018/07/18] 34 | * 根据#5 ,用new来创建对象 35 | 36 | ## [1.0.2] - [2018/07/16] 37 | * 修复 #4 38 | 39 | ## [1.0.1] - [2018/07/11] 40 | * 支持布局方式(CUSTOM),你可以定制你自己的布局 41 | 42 | ## [1.0.0] - [2018/07/08] 43 | * 增加布局方式( DEFAULT,STACK,TINDER ) 44 | * 可以将分页指示器放在容器外面 45 | 46 | ## [0.0.9] - [2018/06/10] 47 | * 增加CI 48 | * 增加测试用例 49 | 50 | ## [0.0.8] - [2018/06/07] 51 | * 新增中文文档 52 | * 更新依赖包:infinity_page_view 版本到 1.0.0 53 | 54 | ## [0.0.7] - [2018/05/24] 55 | * The default color of pagination is ThemeData.scaffoldBackgroundColor and ThemeData.primaryColor 56 | * The default color of control buttons is ThemeData.primaryColor and ThemeData.disabledColor 57 | * The alignment of pagination is Alignment.bottomCenter by default when scrollDirection== Axis.horizontal, Alignment.centerRight by default when scrollDirection== Axis.vertical 58 | * Change default value of `autoplay` to false 59 | 60 | ## [0.0.6] - [2018/05/24] 61 | * Fix index bug 62 | 63 | ## [0.0.5] - [2018/05/24] 64 | * Fix zero itemCount bug 65 | 66 | ## [0.0.4] - [2018/05/20] 67 | * Update README 68 | 69 | ## [0.0.3] - [2018/05/20] 70 | * Update README 71 | * Support none loop mode 72 | * Add more examples 73 | 74 | ## [0.0.2] - [2018/05/20] 75 | * Autoplay 76 | * Plugins support 77 | * Examples 78 | 79 | ## [0.0.1] - [2018/05/20] 80 | * Infinite loop 81 | * Control buttons 82 | * Pagination 83 | * Custom control buttons 84 | * Custom pagination 85 | * Controler -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.1.8] - [2022/05/27] 2 | 3 | * fix example error 4 | * fix SwiperPagination.rect indicator does not display 5 | * support null safety 6 | * support Flutter 3 7 | 8 | ## [1.1.7] - [2022/05/27] 9 | 10 | * test publish to pub.dev 11 | 12 | 13 | ## [1.1.5] - [2019/03/10] 14 | 15 | * Fix findRenderObject is null 16 | 17 | 18 | ## [1.1.4] - [2018/10/18] 19 | 20 | ## [1.1.2] - [2018/10/10] 21 | * Fix #7 22 | * Support layout for pagination. 23 | 24 | ## [1.1.1] - [2018/09/20] 25 | * Fix `test` failure. 26 | 27 | ## [1.1.0] - [2018/09/20] 28 | * Fix index bug ,See #11 29 | * Enable `autoplayDisableOnInteraction` property, if set `true`,disable autoplay when user swipes. 30 | 31 | ## [1.0.7] - [2018/09/02] 32 | * Don't call SwiperController's dispose when `Swiper` dispose. 33 | 34 | ## [1.0.6] - [2018/08/28] 35 | * Implement vertical scroll direction for `TINDER` and `STACK` layout, see #9 36 | 37 | ## [1.0.5] - [2018/08/09] 38 | * Add feature : support fade for `viewportFraction` 39 | 40 | ## [1.0.4] - [2018/07/18] 41 | * Fix some typo,thanks to [csharad](https://github.com/csharad) 42 | 43 | ## [1.0.3] - [2018/07/18] 44 | * Use new to create everything. See #5 45 | 46 | ## [1.0.2] - [2018/07/16] 47 | * Fix #4 48 | 49 | ## [1.0.1] - [2018/07/11] 50 | * Add layout (CUSTOM) so that you can create your own layout 51 | 52 | ## [1.0.0] - [2018/07/08] 53 | * Add layouts ( DEFAULT,STACK,TINDER ) 54 | * Allow to put pagination outer of the swiper container. 55 | 56 | ## [0.0.9] - [2018/06/10] 57 | * Add ci 58 | * Add testcase 59 | 60 | ## [0.0.8] - [2018/06/07] 61 | * And chinese document 62 | * Update infinity_page_view version to 1.0.0 63 | 64 | ## [0.0.7] - [2018/05/24] 65 | * The default color of pagination is ThemeData.scaffoldBackgroundColor and ThemeData.primaryColor 66 | * The default color of control buttons is ThemeData.primaryColor and ThemeData.disabledColor 67 | * The alignment of pagination is Alignment.bottomCenter by default when scrollDirection== Axis.horizontal, Alignment.centerRight by default when scrollDirection== Axis.vertical 68 | * Change default value of `autoplay` to false 69 | 70 | ## [0.0.6] - [2018/05/24] 71 | * Fix index bug 72 | 73 | ## [0.0.5] - [2018/05/24] 74 | * Fix zero itemCount bug 75 | 76 | ## [0.0.4] - [2018/05/20] 77 | * Update README 78 | 79 | ## [0.0.3] - [2018/05/20] 80 | * Update README 81 | * Support none loop mode 82 | * Add more examples 83 | 84 | ## [0.0.2] - [2018/05/20] 85 | * Autoplay 86 | * Plugins support 87 | * Examples 88 | 89 | ## [0.0.1] - [2018/05/20] 90 | * Infinite loop 91 | * Control buttons 92 | * Pagination 93 | * Custom control buttons 94 | * Custom pagination 95 | * Controler -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Xueliang Ren 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README-ZH.md: -------------------------------------------------------------------------------- 1 | ![Logo](banner.jpg) 2 | 3 |

4 | 5 | Build Status 6 | 7 | 8 | Coverage Status 9 | 10 | 11 | PRs Welcome 12 | 13 | 14 | pub package 15 | 16 |

17 |

18 | 19 | 英文说明 20 | 21 |

22 | 23 | 24 | # flutter_swiper_view 25 | 26 | flutter 最强大的 swiper, 多种布局方式,无限轮播,Android 和 iOS 双端适配. 27 | 28 | 29 | # :sparkles::sparkles: New Features: 视差 30 | 31 | 我们在 Swiper 中也像 Android 一样支持了 `PageTransformer`, 只要给 Swiper 设置一下 `transformer` 属性就行, 32 | 这里返回一个被转换的组件给 Swiper. 目前仅仅支持 `DEFAULT` 布局. 33 | 感谢 @FlutterRocks ,棒棒哒 👏. 34 | 35 | 36 | 37 | 38 | # :sparkles::sparkles: 新功能 39 | 40 | 41 | ![](https://github.com/jzoom/images/raw/master/layout1.gif) 42 | 43 | ![](https://github.com/jzoom/images/raw/master/layout2.gif) 44 | 45 | ![](https://github.com/jzoom/images/raw/master/layout3.gif) 46 | 47 | [更多](#内建的布局) 48 | 49 | 50 | # 截图 51 | 52 | ![Horizontal](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/1.gif) 53 | 54 | ![Vertical](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/2.gif) 55 | 56 | ![Custom Pagination](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/3.gif) 57 | 58 | ![Custom Pagination](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/4.gif) 59 | 60 | ![Phone](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/5.gif) 61 | 62 | ![Example](https://github.com/jzoom/images/raw/master/swiper-example.gif) 63 | 64 | [更多](#代码) 65 | 66 | ## 功能路线 67 | 68 | 1.x.x 功能实现: 69 | 70 | - [x] 无限循环轮播 71 | - [x] 控制按钮 72 | - [x] 分页指示器 73 | - [x] 非无限循环模式 74 | - [x] 单元测试 75 | - [x] 例子 76 | - [x] 滚动方向 77 | - [x] 可定制控制按钮 78 | - [x] 可定制分页 79 | - [x] 自动播放 80 | - [x] 控制器 81 | - [x] 外部分页指示器 82 | - [ ] 更多布局方式 83 | 84 | 85 | ## 更新日志 86 | 87 | >参考:[CHANGELOG.md](https://github.com/feicien/flutter_swiper_view/blob/master/CHANGELOG-ZH.md) 88 | 89 | ## 目录 90 | 91 | - [安装](#安装) 92 | - [基本使用](#基本使用) 93 | - [构建](#构建) 94 | + [基本构造函数](#基本构造函数) 95 | + [分页指示器](#分页指示器) 96 | + [控制按钮](#控制按钮) 97 | + [控制器](#控制器) 98 | + [自动播放](#自动播放) 99 | + [内建的布局](#内建的布局) 100 | + [一些常用代码示例](#代码) 101 | 102 | ### 安装 103 | 104 | 增加 105 | 106 | ```dart 107 | dependencies: 108 | flutter_swiper_view: ^1.1.8 109 | ``` 110 | 到项目根目录下的 pubspec.yaml ,并且根目录运行命令行 111 | 112 | ```dart 113 | flutter pub get 114 | ``` 115 | 116 | 117 | ### 基本使用 118 | 119 | 使用命令行创建一个新项目: 120 | 121 | ```dart 122 | flutter create myapp 123 | ``` 124 | 125 | 编辑 lib/main.dart: 126 | 127 | ```dart 128 | import 'package:flutter/material.dart'; 129 | 130 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 131 | 132 | void main() => runApp(const MyApp()); 133 | 134 | class MyApp extends StatelessWidget { 135 | const MyApp({Key? key}) : super(key: key); 136 | 137 | @override 138 | Widget build(BuildContext context) { 139 | return MaterialApp( 140 | title: 'Flutter Demo', 141 | theme: ThemeData( 142 | primarySwatch: Colors.blue, 143 | ), 144 | home: const MyHomePage(title: 'Flutter Demo Home Page'), 145 | ); 146 | } 147 | } 148 | 149 | class MyHomePage extends StatefulWidget { 150 | const MyHomePage({Key? key, required this.title}) : super(key: key); 151 | 152 | final String title; 153 | 154 | @override 155 | State createState() => _MyHomePageState(); 156 | } 157 | 158 | class _MyHomePageState extends State { 159 | 160 | @override 161 | Widget build(BuildContext context) { 162 | return Scaffold( 163 | appBar: AppBar( 164 | title: Text(widget.title), 165 | ), 166 | body: Swiper( 167 | itemBuilder: (context, index){ 168 | return Image.network("https://via.placeholder.com/350x150",fit: BoxFit.fill,); 169 | }, 170 | itemCount: 3, 171 | pagination: const SwiperPagination(), 172 | control: const SwiperControl(), 173 | ), 174 | ); 175 | } 176 | } 177 | ``` 178 | 179 | 180 | 181 | ### 构建 182 | 183 | 184 | #### 基本参数 185 | 186 | | 参数 | 默认值 | 描述 | 187 | | :-------------- |:-----------------:| :------------------------| 188 | | scrollDirection | Axis.horizontal |滚动方向,设置为 Axis.vertical 如果需要垂直滚动 | 189 | | loop | true |无限轮播模式开关 | 190 | | index | 0 |初始的时候下标位置 | 191 | | autoplay | false |自动播放开关. | 192 | | onIndexChanged | void onIndexChanged(int index) | 当用户手动拖拽或者自动播放引起下标改变的时候调用 | 193 | | onTap | void onTap(int index) | 当用户点击某个轮播的时候调用 | 194 | | duration | 300.0 | 动画时间,单位是毫秒 | 195 | | pagination | null | 设置 `SwiperPagination()` 展示默认分页指示器 196 | | control | null | 设置 `SwiperControl()` 展示默认分页按钮 197 | 198 | 199 | #### 分页指示器 200 | 201 | 分页指示器继承自 `SwiperPlugin`,`SwiperPlugin` 为 `Swiper` 提供额外的界面.设置为`SwiperPagination()` 展示默认分页. 202 | 203 | 204 | | 参数 | 默认值 | 描述 | 205 | | :------------ |:---------------:| :-----| 206 | | alignment | Alignment.bottomCenter | 如果要将分页指示器放到其他位置,那么可以修改这个参数 | 207 | | margin | const EdgeInsets.all(10.0) | 分页指示器与容器边框的距离 | 208 | | builder | SwiperPagination.dots | 目前已经定义了三个默认的分页指示器样式: `SwiperPagination.dots` 、 `SwiperPagination.fraction`、 `SwiperPagination.rect`,都可以做进一步的自定义. | 209 | 210 | 如果需要定制自己的分页指示器,那么可以这样写: 211 | 212 | ```dart 213 | Swiper( 214 | ..., 215 | pagination: SwiperCustomPagination( 216 | builder: (context, config){ 217 | return YourOwnPaginatipon(); 218 | }, 219 | ) 220 | ); 221 | ``` 222 | 223 | 224 | 225 | #### 控制按钮 226 | 227 | 控制按钮组件也是继承自 `SwiperPlugin`,设置 `SwiperControl()` 展示默认控制按钮. 228 | 229 | 230 | | 参数 | 默认值 | 描述 | 231 | | :------------ |:---------------:| :-----| 232 | | iconPrevious | Icons.arrow_back_ios | 上一页的IconData | 233 | | iconNext | Icons.arrow_forward_ios | 下一页的IconData | 234 | | color | Theme.of(context).primaryColor | 控制按钮颜色 | 235 | | size | 30.0 | 控制按钮的大小 | 236 | | padding | const EdgeInsets.all(5.0) | 控制按钮与容器的距离 | 237 | 238 | 239 | #### 控制器(SwiperController) 240 | 241 | `SwiperController` 用于控制 Swiper的`index`属性, 停止和开始自动播放. 通过 `SwiperController()` 创建一个 SwiperController 实例,并保存,以便将来能使用。 242 | 243 | 244 | | 方法 | 描述 | 245 | | :------------ |:-----| 246 | | void move(int index, {bool animation: true}) | 移动到指定下标,设置是否播放动画| 247 | | void next({bool animation: true}) | 下一页 | 248 | | void previous({bool animation: true}) | 上一页 | 249 | | void startAutoplay() | 开始自动播放 | 250 | | void stopAutoplay() | 停止自动播放 | 251 | 252 | 253 | 254 | #### 自动播放 255 | 256 | | 参数 | 默认值 | 描述 | 257 | | :------------ |:---------------:| :-----| 258 | | autoplayDely | 3000 | 自动播放延迟毫秒数. | 259 | | autoplayDisableOnInteraction | true | 当用户拖拽的时候,是否停止自动播放. | 260 | 261 | 262 | 263 | ## 内建的布局 264 | ![](https://github.com/jzoom/images/raw/master/layout1.gif) 265 | 266 | ```dart 267 | Swiper( 268 | itemBuilder: (context, index) { 269 | return Image.network( 270 | "https://via.placeholder.com/288x188", 271 | fit: BoxFit.fill, 272 | ); 273 | }, 274 | itemCount: 10, 275 | viewportFraction: 0.8, 276 | scale: 0.9, 277 | ) 278 | 279 | ``` 280 | 281 | 282 | 283 | ![](https://github.com/jzoom/images/raw/master/layout2.gif) 284 | 285 | ```dart 286 | Swiper( 287 | itemBuilder: (context, index) { 288 | return Image.network( 289 | "https://via.placeholder.com/288x188", 290 | fit: BoxFit.fill, 291 | ); 292 | }, 293 | itemCount: 10, 294 | itemWidth: 300.0, 295 | layout: SwiperLayout.STACK, 296 | ) 297 | ``` 298 | 299 | ![](https://github.com/jzoom/images/raw/master/layout3.gif) 300 | 301 | ```dart 302 | Swiper( 303 | itemBuilder: (context, index) { 304 | return Image.network( 305 | "https://via.placeholder.com/288x188", 306 | fit: BoxFit.fill, 307 | ); 308 | }, 309 | itemCount: 10, 310 | itemWidth: 300.0, 311 | itemHeight: 400.0, 312 | layout: SwiperLayout.TINDER, 313 | ) 314 | ``` 315 | 316 | 317 | 318 | ![](https://github.com/jzoom/images/raw/master/layout4.gif) 319 | 320 | 构建你自己的动画十分简单: 321 | 322 | ```dart 323 | Swiper( 324 | layout: SwiperLayout.CUSTOM, 325 | customLayoutOption: CustomLayoutOption(startIndex: -1, stateCount: 3) 326 | ..addRotate([-45.0 / 180, 0.0, 45.0 / 180]) 327 | ..addTranslate([ 328 | const Offset(-370.0, -40.0), 329 | const Offset(0.0, 0.0), 330 | const Offset(370.0, -40.0) 331 | ]), 332 | itemWidth: 300.0, 333 | itemHeight: 200.0, 334 | itemBuilder: (context, index) { 335 | return Container( 336 | color: Colors.grey, 337 | child: Center( 338 | child: Text("$index"), 339 | ), 340 | ); 341 | }, 342 | itemCount: 10, 343 | ), 344 | ``` 345 | 346 | 347 | `CustomLayoutOption` 被设计用来描述布局和动画,很简单的可以指定每一个元素的状态. 348 | 349 | ```dart 350 | CustomLayoutOption( 351 | startIndex: -1, /// 开始下标 352 | stateCount: 3 /// 下面的数组长度 353 | )..addRotate([ // 每个元素的角度 354 | -45.0/180, 355 | 0.0, 356 | 45.0/180 357 | ])..addTranslate([ /// 每个元素的偏移 358 | const Offset(-370.0, -40.0), 359 | const Offset(0.0, 0.0), 360 | const Offset(370.0, -40.0) 361 | ]) 362 | ``` 363 | 364 | ## 代码 365 | 366 | 367 | ![Example](https://github.com/jzoom/images/raw/master/swiper-example.gif) 368 | 369 | 370 | ```dart 371 | ConstrainedBox( 372 | constraints: BoxConstraints.loose(Size(screenWidth, 170.0)) 373 | child: Swiper( 374 | outer: false, 375 | itemBuilder: (c, i) { 376 | return Wrap( 377 | runSpacing: 6.0, 378 | children: [0,1,2,3,4,5,6,7,8,9].map((i){ 379 | return SizedBox( 380 | width: MediaQuery.of(context).size.width/5, 381 | child: Column( 382 | mainAxisSize: MainAxisSize.min, 383 | children: [ 384 | SizedBox( 385 | height: MediaQuery.of(context).size.width * 0.12, 386 | width: MediaQuery.of(context).size.width * 0.12, 387 | child: Image.network("https://fuss10.elemecdn.com/c/db/d20d49e5029281b9b73db1c5ec6f9jpeg.jpeg%3FimageMogr/format/webp/thumbnail/!90x90r/gravity/Center/crop/90x90"), 388 | ), 389 | Padding( 390 | padding: const EdgeInsets.only(top: 6.0), 391 | child: Text("$i"), 392 | ) 393 | ], 394 | ), 395 | ); 396 | }).toList(), 397 | ); 398 | }, 399 | pagination: const SwiperPagination(margin: EdgeInsets.all(5.0)), 400 | itemCount: 10, 401 | ), 402 | ), 403 | ``` 404 | 405 | 406 | 407 | 这里可以找到所有的定制选项 408 | 409 | >https://github.com/feicien/flutter_swiper_view/blob/master/example/lib/src/example_custom.dart -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](banner.jpg) 2 | 3 |

4 | 5 | Build Status 6 | 7 | 8 | Coverage Status 9 | 10 | 11 | PRs Welcome 12 | 13 | 14 | pub package 15 | 16 |

17 |

18 | 19 | 中文说明 20 | 21 |

22 | 23 | 24 | 25 | # flutter_swiper_view 26 | 27 | The best swiper for flutter , with multiple layouts, infinite loop. Compatible with Android & iOS. 28 | 29 | 30 | 31 | # :sparkles::sparkles: New Features:PageTransformer 32 | 33 | Finally, we have `PageTransformer` like android, just set a `transformer` to `Swiper`, 34 | it returns a widget that has been transformed. For now, only support for layout `DEFAULT`. 35 | Thanks to @FlutterRocks ,you've done great job 👏. 36 | 37 | 38 | 39 | # :sparkles::sparkles: New Features:Layout 40 | 41 | 42 | ![](https://github.com/jzoom/images/raw/master/layout1.gif) 43 | 44 | ![](https://github.com/jzoom/images/raw/master/layout2.gif) 45 | 46 | ![](https://github.com/jzoom/images/raw/master/layout3.gif) 47 | 48 | [See More](#build-in-layouts) 49 | 50 | 51 | # Showcases 52 | 53 | ![Horizontal](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/1.gif) 54 | 55 | ![Vertical](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/2.gif) 56 | 57 | ![Custom Pagination](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/3.gif) 58 | 59 | ![Custom Pagination](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/4.gif) 60 | 61 | ![Phone](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/5.gif) 62 | 63 | ![Example](https://github.com/jzoom/images/raw/master/swiper-example.gif) 64 | 65 | [See More](#codes) 66 | 67 | 68 | ## Changelogs 69 | 70 | >see:[CHANGELOG.md](https://github.com/feicien/flutter_swiper_view/blob/master/CHANGELOG.md) 71 | 72 | ## Getting Started 73 | 74 | - [Installation](#installation) 75 | - [Basic Usage](#basic-usage) 76 | - [Constructor](#constructor) 77 | + [Basic](#basic) 78 | + [Pagination](#pagination) 79 | + [Control buttons](#control-buttons) 80 | + [Controller](#controller) 81 | + [Autoplay](#autoplay) 82 | - [Build in layouts](#build-in-layouts) 83 | - [Codes](#codes) 84 | 85 | ### Installation 86 | 87 | Add 88 | 89 | ```dart 90 | dependencies: 91 | flutter_swiper_view: ^1.1.8 92 | ``` 93 | to your pubspec.yaml ,and run 94 | 95 | ```dart 96 | flutter pub get 97 | ``` 98 | in your project's root directory. 99 | 100 | ### Basic Usage 101 | 102 | Create a new project with command 103 | 104 | ```dart 105 | flutter create myapp 106 | ``` 107 | 108 | Edit lib/main.dart like this: 109 | 110 | ```dart 111 | import 'package:flutter/material.dart'; 112 | 113 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 114 | 115 | void main() => runApp(const MyApp()); 116 | 117 | class MyApp extends StatelessWidget { 118 | const MyApp({Key? key}) : super(key: key); 119 | 120 | @override 121 | Widget build(BuildContext context) { 122 | return MaterialApp( 123 | title: 'Flutter Demo', 124 | theme: ThemeData( 125 | primarySwatch: Colors.blue, 126 | ), 127 | home: const MyHomePage(title: 'Flutter Demo Home Page'), 128 | ); 129 | } 130 | } 131 | 132 | class MyHomePage extends StatefulWidget { 133 | const MyHomePage({Key? key, required this.title}) : super(key: key); 134 | 135 | final String title; 136 | 137 | @override 138 | State createState() => _MyHomePageState(); 139 | } 140 | 141 | class _MyHomePageState extends State { 142 | 143 | @override 144 | Widget build(BuildContext context) { 145 | return Scaffold( 146 | appBar: AppBar( 147 | title: Text(widget.title), 148 | ), 149 | body: Swiper( 150 | itemBuilder: (context, index){ 151 | return Image.network("https://via.placeholder.com/350x150",fit: BoxFit.fill,); 152 | }, 153 | itemCount: 3, 154 | pagination: const SwiperPagination(), 155 | control: const SwiperControl(), 156 | ), 157 | ); 158 | } 159 | } 160 | ``` 161 | 162 | 163 | 164 | ### Constructor 165 | 166 | 167 | #### Basic 168 | 169 | | Parameter | Default | Description | 170 | | :------------ |:---------------:| :-----| 171 | | scrollDirection | Axis.horizontal | If `Axis.horizontal`, the scroll view's children are arranged horizontally in a row instead of vertically in a column. | 172 | | loop | true |Set to `false` to disable continuous loop mode. | 173 | | index | 0 | Index number of initial slide. | 174 | | autoplay | false |Set to `true` enable auto play mode. | 175 | | onIndexChanged | void onIndexChanged(int index) | Called with the new index when the user swiped or autoplay | 176 | | onTap | void onTap(int index) | Called when user tap ui. | 177 | | duration | 300.0 | The milliscends of every transaction animation costs | 178 | | pagination | null | set `SwiperPagination()` to show default pagination 179 | | control | null | set `SwiperControl()` to show default control buttons 180 | 181 | 182 | #### Pagination 183 | 184 | The pagination extends from `SwiperPlugin`,the `SwiperPlugin` provides extra ui for `Swiper`.Set `SwiperPagination()` to show default pagination. 185 | 186 | 187 | | Parameter | Default | Description | 188 | | :------------ |:---------------:| :-----| 189 | | alignment | Alignment.bottomCenter | Change this value if you what to put pagination in other place | 190 | | margin | const EdgeInsets.all(10.0) | The distance between inner side of the parent container. | 191 | | builder | SwiperPagination.dots | There are three default styles `SwiperPagination.dots` and `SwiperPagination.fraction`、 `SwiperPagination.rect`,both can be customized. | 192 | 193 | If you'd like to customize your own pagination, you can do like this: 194 | 195 | ```dart 196 | Swiper( 197 | ..., 198 | pagination: SwiperCustomPagination( 199 | builder: (context, config){ 200 | return YourOwnPaginatipon(); 201 | }, 202 | ) 203 | ); 204 | ``` 205 | 206 | 207 | 208 | #### Control buttons 209 | 210 | The control also extends from `SwiperPlugin`,set `SwiperControl()` to show default control buttons. 211 | 212 | 213 | | Parameter | Default | Description | 214 | | :------------ |:---------------:| :-----| 215 | | iconPrevious | Icons.arrow_back_ios | The icon data to display `previous` control button | 216 | | iconNext | Icons.arrow_forward_ios | The icon data to display `next`. | 217 | | color | Theme.of(context).primaryColor | Control button color | 218 | | size | 30.0 | Control button size | 219 | | padding | const EdgeInsets.all(5.0) | Control button padding | 220 | 221 | 222 | #### Controller 223 | 224 | The `Controller` is used to control the `index` of the Swiper, start or stop autoplay.You can create a controller by `SwiperController()` and save the instance by futher usage. 225 | 226 | 227 | | Method | Description | 228 | | :------------ |:-----| 229 | | void move(int index, {bool animation: true}) | Move to the spicified `index`,with animation or not | 230 | | void next({bool animation: true}) | Move to next | 231 | | void previous({bool animation: true}) | Move to previous | 232 | | void startAutoplay() | Start autoplay | 233 | | void stopAutoplay() | Stop autoplay | 234 | 235 | 236 | 237 | #### Autoplay 238 | 239 | | Parameter | Default | Description | 240 | | :------------ |:---------------:| :-----| 241 | | autoplayDelay | 3000 | Autoplay delay milliseconds. | 242 | | autoplayDisableOnInteraction | true | If set true, `autoplay` is disabled when use swipes. | 243 | 244 | ## Build in layouts 245 | ![](https://github.com/jzoom/images/raw/master/layout1.gif) 246 | 247 | ```dart 248 | Swiper( 249 | itemBuilder: (context, index) { 250 | return Image.network( 251 | "https://via.placeholder.com/288x188", 252 | fit: BoxFit.fill, 253 | ); 254 | }, 255 | itemCount: 10, 256 | viewportFraction: 0.8, 257 | scale: 0.9, 258 | ) 259 | ``` 260 | 261 | 262 | 263 | ![](https://github.com/jzoom/images/raw/master/layout2.gif) 264 | 265 | ```dart 266 | Swiper( 267 | itemBuilder: (context, index) { 268 | return Image.network( 269 | "https://via.placeholder.com/288x188", 270 | fit: BoxFit.fill, 271 | ); 272 | }, 273 | itemCount: 10, 274 | itemWidth: 300.0, 275 | layout: SwiperLayout.STACK, 276 | ) 277 | ``` 278 | 279 | ![](https://github.com/jzoom/images/raw/master/layout3.gif) 280 | 281 | ```dart 282 | Swiper( 283 | itemBuilder: (context, index) { 284 | return Image.network( 285 | "https://via.placeholder.com/288x188", 286 | fit: BoxFit.fill, 287 | ); 288 | }, 289 | itemCount: 10, 290 | itemWidth: 300.0, 291 | itemHeight: 400.0, 292 | layout: SwiperLayout.TINDER, 293 | ) 294 | ``` 295 | 296 | 297 | ![](https://github.com/jzoom/images/raw/master/layout4.gif) 298 | 299 | Very easy to create you own custom animation: 300 | 301 | ```dart 302 | Swiper( 303 | layout: SwiperLayout.CUSTOM, 304 | customLayoutOption: CustomLayoutOption(startIndex: -1, stateCount: 3) 305 | ..addRotate([-45.0 / 180, 0.0, 45.0 / 180]) 306 | ..addTranslate([ 307 | const Offset(-370.0, -40.0), 308 | const Offset(0.0, 0.0), 309 | const Offset(370.0, -40.0) 310 | ]), 311 | itemWidth: 300.0, 312 | itemHeight: 200.0, 313 | itemBuilder: (context, index) { 314 | return Container( 315 | color: Colors.grey, 316 | child: Center( 317 | child: Text("$index"), 318 | ), 319 | ); 320 | }, 321 | itemCount: 10, 322 | ), 323 | ``` 324 | 325 | The `CustomLayoutOption` is designed to describe animations. 326 | It is very easy to specify every state of items in Swiper. 327 | 328 | ```dart 329 | CustomLayoutOption( 330 | startIndex: -1, /// Which index is the first item of array below 331 | stateCount: 3 /// array length 332 | )..addRotate([ // rotation of every item 333 | -45.0/180, 334 | 0.0, 335 | 45.0/180 336 | ])..addTranslate([ /// offset of every item 337 | const Offset(-370.0, -40.0), 338 | const Offset(0.0, 0.0), 339 | const Offset(370.0, -40.0) 340 | ]) 341 | ``` 342 | 343 | ## Codes 344 | 345 | ![Example](https://github.com/jzoom/images/raw/master/swiper-example.gif) 346 | 347 | ```dart 348 | ConstrainedBox( 349 | constraints: BoxConstraints.loose(Size(screenWidth, 170.0)) 350 | child: Swiper( 351 | outer: false, 352 | itemBuilder: (c, i) { 353 | return Wrap( 354 | runSpacing: 6.0, 355 | children: [0,1,2,3,4,5,6,7,8,9].map((i){ 356 | return SizedBox( 357 | width: MediaQuery.of(context).size.width/5, 358 | child: Column( 359 | mainAxisSize: MainAxisSize.min, 360 | children: [ 361 | SizedBox( 362 | height: MediaQuery.of(context).size.width * 0.12, 363 | width: MediaQuery.of(context).size.width * 0.12, 364 | child: Image.network("https://fuss10.elemecdn.com/c/db/d20d49e5029281b9b73db1c5ec6f9jpeg.jpeg%3FimageMogr/format/webp/thumbnail/!90x90r/gravity/Center/crop/90x90"), 365 | ), 366 | Padding( 367 | padding: const EdgeInsets.only(top: 6.0), 368 | child: Text("$i"), 369 | ) 370 | ], 371 | ), 372 | ); 373 | }).toList(), 374 | ); 375 | }, 376 | pagination: const SwiperPagination(margin: EdgeInsets.all(5.0)), 377 | itemCount: 10, 378 | ), 379 | ), 380 | ``` 381 | 382 | 383 | 384 | 385 | 386 | You can find all custom options here: 387 | 388 | >https://github.com/feicien/flutter_swiper_view/blob/master/example/lib/src/example_custom.dart 389 | 390 | 391 | 392 | 393 | 394 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/banner.jpg -------------------------------------------------------------------------------- /dev/bots/travis_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "$PWD" 4 | export ROOT="$PWD" 5 | 6 | mkdir ~/development 7 | 8 | cd ~/development 9 | wget https://storage.googleapis.com/flutter_infra/releases/beta/linux/flutter_linux_v0.4.4-beta.tar.xz 10 | tar xf ~/development/flutter_linux_v0.4.4-beta.tar.xz 11 | 12 | export PATH=~/development/flutter/bin:$PATH 13 | 14 | 15 | cd $ROOT 16 | flutter packages get 17 | 18 | gem install coveralls-lcov 19 | -------------------------------------------------------------------------------- /dev/bots/travis_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | export PATH=~/development/flutter/bin:$PATH 5 | export ROOT="$PWD" 6 | 7 | if [[ "$SHARD" == "dartfmt" ]]; then 8 | echo 'Formating code' 9 | cd $ROOT 10 | flutter format . || exit $? 11 | else 12 | # tests shard 13 | cd $ROOT 14 | 15 | flutter test --coverage test/* || exit $? 16 | 17 | fi 18 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | 9 | .flutter-plugins 10 | pubspec.lock 11 | -------------------------------------------------------------------------------- /example/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /example/.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /example/.idea/libraries/Flutter_Plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/.idea/libraries/Flutter_for_Android.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /example/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/.idea/runConfigurations/main_dart.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 44b7e7d3f42f050a79712daab253af06e9daf530 8 | channel: beta 9 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | For help getting started with Flutter, view our online 8 | [documentation](https://flutter.io/). 9 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.class 3 | .gradle 4 | /local.properties 5 | /.idea/ 6 | .DS_Store 7 | /build 8 | /captures 9 | GeneratedPluginRegistrant.java 10 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | apply plugin: 'com.android.application' 15 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 16 | 17 | android { 18 | compileSdkVersion 32 19 | 20 | 21 | defaultConfig { 22 | applicationId "com.example.example" 23 | minSdkVersion 16 24 | targetSdkVersion 32 25 | versionCode 1 26 | versionName "1.0" 27 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 28 | } 29 | 30 | buildTypes { 31 | release { 32 | // Signing with the debug keys for now, so `flutter run --release` works. 33 | signingConfig signingConfigs.debug 34 | } 35 | } 36 | 37 | compileOptions { 38 | sourceCompatibility JavaVersion.VERSION_1_8 39 | targetCompatibility JavaVersion.VERSION_1_8 40 | } 41 | lint { 42 | disable 'InvalidPackage' 43 | } 44 | } 45 | 46 | flutter { 47 | source '../..' 48 | } 49 | 50 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 18 | 26 | 30 | 34 | 35 | 36 | 37 | 38 | 39 | 41 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.example; 2 | 3 | 4 | import io.flutter.embedding.android.FlutterActivity; 5 | 6 | public class MainActivity extends FlutterActivity { 7 | } 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:7.2.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | mavenCentral() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip 7 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/example.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /example/example_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /example/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/images/1.png -------------------------------------------------------------------------------- /example/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/images/2.png -------------------------------------------------------------------------------- /example/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/images/3.png -------------------------------------------------------------------------------- /example/images/bg.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/images/bg.jpeg -------------------------------------------------------------------------------- /example/images/bg0.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/images/bg0.jpeg -------------------------------------------------------------------------------- /example/images/bg1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/images/bg1.jpeg -------------------------------------------------------------------------------- /example/images/bg2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/images/bg2.jpeg -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/app.flx 37 | /Flutter/app.zip 38 | /Flutter/flutter_assets/ 39 | /Flutter/App.framework 40 | /Flutter/Flutter.framework 41 | /Flutter/Generated.xcconfig 42 | /ServiceDefinitions.json 43 | 44 | Pods/ 45 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | UIRequiredDeviceCapabilities 24 | 25 | arm64 26 | 27 | MinimumOSVersion 28 | 8.0 29 | 30 | 31 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/MyLaunch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/MyLaunch.jpg -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 7 | [GeneratedPluginRegistrant registerWithRegistry:self]; 8 | // Override point for customization after application launch. 9 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 10 | } 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | arm64 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /example/ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/lib/inner_swiper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 3 | 4 | void main() => runApp(const MyApp()); 5 | 6 | class MyApp extends StatelessWidget { 7 | const MyApp({Key? key}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return const MaterialApp( 12 | home: InnerSwiper(), 13 | ); 14 | } 15 | } 16 | 17 | class InnerSwiper extends StatefulWidget { 18 | const InnerSwiper({Key? key}) : super(key: key); 19 | 20 | @override 21 | State createState() { 22 | return _InnerSwiperState(); 23 | } 24 | } 25 | 26 | class _InnerSwiperState extends State { 27 | late SwiperController controller; 28 | 29 | late List autoPlayer; 30 | 31 | late List controllers; 32 | 33 | @override 34 | void initState() { 35 | controller = SwiperController(); 36 | autoPlayer = List.generate(10, (index) => false); 37 | controllers = List.generate(10, (index) => SwiperController()); 38 | super.initState(); 39 | } 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | return Scaffold( 44 | body: Swiper( 45 | loop: false, 46 | itemCount: 10, 47 | controller: controller, 48 | pagination: const SwiperPagination(), 49 | itemBuilder: (context, index) { 50 | return Column( 51 | children: [ 52 | SizedBox( 53 | height: 300.0, 54 | child: Swiper( 55 | controller: controllers[index], 56 | pagination: const SwiperPagination(), 57 | itemCount: 4, 58 | itemBuilder: (context, index) { 59 | return Container( 60 | color: Colors.greenAccent, 61 | child: const Text("jkfjkldsfjd"), 62 | ); 63 | }, 64 | autoplay: autoPlayer[index], 65 | ), 66 | ), 67 | ElevatedButton( 68 | onPressed: () { 69 | setState(() { 70 | autoPlayer[index] = true; 71 | }); 72 | }, 73 | child: const Text("Start autoplay"), 74 | ), 75 | ElevatedButton( 76 | onPressed: () { 77 | setState(() { 78 | autoPlayer[index] = false; 79 | }); 80 | }, 81 | child: const Text("End autoplay"), 82 | ), 83 | Text("is autoplay: ${autoPlayer[index]}") 84 | ], 85 | ); 86 | }, 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /example/lib/listener_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 4 | 5 | void main() => runApp(const MyApp()); 6 | 7 | class MyApp extends StatelessWidget { 8 | const MyApp({Key? key}) : super(key: key); 9 | 10 | // This widget is the root of your application. 11 | @override 12 | Widget build(BuildContext context) { 13 | return MaterialApp( 14 | title: 'Flutter Demo', 15 | theme: ThemeData( 16 | primarySwatch: Colors.blue, 17 | ), 18 | home: const MyHomePage(title: 'Flutter Swiper'), 19 | ); 20 | } 21 | } 22 | 23 | class MyHomePage extends StatefulWidget { 24 | const MyHomePage({Key? key, required this.title}) : super(key: key); 25 | 26 | final String title; 27 | 28 | @override 29 | State createState() => _MyHomePageState(); 30 | } 31 | 32 | class _MyHomePageState extends State { 33 | @override 34 | Widget build(BuildContext context) { 35 | return Scaffold( 36 | body: Swiper( 37 | itemCount: 10, 38 | itemBuilder: (c, i) { 39 | return Text("$i"); 40 | }, 41 | plugins: const [], 42 | ), 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/gestures.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 5 | import 'src/example_custom.dart'; 6 | import 'src/config.dart'; 7 | import 'src/example_swiper_in_scrollview.dart'; 8 | 9 | void main() => runApp(const MyApp()); 10 | 11 | class MyScrollBehavior extends MaterialScrollBehavior { 12 | @override 13 | Set get dragDevices => { 14 | PointerDeviceKind.touch, 15 | PointerDeviceKind.mouse, 16 | }; 17 | } 18 | 19 | class MyApp extends StatelessWidget { 20 | const MyApp({Key? key}) : super(key: key); 21 | 22 | // This widget is the root of your application. 23 | @override 24 | Widget build(BuildContext context) { 25 | return MaterialApp( 26 | scrollBehavior: MyScrollBehavior(), 27 | title: 'Flutter Demo', 28 | theme: ThemeData.light(), 29 | home: const MyHomePage(title: 'Flutter Swiper'), 30 | //home: buildHome(), 31 | routes: { 32 | '/example01': (context) => const ExampleHorizontal(), 33 | '/example02': (context) => const ExampleVertical(), 34 | '/example03': (context) => const ExampleFraction(), 35 | '/example04': (context) => const ExampleCustomPagination(), 36 | '/example05': (context) => const ExamplePhone(), 37 | '/example06': (context) => const ScaffoldWidget( 38 | title: "ScrollView", child: ExampleSwiperInScrollView()), 39 | '/example07': (context) => const ScaffoldWidget( 40 | title: "Custom All", 41 | child: ExampleCustom(), 42 | ), 43 | '/example08': (context) => const ExampleRect(), 44 | }, 45 | ); 46 | } 47 | } 48 | 49 | class MyHomePage extends StatefulWidget { 50 | const MyHomePage({Key? key, required this.title}) : super(key: key); 51 | 52 | final String title; 53 | 54 | @override 55 | State createState() => _MyHomePageState(); 56 | } 57 | 58 | class _MyHomePageState extends State { 59 | List render(BuildContext context, List> children) { 60 | return ListTile.divideTiles( 61 | context: context, 62 | tiles: children.map((data) { 63 | return buildListTile(context, data[0], data[1], data[2]); 64 | }), 65 | ).toList(); 66 | } 67 | 68 | Widget buildListTile( 69 | BuildContext context, String title, String subtitle, String url) { 70 | return ListTile( 71 | onTap: () { 72 | Navigator.of(context).pushNamed(url); 73 | }, 74 | isThreeLine: true, 75 | dense: false, 76 | leading: null, 77 | title: Text(title), 78 | subtitle: Text(subtitle), 79 | trailing: const Icon( 80 | Icons.arrow_right, 81 | color: Colors.blueAccent, 82 | ), 83 | ); 84 | } 85 | 86 | @override 87 | Widget build(BuildContext context) { 88 | // DateTime moonLanding = DateTime.parse("1969-07-20"); 89 | 90 | return Scaffold( 91 | appBar: AppBar( 92 | title: Text(widget.title), 93 | ), 94 | body: ListView( 95 | children: render(context, [ 96 | ["Horizontal", "Scroll Horizontal", "/example01"], 97 | ["Vertical", "Scroll Vertical", "/example02"], 98 | ["Fraction", "Fraction style", "/example03"], 99 | ["Rect", "Rect style", "/example08"], 100 | ["Custom Pagination", "Custom Pagination", "/example04"], 101 | ["Phone", "Phone view", "/example05"], 102 | ["ScrollView ", "In a ScrollView", "/example06"], 103 | ["Custom", "Custom all properties", "/example07"], 104 | ]), 105 | ), 106 | ); 107 | } 108 | } 109 | 110 | const List titles = [ 111 | "Flutter Swiper is awosome", 112 | "Really nice", 113 | "Yeap" 114 | ]; 115 | 116 | class ExampleHorizontal extends StatelessWidget { 117 | const ExampleHorizontal({Key? key}) : super(key: key); 118 | 119 | @override 120 | Widget build(BuildContext context) { 121 | return Scaffold( 122 | appBar: AppBar( 123 | title: const Text("ExampleHorizontal"), 124 | ), 125 | body: Swiper( 126 | itemBuilder: (context, index) { 127 | return Image.asset( 128 | images[index], 129 | fit: BoxFit.fill, 130 | ); 131 | }, 132 | indicatorLayout: PageIndicatorLayout.COLOR, 133 | autoplay: true, 134 | itemCount: images.length, 135 | pagination: const SwiperPagination(), 136 | control: const SwiperControl(), 137 | )); 138 | } 139 | } 140 | 141 | class ExampleVertical extends StatelessWidget { 142 | const ExampleVertical({Key? key}) : super(key: key); 143 | 144 | @override 145 | Widget build(BuildContext context) { 146 | return Scaffold( 147 | appBar: AppBar( 148 | title: const Text("ExampleVertical"), 149 | ), 150 | body: Swiper( 151 | itemBuilder: (context, index) { 152 | return Image.asset( 153 | images[index], 154 | fit: BoxFit.fill, 155 | ); 156 | }, 157 | autoplay: true, 158 | itemCount: images.length, 159 | scrollDirection: Axis.vertical, 160 | pagination: const SwiperPagination(alignment: Alignment.centerRight), 161 | control: const SwiperControl(), 162 | )); 163 | } 164 | } 165 | 166 | class ExampleFraction extends StatelessWidget { 167 | const ExampleFraction({Key? key}) : super(key: key); 168 | 169 | @override 170 | Widget build(BuildContext context) { 171 | return Scaffold( 172 | appBar: AppBar( 173 | title: const Text("ExampleFraction"), 174 | ), 175 | body: Column( 176 | children: [ 177 | Expanded( 178 | child: Swiper( 179 | itemBuilder: (context, index) { 180 | return Image.asset( 181 | images[index], 182 | fit: BoxFit.fill, 183 | ); 184 | }, 185 | autoplay: true, 186 | itemCount: images.length, 187 | pagination: 188 | const SwiperPagination(builder: SwiperPagination.fraction), 189 | control: const SwiperControl(), 190 | )), 191 | Expanded( 192 | child: Swiper( 193 | itemBuilder: (context, index) { 194 | return Image.asset( 195 | images[index], 196 | fit: BoxFit.fill, 197 | ); 198 | }, 199 | autoplay: true, 200 | itemCount: images.length, 201 | scrollDirection: Axis.vertical, 202 | pagination: const SwiperPagination( 203 | alignment: Alignment.centerRight, 204 | builder: SwiperPagination.fraction), 205 | )) 206 | ], 207 | )); 208 | } 209 | } 210 | 211 | class ExampleRect extends StatelessWidget { 212 | const ExampleRect({Key? key}) : super(key: key); 213 | 214 | @override 215 | Widget build(BuildContext context) { 216 | return Scaffold( 217 | appBar: AppBar( 218 | title: const Text("ExampleFraction"), 219 | ), 220 | body: Column( 221 | children: [ 222 | Expanded( 223 | child: Swiper( 224 | itemBuilder: (context, index) { 225 | return Image.asset( 226 | images[index], 227 | fit: BoxFit.fill, 228 | ); 229 | }, 230 | autoplay: true, 231 | itemCount: images.length, 232 | pagination: 233 | const SwiperPagination(builder: SwiperPagination.rect), 234 | control: const SwiperControl(), 235 | ), 236 | ), 237 | Expanded( 238 | child: Swiper( 239 | itemBuilder: (context, index) { 240 | return Image.asset( 241 | images[index], 242 | fit: BoxFit.fill, 243 | ); 244 | }, 245 | autoplay: true, 246 | itemCount: images.length, 247 | scrollDirection: Axis.vertical, 248 | pagination: const SwiperPagination( 249 | alignment: Alignment.centerRight, 250 | builder: SwiperPagination.rect, 251 | ), 252 | ), 253 | ) 254 | ], 255 | )); 256 | } 257 | } 258 | 259 | class ExampleCustomPagination extends StatelessWidget { 260 | const ExampleCustomPagination({Key? key}) : super(key: key); 261 | 262 | @override 263 | Widget build(BuildContext context) { 264 | return Scaffold( 265 | appBar: AppBar( 266 | title: const Text("Custom Pagination"), 267 | ), 268 | body: Column( 269 | children: [ 270 | Expanded( 271 | child: Swiper( 272 | itemBuilder: (context, index) { 273 | return Image.asset( 274 | images[index], 275 | fit: BoxFit.fill, 276 | ); 277 | }, 278 | autoplay: true, 279 | itemCount: images.length, 280 | pagination: SwiperPagination( 281 | margin: const EdgeInsets.all(0.0), 282 | builder: SwiperCustomPagination(builder: 283 | (BuildContext context, SwiperPluginConfig config) { 284 | return ConstrainedBox( 285 | constraints: const BoxConstraints.expand(height: 50.0), 286 | child: Container( 287 | color: Colors.white, 288 | child: Text( 289 | "${titles[config.activeIndex]} ${config.activeIndex + 1}/${config.itemCount}", 290 | style: const TextStyle(fontSize: 20.0), 291 | )), 292 | ); 293 | })), 294 | control: const SwiperControl(), 295 | ), 296 | ), 297 | Expanded( 298 | child: Swiper( 299 | itemBuilder: (context, index) { 300 | return Image.asset( 301 | images[index], 302 | fit: BoxFit.fill, 303 | ); 304 | }, 305 | autoplay: true, 306 | itemCount: images.length, 307 | pagination: SwiperPagination( 308 | margin: const EdgeInsets.all(0.0), 309 | builder: SwiperCustomPagination(builder: 310 | (BuildContext context, SwiperPluginConfig config) { 311 | return ConstrainedBox( 312 | constraints: const BoxConstraints.expand(height: 50.0), 313 | child: Row( 314 | children: [ 315 | Text( 316 | "${titles[config.activeIndex]} ${config.activeIndex + 1}/${config.itemCount}", 317 | style: const TextStyle(fontSize: 20.0), 318 | ), 319 | Expanded( 320 | child: Align( 321 | alignment: Alignment.centerRight, 322 | child: const DotSwiperPaginationBuilder( 323 | color: Colors.black12, 324 | activeColor: Colors.black, 325 | size: 10.0, 326 | activeSize: 20.0) 327 | .build(context, config), 328 | ), 329 | ) 330 | ], 331 | ), 332 | ); 333 | })), 334 | control: const SwiperControl(color: Colors.redAccent), 335 | ), 336 | ) 337 | ], 338 | )); 339 | } 340 | } 341 | 342 | class ExamplePhone extends StatelessWidget { 343 | const ExamplePhone({Key? key}) : super(key: key); 344 | 345 | @override 346 | Widget build(BuildContext context) { 347 | return Scaffold( 348 | appBar: AppBar( 349 | title: const Text("Phone"), 350 | ), 351 | body: Stack( 352 | children: [ 353 | ConstrainedBox( 354 | constraints: const BoxConstraints.expand(), 355 | child: Image.asset( 356 | "images/bg.jpeg", 357 | fit: BoxFit.fill, 358 | ), 359 | ), 360 | Swiper.children( 361 | autoplay: false, 362 | pagination: const SwiperPagination( 363 | margin: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 30.0), 364 | builder: DotSwiperPaginationBuilder( 365 | color: Colors.white30, 366 | activeColor: Colors.white, 367 | size: 20.0, 368 | activeSize: 20.0)), 369 | children: [ 370 | Image.asset( 371 | "images/1.png", 372 | fit: BoxFit.contain, 373 | ), 374 | Image.asset( 375 | "images/2.png", 376 | fit: BoxFit.contain, 377 | ), 378 | Image.asset("images/3.png", fit: BoxFit.contain) 379 | ], 380 | ) 381 | ], 382 | ), 383 | ); 384 | } 385 | } 386 | 387 | class ScaffoldWidget extends StatelessWidget { 388 | final Widget child; 389 | final String title; 390 | final List? actions; 391 | 392 | const ScaffoldWidget({ 393 | Key? key, 394 | required this.title, 395 | required this.child, 396 | this.actions, 397 | }) : super(key: key); 398 | 399 | @override 400 | Widget build(BuildContext context) { 401 | return Scaffold( 402 | appBar: AppBar( 403 | title: Text(title), 404 | actions: actions, 405 | ), 406 | body: child, 407 | ); 408 | } 409 | } 410 | -------------------------------------------------------------------------------- /example/lib/src/config.dart: -------------------------------------------------------------------------------- 1 | const List images = [ 2 | "images/bg0.jpeg", 3 | "images/bg1.jpeg", 4 | "images/bg2.jpeg", 5 | ]; 6 | -------------------------------------------------------------------------------- /example/lib/src/example_custom.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 3 | 4 | import 'config.dart'; 5 | import 'forms/form_widget.dart'; 6 | 7 | class ExampleCustom extends StatefulWidget { 8 | const ExampleCustom({Key? key}) : super(key: key); 9 | 10 | @override 11 | State createState() { 12 | return _ExampleCustomState(); 13 | } 14 | } 15 | 16 | class _ExampleCustomState extends State { 17 | //properties want to custom 18 | late int _itemCount; 19 | 20 | late bool _loop; 21 | 22 | late bool _autoplay; 23 | 24 | late int _autoplayDelay; 25 | 26 | late double _padding; 27 | 28 | late bool _outer; 29 | 30 | late double _radius; 31 | 32 | late double _viewportFraction; 33 | 34 | late SwiperLayout _layout; 35 | 36 | late int _currentIndex; 37 | 38 | late double _scale; 39 | 40 | late Axis _scrollDirection; 41 | 42 | late AxisDirection _axisDirection; 43 | 44 | late Curve _curve; 45 | 46 | late double _fade; 47 | 48 | late bool _autoplayDisableOnInteraction; 49 | 50 | late CustomLayoutOption customLayoutOption; 51 | 52 | late SwiperController _controller; 53 | 54 | TextEditingController numberController = TextEditingController(); 55 | 56 | Widget _buildItem(BuildContext context, int index) { 57 | return ClipRRect( 58 | borderRadius: BorderRadius.all(Radius.circular(_radius)), 59 | child: Image.asset( 60 | images[index % images.length], 61 | fit: BoxFit.fill, 62 | ), 63 | ); 64 | } 65 | 66 | @override 67 | void didUpdateWidget(ExampleCustom oldWidget) { 68 | customLayoutOption = CustomLayoutOption(startIndex: -1, stateCount: 3) 69 | ..addRotate([-45.0 / 180, 0.0, 45.0 / 180]) 70 | ..addTranslate([ 71 | const Offset(-370.0, -40.0), 72 | const Offset(0.0, 0.0), 73 | const Offset(370.0, -40.0) 74 | ]); 75 | super.didUpdateWidget(oldWidget); 76 | } 77 | 78 | @override 79 | void initState() { 80 | customLayoutOption = CustomLayoutOption(startIndex: -1, stateCount: 3) 81 | ..addRotate([-25.0 / 180, 0.0, 25.0 / 180]) 82 | ..addTranslate([ 83 | const Offset(-350.0, 0.0), 84 | const Offset(0.0, 0.0), 85 | const Offset(350.0, 0.0) 86 | ]); 87 | _fade = 1.0; 88 | _currentIndex = 0; 89 | _curve = Curves.ease; 90 | _scale = 0.8; 91 | _autoplay = false; 92 | _controller = SwiperController(); 93 | _layout = SwiperLayout.TINDER; 94 | _radius = 10.0; 95 | _padding = 0.0; 96 | _loop = true; 97 | _itemCount = 3; 98 | _autoplayDelay = 3000; 99 | _viewportFraction = 0.8; 100 | _outer = false; 101 | _scrollDirection = Axis.horizontal; 102 | _axisDirection = AxisDirection.left; 103 | _autoplayDisableOnInteraction = false; 104 | super.initState(); 105 | } 106 | 107 | // maintain the index 108 | 109 | Widget buildSwiper() { 110 | return Swiper( 111 | onTap: (index) { 112 | Navigator.of(context).push(MaterialPageRoute( 113 | builder: (context) { 114 | return Scaffold( 115 | appBar: AppBar( 116 | title: const Text('New page'), 117 | ), 118 | body: Container(), 119 | ); 120 | }, 121 | )); 122 | }, 123 | customLayoutOption: customLayoutOption, 124 | fade: _fade, 125 | index: _currentIndex, 126 | onIndexChanged: (index) { 127 | setState(() { 128 | _currentIndex = index; 129 | }); 130 | }, 131 | curve: _curve, 132 | scale: _scale, 133 | itemWidth: 300.0, 134 | controller: _controller, 135 | layout: _layout, 136 | outer: _outer, 137 | itemHeight: 200.0, 138 | viewportFraction: _viewportFraction, 139 | autoplayDelay: _autoplayDelay, 140 | loop: _loop, 141 | autoplay: _autoplay, 142 | itemBuilder: _buildItem, 143 | itemCount: _itemCount, 144 | scrollDirection: _scrollDirection, 145 | axisDirection: _axisDirection, 146 | indicatorLayout: PageIndicatorLayout.COLOR, 147 | autoplayDisableOnInteraction: _autoplayDisableOnInteraction, 148 | pagination: const SwiperPagination( 149 | builder: DotSwiperPaginationBuilder( 150 | size: 20.0, activeSize: 20.0, space: 10.0)), 151 | ); 152 | } 153 | 154 | @override 155 | Widget build(BuildContext context) { 156 | return Column( 157 | children: [ 158 | Container( 159 | color: Colors.black87, 160 | child: SizedBox( 161 | height: 300.0, 162 | width: double.infinity, 163 | child: buildSwiper(), 164 | ), 165 | ), 166 | Expanded( 167 | child: ListView( 168 | children: [ 169 | Text('Index:$_currentIndex'), 170 | Row( 171 | children: [ 172 | ElevatedButton( 173 | onPressed: () { 174 | _controller.previous(animation: true); 175 | }, 176 | child: const Text('Prev'), 177 | ), 178 | ElevatedButton( 179 | onPressed: () { 180 | _controller.next(animation: true); 181 | }, 182 | child: const Text('Next'), 183 | ), 184 | Expanded( 185 | child: TextField( 186 | controller: numberController, 187 | )), 188 | ElevatedButton( 189 | onPressed: () { 190 | final text = numberController.text; 191 | setState(() { 192 | _currentIndex = int.tryParse(text) ?? 0; 193 | }); 194 | }, 195 | child: const Text('Update'), 196 | ), 197 | ], 198 | ), 199 | FormWidget( 200 | label: 'layout', 201 | child: FormSelect( 202 | placeholder: 'Select layout', 203 | value: _layout, 204 | values: const [ 205 | SwiperLayout.DEFAULT, 206 | SwiperLayout.STACK, 207 | SwiperLayout.TINDER, 208 | SwiperLayout.CUSTOM 209 | ], 210 | valueChanged: (value) { 211 | _layout = value; 212 | setState(() {}); 213 | }, 214 | ), 215 | ), 216 | FormWidget( 217 | label: 'scrollDirection', 218 | child: Switch( 219 | value: _scrollDirection == Axis.horizontal, 220 | onChanged: (value) => setState(() => _scrollDirection = 221 | value ? Axis.horizontal : Axis.vertical)), 222 | ), 223 | if (_layout == SwiperLayout.STACK) 224 | FormWidget( 225 | label: 'axisDirection (left <-> right)', 226 | child: Switch( 227 | value: _axisDirection == AxisDirection.right, 228 | onChanged: (value) => setState(() => _axisDirection = 229 | value ? AxisDirection.right : AxisDirection.left)), 230 | ), 231 | FormWidget( 232 | label: 'autoplayDisableOnInteraction', 233 | child: Switch( 234 | value: _autoplayDisableOnInteraction, 235 | onChanged: (value) => 236 | setState(() => _autoplayDisableOnInteraction = value)), 237 | ), 238 | //Pannel Begin 239 | FormWidget( 240 | label: 'loop', 241 | child: Switch( 242 | value: _loop, 243 | onChanged: (value) => setState(() => _loop = value)), 244 | ), 245 | FormWidget( 246 | label: 'outer', 247 | child: Switch( 248 | value: _outer, 249 | onChanged: (value) => setState(() => _outer = value)), 250 | ), 251 | //Pannel Begin 252 | FormWidget( 253 | label: 'autoplay', 254 | child: Switch( 255 | value: _autoplay, 256 | onChanged: (value) => setState(() => _autoplay = value)), 257 | ), 258 | 259 | FormWidget( 260 | label: 'padding', 261 | child: NumberPad( 262 | number: _padding, 263 | step: 5.0, 264 | min: 0.0, 265 | max: 30.0, 266 | onChangeValue: (value) { 267 | _padding = value.toDouble(); 268 | setState(() {}); 269 | }, 270 | ), 271 | ), 272 | FormWidget( 273 | label: 'scale', 274 | child: NumberPad( 275 | number: _scale, 276 | step: 0.1, 277 | min: 0.0, 278 | max: 1.0, 279 | onChangeValue: (value) { 280 | _scale = value.toDouble(); 281 | setState(() {}); 282 | }, 283 | ), 284 | ), 285 | FormWidget( 286 | label: 'fade', 287 | child: NumberPad( 288 | number: _fade, 289 | step: 0.1, 290 | min: 0.0, 291 | max: 1.0, 292 | onChangeValue: (value) { 293 | _fade = value.toDouble(); 294 | setState(() {}); 295 | }, 296 | ), 297 | ), 298 | FormWidget( 299 | label: 'itemCount', 300 | child: NumberPad( 301 | number: _itemCount, 302 | step: 1, 303 | min: 0, 304 | max: 100, 305 | onChangeValue: (value) { 306 | _itemCount = value.toInt(); 307 | setState(() {}); 308 | }, 309 | ), 310 | ), 311 | 312 | FormWidget( 313 | label: 'radius', 314 | child: NumberPad( 315 | number: _radius, 316 | step: 1.0, 317 | min: 0.0, 318 | max: 30.0, 319 | onChangeValue: (value) { 320 | _radius = value.toDouble(); 321 | setState(() {}); 322 | }, 323 | ), 324 | ), 325 | 326 | FormWidget( 327 | label: 'viewportFraction', 328 | child: NumberPad( 329 | number: _viewportFraction, 330 | step: 0.1, 331 | max: 1.0, 332 | min: 0.5, 333 | onChangeValue: (value) { 334 | _viewportFraction = value.toDouble(); 335 | setState(() {}); 336 | }, 337 | ), 338 | ), 339 | 340 | FormWidget( 341 | label: 'curve', 342 | child: FormSelect( 343 | placeholder: 'Select curve', 344 | value: _curve, 345 | values: const [ 346 | Curves.easeInOut, 347 | Curves.ease, 348 | Curves.bounceInOut, 349 | Curves.bounceOut, 350 | Curves.bounceIn, 351 | Curves.fastOutSlowIn 352 | ], 353 | valueChanged: (value) { 354 | _curve = value; 355 | setState(() {}); 356 | }, 357 | ), 358 | ), 359 | ], 360 | ), 361 | ) 362 | ], 363 | ); 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /example/lib/src/example_swiper_in_scrollview.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 3 | 4 | class ExampleSwiperInScrollView extends StatefulWidget { 5 | const ExampleSwiperInScrollView({Key? key}) : super(key: key); 6 | 7 | @override 8 | State createState() { 9 | return _ExampleState(); 10 | } 11 | } 12 | 13 | class _ExampleState extends State 14 | with TickerProviderStateMixin { 15 | late AnimationController controller; 16 | late Animation _animation10; 17 | late Animation _animation11; 18 | late Animation _animation12; 19 | late Animation _animation13; 20 | 21 | _ExampleState(); 22 | 23 | @override 24 | void dispose() { 25 | controller.dispose(); 26 | 27 | super.dispose(); 28 | } 29 | 30 | @override 31 | void initState() { 32 | controller = AnimationController(vsync: this); 33 | _animation10 = Tween(begin: 0.0, end: 1.0).animate(controller); 34 | _animation11 = Tween(begin: 0.0, end: 1.0).animate(controller); 35 | _animation12 = Tween(begin: 0.0, end: 1.0).animate(controller); 36 | _animation13 = Tween(begin: 0.0, end: 1.0).animate(controller); 37 | controller.animateTo(1.0, duration: const Duration(seconds: 3)); 38 | super.initState(); 39 | } 40 | 41 | Widget _buildDynamicCard() { 42 | return Card( 43 | elevation: 2.0, 44 | color: Colors.white, 45 | child: Stack( 46 | children: [ 47 | Column( 48 | children: [ 49 | Container( 50 | padding: const EdgeInsets.all(10.0), 51 | ), 52 | ], 53 | ), 54 | Column( 55 | children: [ 56 | Row( 57 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 58 | children: [ 59 | Column( 60 | children: [ 61 | Container( 62 | padding: const EdgeInsets.only(top: 40.0), 63 | ), 64 | ScaleTransition( 65 | scale: _animation10, 66 | alignment: FractionalOffset.center, 67 | ), 68 | ], 69 | ), 70 | Column( 71 | children: [ 72 | Container( 73 | padding: const EdgeInsets.only(top: 160.0), 74 | ), 75 | ScaleTransition( 76 | scale: _animation11, 77 | alignment: FractionalOffset.center, 78 | ), 79 | ], 80 | ), 81 | Container( 82 | padding: const EdgeInsets.all(1.0), 83 | ), 84 | Column( 85 | children: [ 86 | Container( 87 | padding: const EdgeInsets.only(top: 160.0), 88 | ), 89 | ScaleTransition( 90 | scale: _animation12, 91 | alignment: FractionalOffset.center, 92 | ), 93 | ], 94 | ), 95 | Column( 96 | children: [ 97 | Container( 98 | padding: const EdgeInsets.only(top: 40.0), 99 | ), 100 | ScaleTransition( 101 | scale: _animation13, 102 | alignment: FractionalOffset.center, 103 | ), 104 | ], 105 | ), 106 | ]), 107 | Container( 108 | padding: const EdgeInsets.all(10.0), 109 | ), 110 | ], 111 | ) 112 | ], 113 | ), 114 | ); 115 | } 116 | 117 | @override 118 | Widget build(BuildContext context) { 119 | return Container( 120 | color: Theme.of(context).primaryColorLight, 121 | child: CustomScrollView( 122 | slivers: [ 123 | SliverList( 124 | delegate: SliverChildBuilderDelegate((c, i) { 125 | return Column( 126 | mainAxisSize: MainAxisSize.min, 127 | children: [ 128 | SizedBox( 129 | height: 100.0, 130 | child: Swiper( 131 | scale: 0.8, 132 | fade: 0.8, 133 | itemBuilder: (c, i) { 134 | return Container( 135 | color: Colors.grey, 136 | child: Text("$i"), 137 | ); 138 | }, 139 | itemCount: 10, 140 | pagination: const SwiperPagination(), 141 | ), 142 | ), 143 | SizedBox( 144 | height: 100.0, 145 | child: Swiper( 146 | scale: 0.8, 147 | fade: 0.8, 148 | itemBuilder: (c, i) { 149 | return Container( 150 | color: Colors.grey, 151 | child: Text("$i"), 152 | ); 153 | }, 154 | pagination: const SwiperPagination( 155 | builder: SwiperPagination.fraction), 156 | itemCount: 0), 157 | ), 158 | SizedBox( 159 | height: 100.0, 160 | child: Swiper( 161 | scale: 0.8, 162 | fade: 0.8, 163 | itemBuilder: (c, i) { 164 | return Container( 165 | color: Colors.grey, 166 | child: Text("$i"), 167 | ); 168 | }, 169 | pagination: const SwiperPagination( 170 | builder: SwiperPagination.fraction), 171 | itemCount: 10000), 172 | ), 173 | SizedBox( 174 | height: 100.0, 175 | child: Swiper( 176 | outer: true, 177 | scale: 0.8, 178 | fade: 0.8, 179 | itemBuilder: (c, i) { 180 | return Container( 181 | color: Colors.grey, 182 | child: Text("$i"), 183 | ); 184 | }, 185 | pagination: const SwiperPagination(), 186 | itemCount: 10), 187 | ), 188 | const Text("Image from network"), 189 | SizedBox( 190 | height: 300.0, 191 | child: Swiper( 192 | itemCount: 10, 193 | itemBuilder: (BuildContext context, int index) { 194 | return Image.network( 195 | "https://ss3.baidu.com/9fo3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=87d6daed02f41bd5c553eef461d881a0/f9198618367adab4b025268587d4b31c8601e47b.jpg"); 196 | }, 197 | ), 198 | ), 199 | SizedBox( 200 | height: 100.0, 201 | child: Swiper( 202 | outer: true, 203 | scale: 0.8, 204 | fade: 0.8, 205 | itemBuilder: (c, i) { 206 | return Card( 207 | elevation: 2.0, 208 | child: Stack( 209 | alignment: AlignmentDirectional.center, 210 | children: [ 211 | Image.network( 212 | "https://ss3.baidu.com/9fo3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=87d6daed02f41bd5c553eef461d881a0/f9198618367adab4b025268587d4b31c8601e47b.jpg"), 213 | FractionalTranslation( 214 | translation: const Offset(0.0, 0.0), 215 | child: Container( 216 | alignment: const FractionalOffset(0.0, 0.0), 217 | decoration: BoxDecoration( 218 | border: Border.all( 219 | color: Colors.lightBlue.withOpacity(0.5), 220 | width: 100.0, 221 | ), 222 | shape: BoxShape.circle, 223 | ), 224 | ), 225 | ), 226 | Container( 227 | //padding: const EdgeInsets.only(bottom:10.0), 228 | margin: const EdgeInsets.all(140.0), 229 | 230 | child: const Icon(Icons.location_on, 231 | color: Colors.white, size: 25.0), 232 | ), 233 | ], 234 | ), 235 | ); 236 | }, 237 | pagination: const SwiperPagination( 238 | alignment: Alignment.topCenter), 239 | itemCount: 10), 240 | ), 241 | SizedBox( 242 | height: 400.0, 243 | child: Swiper( 244 | outer: true, 245 | itemBuilder: (c, i) { 246 | return _buildDynamicCard(); 247 | }, 248 | pagination: const SwiperPagination( 249 | alignment: Alignment.topCenter), 250 | itemCount: 10), 251 | ), 252 | SizedBox( 253 | height: 100.0, 254 | child: Swiper( 255 | outer: true, 256 | fade: 0.8, 257 | viewportFraction: 0.8, 258 | scale: 0.8, 259 | itemBuilder: (c, i) { 260 | return Container( 261 | color: Colors.grey, 262 | child: Text("$i"), 263 | ); 264 | }, 265 | pagination: const SwiperPagination( 266 | alignment: Alignment.topCenter), 267 | itemCount: 10), 268 | ), 269 | ], 270 | ); 271 | }, childCount: 1)) 272 | ], 273 | ), 274 | ); 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /example/lib/src/forms/form_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | 4 | class FormWidget extends StatelessWidget { 5 | final String label; 6 | 7 | final Widget child; 8 | 9 | const FormWidget({ 10 | Key? key, 11 | required this.label, 12 | required this.child, 13 | }) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Padding( 18 | padding: const EdgeInsets.all(5.0), 19 | child: Row( 20 | children: [ 21 | Text(label, style: const TextStyle(fontSize: 14.0)), 22 | Expanded( 23 | child: Align( 24 | alignment: Alignment.centerRight, 25 | child: child, 26 | ), 27 | ) 28 | ], 29 | ), 30 | ); 31 | } 32 | } 33 | 34 | class FormSelect extends StatefulWidget { 35 | final String placeholder; 36 | final ValueChanged valueChanged; 37 | final List values; 38 | final T value; 39 | 40 | const FormSelect({ 41 | Key? key, 42 | required this.placeholder, 43 | required this.valueChanged, 44 | required this.value, 45 | required this.values, 46 | }) : super(key: key); 47 | 48 | @override 49 | State createState() { 50 | return _FormSelectState(); 51 | } 52 | } 53 | 54 | class _FormSelectState extends State> { 55 | int _selectedIndex = 0; 56 | 57 | @override 58 | void initState() { 59 | for (int i = 0; i < widget.values.length; ++i) { 60 | if (widget.values[i] == widget.value) { 61 | _selectedIndex = i; 62 | break; 63 | } 64 | } 65 | 66 | super.initState(); 67 | } 68 | 69 | @override 70 | Widget build(BuildContext context) { 71 | final placeholder = widget.placeholder; 72 | final values = widget.values; 73 | 74 | return InkWell( 75 | child: Text( 76 | _selectedIndex < 0 ? placeholder : values[_selectedIndex].toString()), 77 | onTap: () { 78 | _selectedIndex = 0; 79 | showBottomSheet( 80 | context: context, 81 | builder: (context) { 82 | return SizedBox( 83 | height: values.length * 30.0 + 200.0, 84 | child: Column( 85 | mainAxisSize: MainAxisSize.min, 86 | children: [ 87 | SizedBox( 88 | height: values.length * 30.0 + 70.0, 89 | child: CupertinoPicker( 90 | itemExtent: 30.0, 91 | children: values.map((value) { 92 | return Text(value.toString()); 93 | }).toList(), 94 | onSelectedItemChanged: (index) { 95 | _selectedIndex = index; 96 | }, 97 | ), 98 | ), 99 | Center( 100 | child: ElevatedButton( 101 | onPressed: () { 102 | if (_selectedIndex >= 0) { 103 | widget.valueChanged( 104 | widget.values[_selectedIndex], 105 | ); 106 | } 107 | 108 | setState(() {}); 109 | 110 | Navigator.of(context).pop(); 111 | }, 112 | child: const Text('ok'), 113 | ), 114 | ) 115 | ], 116 | ), 117 | ); 118 | }); 119 | }, 120 | ); 121 | } 122 | } 123 | 124 | class NumberPad extends StatelessWidget { 125 | final num number; 126 | final num step; 127 | final num max; 128 | final num min; 129 | final ValueChanged onChangeValue; 130 | 131 | const NumberPad({ 132 | Key? key, 133 | required this.number, 134 | required this.step, 135 | required this.onChangeValue, 136 | required this.max, 137 | required this.min, 138 | }) : super(key: key); 139 | 140 | void onAdd() { 141 | onChangeValue(number + step > max ? max : number + step); 142 | } 143 | 144 | void onSub() { 145 | onChangeValue(number - step < min ? min : number - step); 146 | } 147 | 148 | @override 149 | Widget build(BuildContext context) { 150 | return Row( 151 | mainAxisSize: MainAxisSize.min, 152 | children: [ 153 | IconButton(icon: const Icon(Icons.exposure_neg_1), onPressed: onSub), 154 | Text( 155 | number is int ? number.toString() : number.toStringAsFixed(1), 156 | style: const TextStyle(fontSize: 14.0), 157 | ), 158 | IconButton(icon: const Icon(Icons.exposure_plus_1), onPressed: onAdd) 159 | ], 160 | ); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter project. 3 | 4 | environment: 5 | sdk: '>=2.12.0 <3.0.0' 6 | 7 | dependencies: 8 | flutter: 9 | sdk: flutter 10 | 11 | # The following adds the Cupertino Icons font to your application. 12 | # Use with the CupertinoIcons class for iOS style icons. 13 | cupertino_icons: ^1.0.2 14 | 15 | flutter_swiper_view: 16 | path: ../ 17 | 18 | 19 | dev_dependencies: 20 | flutter_lints: ^2.0.1 21 | flutter_test: 22 | sdk: flutter 23 | 24 | flutter: 25 | uses-material-design: true 26 | 27 | 28 | assets: 29 | - images/bg0.jpeg 30 | - images/bg1.jpeg 31 | - images/bg2.jpeg 32 | - images/bg.jpeg 33 | - images/1.png 34 | - images/2.png 35 | - images/3.png 36 | -------------------------------------------------------------------------------- /example/res/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/res/1.gif -------------------------------------------------------------------------------- /example/res/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/res/2.gif -------------------------------------------------------------------------------- /example/res/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/res/3.gif -------------------------------------------------------------------------------- /example/res/4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/res/4.gif -------------------------------------------------------------------------------- /example/res/5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feicien/flutter_swiper_view/d9610890b4e6d0213cb2f2139b21835c96a8f0cb/example/res/5.gif -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter 3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to 4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties 5 | // are correct. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_test/flutter_test.dart'; 9 | 10 | import 'package:example/main.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(const MyApp()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /flutter_swiper.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /lib/flutter_swiper_view.dart: -------------------------------------------------------------------------------- 1 | library flutter_swiper_view; 2 | 3 | export 'src/flutter_page_indicator.dart'; 4 | export 'src/index_controller.dart'; 5 | export 'src/swiper.dart'; 6 | export 'src/swiper_pagination.dart'; 7 | export 'src/swiper_control.dart'; 8 | export 'src/swiper_controller.dart'; 9 | export 'src/swiper_plugin.dart'; 10 | -------------------------------------------------------------------------------- /lib/src/custom_layout.dart: -------------------------------------------------------------------------------- 1 | part of 'swiper.dart'; 2 | 3 | abstract class _CustomLayoutStateBase extends State 4 | with SingleTickerProviderStateMixin { 5 | late double _swiperWidth; 6 | late double _swiperHeight; 7 | late Animation _animation; 8 | late AnimationController _animationController; 9 | SwiperController get _controller => widget.controller; 10 | late int _startIndex; 11 | int? _animationCount; 12 | int _currentIndex = 0; 13 | 14 | @override 15 | void initState() { 16 | _currentIndex = widget.index ?? 0; 17 | if (widget.itemWidth == null) { 18 | throw Exception( 19 | '==============\n\nwidget.itemWidth must not be null when use stack layout.\n========\n', 20 | ); 21 | } 22 | 23 | _createAnimationController(); 24 | _controller.addListener(_onController); 25 | super.initState(); 26 | } 27 | 28 | void _createAnimationController() { 29 | _animationController = AnimationController(vsync: this, value: 0.5); 30 | Tween tween = Tween(begin: 0.0, end: 1.0); 31 | _animation = tween.animate(_animationController); 32 | } 33 | 34 | @override 35 | void didChangeDependencies() { 36 | WidgetsBinding.instance.addPostFrameCallback(_getSize); 37 | super.didChangeDependencies(); 38 | } 39 | 40 | void _getSize(Duration _) { 41 | if (!mounted) return; 42 | afterRender(); 43 | } 44 | 45 | @mustCallSuper 46 | void afterRender() { 47 | RenderObject renderObject = context.findRenderObject()!; 48 | Size size = renderObject.paintBounds.size; 49 | _swiperWidth = size.width; 50 | _swiperHeight = size.height; 51 | setState(() {}); 52 | } 53 | 54 | @override 55 | void didUpdateWidget(T oldWidget) { 56 | if (widget.controller != oldWidget.controller) { 57 | oldWidget.controller.removeListener(_onController); 58 | widget.controller.addListener(_onController); 59 | } 60 | 61 | if (widget.loop != oldWidget.loop) { 62 | if (!widget.loop) { 63 | _currentIndex = _ensureIndex(_currentIndex); 64 | } 65 | } 66 | 67 | if (widget.axisDirection != oldWidget.axisDirection) { 68 | afterRender(); 69 | } 70 | 71 | super.didUpdateWidget(oldWidget); 72 | } 73 | 74 | int _ensureIndex(int index) { 75 | var res = index; 76 | res = index % widget.itemCount; 77 | if (res < 0) { 78 | res += widget.itemCount; 79 | } 80 | return res; 81 | } 82 | 83 | @override 84 | void dispose() { 85 | widget.controller.removeListener(_onController); 86 | _animationController.dispose(); 87 | super.dispose(); 88 | } 89 | 90 | Widget _buildItem(int i, int realIndex, double animationValue); 91 | 92 | Widget _buildContainer(List list) { 93 | return Stack( 94 | children: list, 95 | ); 96 | } 97 | 98 | Widget _buildAnimation(BuildContext context, Widget? w) { 99 | List list = []; 100 | 101 | double animationValue = _animation.value; 102 | 103 | for (int i = 0; i < _animationCount! && widget.itemCount > 0; ++i) { 104 | int realIndex = _currentIndex + i + _startIndex; 105 | realIndex = realIndex % widget.itemCount; 106 | if (realIndex < 0) { 107 | realIndex += widget.itemCount; 108 | } 109 | 110 | if (widget.axisDirection == AxisDirection.right) { 111 | list.insert(0, _buildItem(i, realIndex, animationValue)); 112 | } else { 113 | list.add(_buildItem(i, realIndex, animationValue)); 114 | } 115 | } 116 | 117 | return GestureDetector( 118 | behavior: HitTestBehavior.opaque, 119 | onPanStart: _onPanStart, 120 | onPanEnd: _onPanEnd, 121 | onPanUpdate: _onPanUpdate, 122 | child: ClipRect( 123 | child: Center( 124 | child: _buildContainer(list), 125 | ), 126 | ), 127 | ); 128 | } 129 | 130 | @override 131 | Widget build(BuildContext context) { 132 | if (_animationCount == null) { 133 | return Container(); 134 | } 135 | return AnimatedBuilder( 136 | animation: _animationController, 137 | builder: _buildAnimation, 138 | ); 139 | } 140 | 141 | late double _currentValue; 142 | late double _currentPos; 143 | 144 | bool _lockScroll = false; 145 | 146 | Future _move(double position, {int? nextIndex}) async { 147 | if (_lockScroll) return; 148 | try { 149 | _lockScroll = true; 150 | await _animationController.animateTo( 151 | position, 152 | duration: Duration(milliseconds: widget.duration!), 153 | curve: widget.curve, 154 | ); 155 | if (nextIndex != null) { 156 | widget.onIndexChanged!(widget.getCorrectIndex(nextIndex)); 157 | } 158 | } catch (e) { 159 | debugPrint('error animating _animationController ${e.toString()}'); 160 | } finally { 161 | if (nextIndex != null) { 162 | try { 163 | _animationController.value = 0.5; 164 | } catch (e) { 165 | debugPrint( 166 | 'error setting _animationController.value ${e.toString()}'); 167 | } 168 | 169 | _currentIndex = nextIndex; 170 | } 171 | _lockScroll = false; 172 | } 173 | } 174 | 175 | int _getProperNewIndex(int newIndex) { 176 | var res = newIndex; 177 | if (!widget.loop && newIndex >= widget.itemCount - 1) { 178 | res = widget.itemCount - 1; 179 | } else if (!widget.loop && newIndex < 0) { 180 | res = 0; 181 | } 182 | return res; 183 | } 184 | 185 | void _onController() { 186 | SwiperController controller = widget.controller; 187 | final event = controller.event; 188 | if (event is StepBasedIndexControllerEvent) { 189 | final newIndex = event.calcNextIndex( 190 | currentIndex: _currentIndex, 191 | itemCount: widget.itemCount, 192 | loop: widget.loop, 193 | reverse: false, 194 | ); 195 | _move(event.targetPosition, nextIndex: newIndex); 196 | } else if (event is MoveIndexControllerEvent) { 197 | _move( 198 | event.targetPosition, 199 | nextIndex: _getProperNewIndex(event.newIndex), 200 | ); 201 | } 202 | } 203 | 204 | void _onPanEnd(DragEndDetails details) { 205 | if (_lockScroll) return; 206 | 207 | double velocity = widget.scrollDirection == Axis.horizontal 208 | ? details.velocity.pixelsPerSecond.dx 209 | : details.velocity.pixelsPerSecond.dy; 210 | 211 | if (_animationController.value >= 0.75 || velocity > 500.0) { 212 | if (_currentIndex <= 0 && !widget.loop) { 213 | return; 214 | } 215 | _move(1.0, nextIndex: _currentIndex - 1); 216 | } else if (_animationController.value < 0.25 || velocity < -500.0) { 217 | if (_currentIndex >= widget.itemCount - 1 && !widget.loop) { 218 | return; 219 | } 220 | _move(0.0, nextIndex: _currentIndex + 1); 221 | } else { 222 | _move(0.5); 223 | } 224 | } 225 | 226 | void _onPanStart(DragStartDetails details) { 227 | if (_lockScroll) return; 228 | _currentValue = _animationController.value; 229 | _currentPos = widget.scrollDirection == Axis.horizontal 230 | ? details.globalPosition.dx 231 | : details.globalPosition.dy; 232 | } 233 | 234 | void _onPanUpdate(DragUpdateDetails details) { 235 | if (_lockScroll) return; 236 | double value = _currentValue + 237 | ((widget.scrollDirection == Axis.horizontal 238 | ? details.globalPosition.dx 239 | : details.globalPosition.dy) - 240 | _currentPos) / 241 | _swiperWidth / 242 | 2; 243 | // no loop ? 244 | if (!widget.loop) { 245 | if (_currentIndex >= widget.itemCount - 1) { 246 | if (value < 0.5) { 247 | value = 0.5; 248 | } 249 | } else if (_currentIndex <= 0) { 250 | if (value > 0.5) { 251 | value = 0.5; 252 | } 253 | } 254 | } 255 | 256 | _animationController.value = value; 257 | } 258 | } 259 | 260 | double _getValue(List values, double animationValue, int index) { 261 | double s = values[index]; 262 | if (animationValue >= 0.5) { 263 | if (index < values.length - 1) { 264 | s = s + (values[index + 1] - s) * (animationValue - 0.5) * 2.0; 265 | } 266 | } else { 267 | if (index != 0) { 268 | s = s - (s - values[index - 1]) * (0.5 - animationValue) * 2.0; 269 | } 270 | } 271 | return s; 272 | } 273 | 274 | Offset _getOffsetValue(List values, double animationValue, int index) { 275 | Offset s = values[index]; 276 | double dx = s.dx; 277 | double dy = s.dy; 278 | if (animationValue >= 0.5) { 279 | if (index < values.length - 1) { 280 | dx = dx + (values[index + 1].dx - dx) * (animationValue - 0.5) * 2.0; 281 | dy = dy + (values[index + 1].dy - dy) * (animationValue - 0.5) * 2.0; 282 | } 283 | } else { 284 | if (index != 0) { 285 | dx = dx - (dx - values[index - 1].dx) * (0.5 - animationValue) * 2.0; 286 | dy = dy - (dy - values[index - 1].dy) * (0.5 - animationValue) * 2.0; 287 | } 288 | } 289 | return Offset(dx, dy); 290 | } 291 | 292 | abstract class TransformBuilder { 293 | final List values; 294 | 295 | TransformBuilder({required this.values}); 296 | 297 | Widget build(int i, double animationValue, Widget widget); 298 | } 299 | 300 | class ScaleTransformBuilder extends TransformBuilder { 301 | final Alignment alignment; 302 | 303 | ScaleTransformBuilder({ 304 | required List values, 305 | this.alignment = Alignment.center, 306 | }) : super(values: values); 307 | 308 | @override 309 | Widget build(int i, double animationValue, Widget widget) { 310 | double s = _getValue(values, animationValue, i); 311 | return Transform.scale(scale: s, child: widget); 312 | } 313 | } 314 | 315 | class OpacityTransformBuilder extends TransformBuilder { 316 | OpacityTransformBuilder({required List values}) 317 | : super(values: values); 318 | 319 | @override 320 | Widget build(int i, double animationValue, Widget widget) { 321 | double v = _getValue(values, animationValue, i); 322 | return Opacity( 323 | opacity: v, 324 | child: widget, 325 | ); 326 | } 327 | } 328 | 329 | class RotateTransformBuilder extends TransformBuilder { 330 | RotateTransformBuilder({required List values}) 331 | : super(values: values); 332 | 333 | @override 334 | Widget build(int i, double animationValue, Widget widget) { 335 | double v = _getValue(values, animationValue, i); 336 | return Transform.rotate( 337 | angle: v, 338 | child: widget, 339 | ); 340 | } 341 | } 342 | 343 | class TranslateTransformBuilder extends TransformBuilder { 344 | TranslateTransformBuilder({required List values}) 345 | : super(values: values); 346 | 347 | @override 348 | Widget build(int i, double animationValue, Widget widget) { 349 | Offset s = _getOffsetValue(values, animationValue, i); 350 | return Transform.translate( 351 | offset: s, 352 | child: widget, 353 | ); 354 | } 355 | } 356 | 357 | class CustomLayoutOption { 358 | final List builders = []; 359 | final int startIndex; 360 | final int? stateCount; 361 | 362 | CustomLayoutOption({this.stateCount, required this.startIndex}); 363 | 364 | void addOpacity(List values) { 365 | builders.add(OpacityTransformBuilder(values: values)); 366 | } 367 | 368 | void addTranslate(List values) { 369 | builders.add(TranslateTransformBuilder(values: values)); 370 | } 371 | 372 | void addScale(List values, Alignment alignment) { 373 | builders.add(ScaleTransformBuilder(values: values, alignment: alignment)); 374 | } 375 | 376 | void addRotate(List values) { 377 | builders.add(RotateTransformBuilder(values: values)); 378 | } 379 | } 380 | 381 | class _CustomLayoutSwiper extends _SubSwiper { 382 | final CustomLayoutOption option; 383 | 384 | const _CustomLayoutSwiper({ 385 | required this.option, 386 | double? itemWidth, 387 | required bool loop, 388 | double? itemHeight, 389 | ValueChanged? onIndexChanged, 390 | Key? key, 391 | IndexedWidgetBuilder? itemBuilder, 392 | required Curve curve, 393 | int? duration, 394 | int? index, 395 | required int itemCount, 396 | Axis? scrollDirection, 397 | required SwiperController controller, 398 | }) : super( 399 | loop: loop, 400 | onIndexChanged: onIndexChanged, 401 | itemWidth: itemWidth, 402 | itemHeight: itemHeight, 403 | key: key, 404 | itemBuilder: itemBuilder, 405 | curve: curve, 406 | duration: duration, 407 | index: index, 408 | itemCount: itemCount, 409 | controller: controller, 410 | scrollDirection: scrollDirection); 411 | 412 | @override 413 | State createState() { 414 | return _CustomLayoutState(); 415 | } 416 | } 417 | 418 | class _CustomLayoutState extends _CustomLayoutStateBase<_CustomLayoutSwiper> { 419 | @override 420 | void didChangeDependencies() { 421 | super.didChangeDependencies(); 422 | _startIndex = widget.option.startIndex; 423 | _animationCount = widget.option.stateCount; 424 | } 425 | 426 | @override 427 | void didUpdateWidget(_CustomLayoutSwiper oldWidget) { 428 | _startIndex = widget.option.startIndex; 429 | _animationCount = widget.option.stateCount; 430 | super.didUpdateWidget(oldWidget); 431 | } 432 | 433 | @override 434 | Widget _buildItem(int index, int realIndex, double animationValue) { 435 | List builders = widget.option.builders; 436 | 437 | Widget child = SizedBox( 438 | width: widget.itemWidth ?? double.infinity, 439 | height: widget.itemHeight ?? double.infinity, 440 | child: widget.itemBuilder!(context, realIndex)); 441 | 442 | for (int i = builders.length - 1; i >= 0; --i) { 443 | TransformBuilder builder = builders[i]; 444 | child = builder.build(index, animationValue, child); 445 | } 446 | 447 | return child; 448 | } 449 | } 450 | -------------------------------------------------------------------------------- /lib/src/flutter_page_indicator.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: constant_identifier_names 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 5 | 6 | class WarmPainter extends BasePainter { 7 | WarmPainter(PageIndicator widget, double page, int index, Paint paint) 8 | : super(widget, page, index, paint); 9 | 10 | @override 11 | void draw(Canvas canvas, double space, double size, double radius) { 12 | double progress = page - index; 13 | double distance = size + space; 14 | double start = index * (size + space); 15 | 16 | if (progress > 0.5) { 17 | double right = start + size + distance; 18 | //progress=>0.5-1.0 19 | //left:0.0=>distance 20 | 21 | double left = index * distance + distance * (progress - 0.5) * 2; 22 | canvas.drawRRect( 23 | RRect.fromLTRBR(left, 0.0, right, size, Radius.circular(radius)), 24 | _paint); 25 | } else { 26 | double right = start + size + distance * progress * 2; 27 | 28 | canvas.drawRRect( 29 | RRect.fromLTRBR(start, 0.0, right, size, Radius.circular(radius)), 30 | _paint); 31 | } 32 | } 33 | } 34 | 35 | class DropPainter extends BasePainter { 36 | DropPainter(PageIndicator widget, double page, int index, Paint paint) 37 | : super(widget, page, index, paint); 38 | 39 | @override 40 | void draw(Canvas canvas, double space, double size, double radius) { 41 | double progress = page - index; 42 | double dropHeight = widget.dropHeight; 43 | double rate = (0.5 - progress).abs() * 2; 44 | double scale = widget.scale; 45 | 46 | //lerp(begin, end, progress) 47 | 48 | canvas.drawCircle( 49 | Offset(radius + ((page) * (size + space)), 50 | radius - dropHeight * (1 - rate)), 51 | radius * (scale + rate * (1.0 - scale)), 52 | _paint); 53 | } 54 | } 55 | 56 | class NonePainter extends BasePainter { 57 | NonePainter(PageIndicator widget, double page, int index, Paint paint) 58 | : super(widget, page, index, paint); 59 | 60 | @override 61 | void draw(Canvas canvas, double space, double size, double radius) { 62 | double progress = page - index; 63 | double secondOffset = index == widget.count - 1 64 | ? radius 65 | : radius + ((index + 1) * (size + space)); 66 | 67 | if (progress > 0.5) { 68 | canvas.drawCircle(Offset(secondOffset, radius), radius, _paint); 69 | } else { 70 | canvas.drawCircle( 71 | Offset(radius + (index * (size + space)), radius), radius, _paint); 72 | } 73 | } 74 | } 75 | 76 | class SlidePainter extends BasePainter { 77 | SlidePainter(PageIndicator widget, double page, int index, Paint paint) 78 | : super(widget, page, index, paint); 79 | 80 | @override 81 | void draw(Canvas canvas, double space, double size, double radius) { 82 | canvas.drawCircle( 83 | Offset(radius + (page * (size + space)), radius), radius, _paint); 84 | } 85 | } 86 | 87 | class ScalePainter extends BasePainter { 88 | ScalePainter(PageIndicator widget, double page, int index, Paint paint) 89 | : super(widget, page, index, paint); 90 | 91 | // 连续的两个点,含有最后一个和第一个 92 | @override 93 | bool _shouldSkip(int i) { 94 | if (index == widget.count - 1) { 95 | return i == 0 || i == index; 96 | } 97 | return (i == index || i == index + 1); 98 | } 99 | 100 | @override 101 | void paint(Canvas canvas, Size size) { 102 | _paint.color = widget.color; 103 | double space = widget.space; 104 | double size = widget.size; 105 | double radius = size / 2; 106 | int c = widget.count; 107 | for (int i = 0; i < c; ++i) { 108 | if (_shouldSkip(i)) { 109 | continue; 110 | } 111 | canvas.drawCircle(Offset(i * (size + space) + radius, radius), 112 | radius * widget.scale, _paint); 113 | } 114 | 115 | _paint.color = widget.activeColor; 116 | draw(canvas, space, size, radius); 117 | } 118 | 119 | @override 120 | void draw(Canvas canvas, double space, double size, double radius) { 121 | double secondOffset = index == widget.count - 1 122 | ? radius 123 | : radius + ((index + 1) * (size + space)); 124 | 125 | double progress = page - index; 126 | _paint.color = Color.lerp(widget.activeColor, widget.color, progress)!; 127 | //last 128 | canvas.drawCircle(Offset(radius + (index * (size + space)), radius), 129 | lerp(radius, radius * widget.scale, progress), _paint); 130 | //first 131 | _paint.color = Color.lerp(widget.color, widget.activeColor, progress)!; 132 | canvas.drawCircle(Offset(secondOffset, radius), 133 | lerp(radius * widget.scale, radius, progress), _paint); 134 | } 135 | } 136 | 137 | class ColorPainter extends BasePainter { 138 | ColorPainter(PageIndicator widget, double page, int index, Paint paint) 139 | : super(widget, page, index, paint); 140 | 141 | // 连续的两个点,含有最后一个和第一个 142 | @override 143 | bool _shouldSkip(int i) { 144 | if (index == widget.count - 1) { 145 | return i == 0 || i == index; 146 | } 147 | return (i == index || i == index + 1); 148 | } 149 | 150 | @override 151 | void draw(Canvas canvas, double space, double size, double radius) { 152 | double progress = page - index; 153 | double secondOffset = index == widget.count - 1 154 | ? radius 155 | : radius + ((index + 1) * (size + space)); 156 | 157 | _paint.color = Color.lerp(widget.activeColor, widget.color, progress)!; 158 | //left 159 | canvas.drawCircle( 160 | Offset(radius + (index * (size + space)), radius), radius, _paint); 161 | //right 162 | _paint.color = Color.lerp(widget.color, widget.activeColor, progress)!; 163 | canvas.drawCircle(Offset(secondOffset, radius), radius, _paint); 164 | } 165 | } 166 | 167 | abstract class BasePainter extends CustomPainter { 168 | final PageIndicator widget; 169 | final double page; 170 | final int index; 171 | final Paint _paint; 172 | 173 | double lerp(double begin, double end, double progress) { 174 | return begin + (end - begin) * progress; 175 | } 176 | 177 | BasePainter(this.widget, this.page, this.index, this._paint); 178 | 179 | void draw(Canvas canvas, double space, double size, double radius); 180 | 181 | bool _shouldSkip(int i) { 182 | return false; 183 | } 184 | //double secondOffset = index == widget.count-1 ? radius : radius + ((index + 1) * (size + space)); 185 | 186 | @override 187 | void paint(Canvas canvas, Size size) { 188 | _paint.color = widget.color; 189 | double space = widget.space; 190 | double size = widget.size; 191 | double radius = size / 2; 192 | int c = widget.count; 193 | for (int i = 0; i < c; ++i) { 194 | if (_shouldSkip(i)) { 195 | continue; 196 | } 197 | canvas.drawCircle( 198 | Offset(i * (size + space) + radius, radius), radius, _paint); 199 | } 200 | 201 | double page = this.page; 202 | if (page < index) { 203 | page = 0.0; 204 | } 205 | _paint.color = widget.activeColor; 206 | draw(canvas, space, size, radius); 207 | } 208 | 209 | @override 210 | bool shouldRepaint(BasePainter oldDelegate) { 211 | return oldDelegate.page != page; 212 | } 213 | } 214 | 215 | class _PageIndicatorState extends State { 216 | int index = 0; 217 | double page = 0; 218 | final Paint _paint = Paint(); 219 | 220 | BasePainter _createPainter() { 221 | switch (widget.layout) { 222 | case PageIndicatorLayout.NONE: 223 | return NonePainter(widget, page, index, _paint); 224 | case PageIndicatorLayout.SLIDE: 225 | return SlidePainter(widget, page, index, _paint); 226 | case PageIndicatorLayout.WARM: 227 | return WarmPainter(widget, page, index, _paint); 228 | case PageIndicatorLayout.COLOR: 229 | return ColorPainter(widget, page, index, _paint); 230 | case PageIndicatorLayout.SCALE: 231 | return ScalePainter(widget, page, index, _paint); 232 | case PageIndicatorLayout.DROP: 233 | return DropPainter(widget, page, index, _paint); 234 | default: 235 | throw Exception("Not a valid layout"); 236 | } 237 | } 238 | 239 | @override 240 | Widget build(BuildContext context) { 241 | Widget child = SizedBox( 242 | width: widget.count * widget.size + (widget.count - 1) * widget.space, 243 | height: widget.size, 244 | child: CustomPaint( 245 | painter: _createPainter(), 246 | ), 247 | ); 248 | 249 | if (widget.layout == PageIndicatorLayout.SCALE || 250 | widget.layout == PageIndicatorLayout.COLOR) { 251 | child = ClipRect( 252 | child: child, 253 | ); 254 | } 255 | 256 | return IgnorePointer( 257 | child: child, 258 | ); 259 | } 260 | 261 | void _setInitialPage() { 262 | // use the initial page index but cut off 263 | // the offset specified when looping (kMiddleValue) 264 | index = widget.controller.initialPage % kMiddleValue; 265 | page = index.toDouble(); 266 | } 267 | 268 | void _onController() { 269 | if (!widget.controller.hasClients) return; 270 | page = widget.controller.page ?? 0.0; 271 | index = page.floor(); 272 | 273 | setState(() {}); 274 | } 275 | 276 | @override 277 | void initState() { 278 | super.initState(); 279 | widget.controller.addListener(_onController); 280 | _setInitialPage(); 281 | } 282 | 283 | @override 284 | void didUpdateWidget(PageIndicator oldWidget) { 285 | super.didUpdateWidget(oldWidget); 286 | if (widget.controller != oldWidget.controller) { 287 | oldWidget.controller.removeListener(_onController); 288 | widget.controller.addListener(_onController); 289 | } 290 | } 291 | 292 | @override 293 | void dispose() { 294 | widget.controller.removeListener(_onController); 295 | super.dispose(); 296 | } 297 | } 298 | 299 | enum PageIndicatorLayout { 300 | NONE, 301 | SLIDE, 302 | WARM, 303 | COLOR, 304 | SCALE, 305 | DROP, 306 | } 307 | 308 | class PageIndicator extends StatefulWidget { 309 | /// size of the dots 310 | final double size; 311 | 312 | /// space between dots. 313 | final double space; 314 | 315 | /// count of dots 316 | final int count; 317 | 318 | /// active color 319 | final Color activeColor; 320 | 321 | /// normal color 322 | final Color color; 323 | 324 | /// layout of the dots,default is [PageIndicatorLayout.SLIDE] 325 | final PageIndicatorLayout? layout; 326 | 327 | // Only valid when layout==PageIndicatorLayout.scale 328 | final double scale; 329 | 330 | // Only valid when layout==PageIndicatorLayout.drop 331 | final double dropHeight; 332 | 333 | final PageController controller; 334 | 335 | final double activeSize; 336 | 337 | const PageIndicator({ 338 | Key? key, 339 | this.size = 20.0, 340 | this.space = 5.0, 341 | required this.count, 342 | this.activeSize = 20.0, 343 | required this.controller, 344 | this.color = Colors.white30, 345 | this.layout = PageIndicatorLayout.SLIDE, 346 | this.activeColor = Colors.white, 347 | this.scale = 0.6, 348 | this.dropHeight = 20.0, 349 | }) : super(key: key); 350 | 351 | @override 352 | State createState() { 353 | return _PageIndicatorState(); 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /lib/src/index_controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | 6 | abstract class IndexControllerEventBase { 7 | final bool animation; 8 | 9 | final completer = Completer(); 10 | Future get future => completer.future; 11 | void complete() { 12 | if (!completer.isCompleted) { 13 | completer.complete(); 14 | } 15 | } 16 | 17 | IndexControllerEventBase({ 18 | required this.animation, 19 | }); 20 | } 21 | 22 | mixin TargetedPositionControllerEvent on IndexControllerEventBase { 23 | double get targetPosition; 24 | } 25 | mixin StepBasedIndexControllerEvent on TargetedPositionControllerEvent { 26 | int get step; 27 | int calcNextIndex({ 28 | required int currentIndex, 29 | required int itemCount, 30 | required bool loop, 31 | required bool reverse, 32 | }) { 33 | var cIndex = currentIndex; 34 | if (reverse) { 35 | cIndex -= step; 36 | } else { 37 | cIndex += step; 38 | } 39 | 40 | if (!loop) { 41 | if (cIndex >= itemCount) { 42 | cIndex = 0; 43 | } else if (cIndex < 0) { 44 | cIndex = itemCount - 1; 45 | } 46 | } 47 | return cIndex; 48 | } 49 | } 50 | 51 | class NextIndexControllerEvent extends IndexControllerEventBase 52 | with TargetedPositionControllerEvent, StepBasedIndexControllerEvent { 53 | NextIndexControllerEvent({ 54 | required bool animation, 55 | }) : super( 56 | animation: animation, 57 | ); 58 | 59 | @override 60 | int get step => 1; 61 | 62 | @override 63 | double get targetPosition => 1; 64 | } 65 | 66 | class PrevIndexControllerEvent extends IndexControllerEventBase 67 | with TargetedPositionControllerEvent, StepBasedIndexControllerEvent { 68 | PrevIndexControllerEvent({ 69 | required bool animation, 70 | }) : super( 71 | animation: animation, 72 | ); 73 | @override 74 | int get step => -1; 75 | 76 | @override 77 | double get targetPosition => 0; 78 | } 79 | 80 | class MoveIndexControllerEvent extends IndexControllerEventBase 81 | with TargetedPositionControllerEvent { 82 | final int newIndex; 83 | final int oldIndex; 84 | MoveIndexControllerEvent({ 85 | required this.newIndex, 86 | required this.oldIndex, 87 | required bool animation, 88 | }) : super( 89 | animation: animation, 90 | ); 91 | @override 92 | double get targetPosition => newIndex > oldIndex ? 1 : 0; 93 | } 94 | 95 | class IndexController extends ChangeNotifier { 96 | IndexControllerEventBase? event; 97 | int index = 0; 98 | Future move(int index, {bool animation = true}) { 99 | final e = event = MoveIndexControllerEvent( 100 | animation: animation, 101 | newIndex: index, 102 | oldIndex: this.index, 103 | ); 104 | notifyListeners(); 105 | return e.future; 106 | } 107 | 108 | Future next({bool animation = true}) { 109 | final e = event = NextIndexControllerEvent(animation: animation); 110 | notifyListeners(); 111 | return e.future; 112 | } 113 | 114 | Future previous({bool animation = true}) { 115 | final e = event = PrevIndexControllerEvent(animation: animation); 116 | notifyListeners(); 117 | return e.future; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /lib/src/parallax.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import 'transformer_page_view.dart'; 4 | 5 | typedef PaintCallback = void Function(Canvas canvas, Size size); 6 | 7 | class ColorPainter extends CustomPainter { 8 | final Paint _paint; 9 | final TransformInfo info; 10 | final List colors; 11 | 12 | ColorPainter(this._paint, this.info, this.colors); 13 | 14 | @override 15 | void paint(Canvas canvas, Size size) { 16 | int index = info.fromIndex; 17 | _paint.color = colors[index]; 18 | canvas.drawRect(Rect.fromLTWH(0.0, 0.0, size.width, size.height), _paint); 19 | if (info.done!) { 20 | return; 21 | } 22 | int alpha; 23 | int color; 24 | double opacity; 25 | double? position = info.position; 26 | if (info.forward!) { 27 | if (index < colors.length - 1) { 28 | color = colors[index + 1].value & 0x00ffffff; 29 | opacity = (position! <= 0 30 | ? (-position / info.viewportFraction!) 31 | : 1 - position / info.viewportFraction!); 32 | if (opacity > 1) { 33 | opacity -= 1.0; 34 | } 35 | if (opacity < 0) { 36 | opacity += 1.0; 37 | } 38 | alpha = (0xff * opacity).toInt(); 39 | 40 | _paint.color = Color((alpha << 24) | color); 41 | canvas.drawRect( 42 | Rect.fromLTWH(0.0, 0.0, size.width, size.height), _paint); 43 | } 44 | } else { 45 | if (index > 0) { 46 | color = colors[index - 1].value & 0x00ffffff; 47 | opacity = (position! > 0 48 | ? position / info.viewportFraction! 49 | : (1 + position / info.viewportFraction!)); 50 | if (opacity > 1) { 51 | opacity -= 1.0; 52 | } 53 | if (opacity < 0) { 54 | opacity += 1.0; 55 | } 56 | alpha = (0xff * opacity).toInt(); 57 | 58 | _paint.color = Color((alpha << 24) | color); 59 | canvas.drawRect( 60 | Rect.fromLTWH(0.0, 0.0, size.width, size.height), _paint); 61 | } 62 | } 63 | } 64 | 65 | @override 66 | bool shouldRepaint(ColorPainter oldDelegate) { 67 | return oldDelegate.info != info; 68 | } 69 | } 70 | 71 | class _ParallaxColorState extends State { 72 | Paint paint = Paint(); 73 | 74 | @override 75 | Widget build(BuildContext context) { 76 | return CustomPaint( 77 | painter: ColorPainter(paint, widget.info, widget.colors), 78 | child: widget.child, 79 | ); 80 | } 81 | } 82 | 83 | class ParallaxColor extends StatefulWidget { 84 | final Widget child; 85 | 86 | final List colors; 87 | 88 | final TransformInfo info; 89 | 90 | const ParallaxColor({ 91 | Key? key, 92 | required this.colors, 93 | required this.info, 94 | required this.child, 95 | }) : super(key: key); 96 | 97 | @override 98 | State createState() { 99 | return _ParallaxColorState(); 100 | } 101 | } 102 | 103 | class ParallaxContainer extends StatelessWidget { 104 | final Widget child; 105 | final double position; 106 | final double translationFactor; 107 | final double opacityFactor; 108 | 109 | const ParallaxContainer({ 110 | Key? key, 111 | required this.child, 112 | required this.position, 113 | this.translationFactor = 100.0, 114 | this.opacityFactor = 1.0, 115 | }) : super(key: key); 116 | 117 | @override 118 | Widget build(BuildContext context) { 119 | return Opacity( 120 | opacity: (1 - position.abs()).clamp(0.0, 1.0) * opacityFactor, 121 | child: Transform.translate( 122 | offset: Offset(position * translationFactor, 0.0), 123 | child: child, 124 | ), 125 | ); 126 | } 127 | } 128 | 129 | class ParallaxImage extends StatelessWidget { 130 | final Image image; 131 | final double imageFactor; 132 | 133 | ParallaxImage.asset( 134 | String name, { 135 | Key? key, 136 | required double position, 137 | this.imageFactor = 0.3, 138 | }) : image = Image.asset( 139 | name, 140 | fit: BoxFit.cover, 141 | alignment: FractionalOffset( 142 | 0.5 + position * imageFactor, 143 | 0.5, 144 | ), 145 | ), 146 | super(key: key); 147 | 148 | @override 149 | Widget build(BuildContext context) { 150 | return image; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/src/swiper_control.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 3 | 4 | class SwiperControl extends SwiperPlugin { 5 | ///IconData for previous 6 | final IconData iconPrevious; 7 | 8 | ///iconData fopr next 9 | final IconData iconNext; 10 | 11 | ///icon size 12 | final double size; 13 | 14 | ///Icon normal color, The theme's [ThemeData.primaryColor] by default. 15 | final Color? color; 16 | 17 | ///if set loop=false on Swiper, this color will be used when swiper goto the last slide. 18 | ///The theme's [ThemeData.disabledColor] by default. 19 | final Color? disableColor; 20 | 21 | final EdgeInsetsGeometry padding; 22 | 23 | final Key? key; 24 | 25 | const SwiperControl({ 26 | this.iconPrevious = Icons.arrow_back_ios, 27 | this.iconNext = Icons.arrow_forward_ios, 28 | this.color, 29 | this.disableColor, 30 | this.key, 31 | this.size = 30.0, 32 | this.padding = const EdgeInsets.all(5.0), 33 | }); 34 | 35 | Widget buildButton({ 36 | required SwiperPluginConfig? config, 37 | required Color color, 38 | required IconData iconDaga, 39 | required int quarterTurns, 40 | required bool previous, 41 | }) { 42 | return GestureDetector( 43 | behavior: HitTestBehavior.opaque, 44 | onTap: () { 45 | if (previous) { 46 | config!.controller.previous(animation: true); 47 | } else { 48 | config!.controller.next(animation: true); 49 | } 50 | }, 51 | child: Padding( 52 | padding: padding, 53 | child: RotatedBox( 54 | quarterTurns: quarterTurns, 55 | child: Icon( 56 | iconDaga, 57 | semanticLabel: previous ? "Previous" : "Next", 58 | size: size, 59 | color: color, 60 | ))), 61 | ); 62 | } 63 | 64 | @override 65 | Widget build(BuildContext context, SwiperPluginConfig config) { 66 | ThemeData themeData = Theme.of(context); 67 | 68 | Color color = this.color ?? themeData.primaryColor; 69 | Color disableColor = this.disableColor ?? themeData.disabledColor; 70 | Color prevColor; 71 | Color nextColor; 72 | 73 | if (config.loop) { 74 | prevColor = nextColor = color; 75 | } else { 76 | bool next = config.activeIndex < config.itemCount - 1; 77 | bool prev = config.activeIndex > 0; 78 | prevColor = prev ? color : disableColor; 79 | nextColor = next ? color : disableColor; 80 | } 81 | 82 | Widget child; 83 | if (config.scrollDirection == Axis.horizontal) { 84 | child = Row( 85 | key: key, 86 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 87 | children: [ 88 | buildButton( 89 | config: config, 90 | color: prevColor, 91 | iconDaga: iconPrevious, 92 | quarterTurns: 0, 93 | previous: true, 94 | ), 95 | buildButton( 96 | config: config, 97 | color: nextColor, 98 | iconDaga: iconNext, 99 | quarterTurns: 0, 100 | previous: false, 101 | ) 102 | ], 103 | ); 104 | } else { 105 | child = Column( 106 | key: key, 107 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 108 | children: [ 109 | buildButton( 110 | config: config, 111 | color: prevColor, 112 | iconDaga: iconPrevious, 113 | quarterTurns: -3, 114 | previous: true, 115 | ), 116 | buildButton( 117 | config: config, 118 | color: nextColor, 119 | iconDaga: iconNext, 120 | quarterTurns: -3, 121 | previous: false, 122 | ) 123 | ], 124 | ); 125 | } 126 | 127 | return SizedBox( 128 | height: double.infinity, 129 | width: double.infinity, 130 | child: child, 131 | ); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /lib/src/swiper_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 2 | 3 | class SwipeIndexControllerEvent extends IndexControllerEventBase { 4 | SwipeIndexControllerEvent({ 5 | required this.pos, 6 | required bool animation, 7 | }) : super(animation: animation); 8 | final double pos; 9 | } 10 | 11 | class BuildIndexControllerEvent extends IndexControllerEventBase { 12 | BuildIndexControllerEvent({ 13 | required bool animation, 14 | required this.config, 15 | }) : super(animation: animation); 16 | final SwiperPluginConfig config; 17 | } 18 | 19 | class AutoPlaySwiperControllerEvent extends IndexControllerEventBase { 20 | AutoPlaySwiperControllerEvent({ 21 | required bool animation, 22 | required this.autoplay, 23 | }) : super(animation: animation); 24 | 25 | AutoPlaySwiperControllerEvent.start({ 26 | required bool animation, 27 | }) : this(animation: animation, autoplay: true); 28 | AutoPlaySwiperControllerEvent.stop({ 29 | required bool animation, 30 | }) : this(animation: animation, autoplay: false); 31 | final bool autoplay; 32 | } 33 | 34 | class SwiperController extends IndexController { 35 | void startAutoplay({bool animation = true}) { 36 | event = AutoPlaySwiperControllerEvent.start(animation: animation); 37 | notifyListeners(); 38 | } 39 | 40 | void stopAutoplay({bool animation = true}) { 41 | event = AutoPlaySwiperControllerEvent.stop(animation: animation); 42 | notifyListeners(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/swiper_pagination.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 3 | 4 | class FractionPaginationBuilder extends SwiperPlugin { 5 | ///color ,if set null , will be Theme.of(context).scaffoldBackgroundColor 6 | final Color? color; 7 | 8 | ///color when active,if set null , will be Theme.of(context).primaryColor 9 | final Color? activeColor; 10 | 11 | ////font size 12 | final double fontSize; 13 | 14 | ///font size when active 15 | final double activeFontSize; 16 | 17 | final Key? key; 18 | 19 | const FractionPaginationBuilder({ 20 | this.color, 21 | this.fontSize = 20.0, 22 | this.key, 23 | this.activeColor, 24 | this.activeFontSize = 35.0, 25 | }); 26 | 27 | @override 28 | Widget build(BuildContext context, SwiperPluginConfig config) { 29 | int itemCount = config.itemCount; 30 | if (itemCount <= 1) { 31 | return Container(); 32 | } 33 | 34 | ThemeData themeData = Theme.of(context); 35 | Color activeColor = this.activeColor ?? themeData.primaryColor; 36 | Color color = this.color ?? themeData.scaffoldBackgroundColor; 37 | 38 | if (Axis.vertical == config.scrollDirection) { 39 | return Column( 40 | key: key, 41 | mainAxisSize: MainAxisSize.min, 42 | children: [ 43 | Text( 44 | "${config.activeIndex + 1}", 45 | style: TextStyle(color: activeColor, fontSize: activeFontSize), 46 | ), 47 | Text( 48 | "/", 49 | style: TextStyle(color: color, fontSize: fontSize), 50 | ), 51 | Text( 52 | "$itemCount", 53 | style: TextStyle(color: color, fontSize: fontSize), 54 | ) 55 | ], 56 | ); 57 | } else { 58 | return Row( 59 | key: key, 60 | mainAxisSize: MainAxisSize.min, 61 | children: [ 62 | Text( 63 | "${config.activeIndex + 1}", 64 | style: TextStyle(color: activeColor, fontSize: activeFontSize), 65 | ), 66 | Text( 67 | " / $itemCount", 68 | style: TextStyle(color: color, fontSize: fontSize), 69 | ) 70 | ], 71 | ); 72 | } 73 | } 74 | } 75 | 76 | class RectSwiperPaginationBuilder extends SwiperPlugin { 77 | ///color when current index,if set null , will be Theme.of(context).primaryColor 78 | final Color? activeColor; 79 | 80 | ///,if set null , will be Theme.of(context).scaffoldBackgroundColor 81 | final Color? color; 82 | 83 | ///Size of the rect when activate 84 | final Size activeSize; 85 | 86 | ///Size of the rect 87 | final Size size; 88 | 89 | /// Space between rects 90 | final double space; 91 | 92 | final Key? key; 93 | 94 | const RectSwiperPaginationBuilder({ 95 | this.activeColor, 96 | this.color, 97 | this.key, 98 | this.size = const Size(10.0, 3.0), 99 | this.activeSize = const Size(10.0, 3.0), 100 | this.space = 2.0, 101 | }); 102 | 103 | @override 104 | Widget build(BuildContext context, SwiperPluginConfig config) { 105 | int itemCount = config.itemCount; 106 | if (itemCount <= 1) { 107 | return Container(); 108 | } 109 | 110 | ThemeData themeData = Theme.of(context); 111 | Color activeColor = this.activeColor ?? themeData.primaryColor; 112 | Color color = this.color ?? themeData.scaffoldBackgroundColor; 113 | 114 | List list = []; 115 | 116 | int activeIndex = config.activeIndex; 117 | if (itemCount > 20) { 118 | debugPrint( 119 | "The itemCount is too big, we suggest use FractionPaginationBuilder instead of DotSwiperPaginationBuilder in this situation"); 120 | } 121 | 122 | for (int i = 0; i < itemCount; ++i) { 123 | bool active = i == activeIndex; 124 | Size size = active ? activeSize : this.size; 125 | list.add(Container( 126 | width: size.width, 127 | height: size.height, 128 | key: Key("pagination_$i"), 129 | margin: EdgeInsets.all(space), 130 | decoration: BoxDecoration( 131 | borderRadius: BorderRadius.circular(1.5), 132 | color: active ? activeColor : color, 133 | ), 134 | )); 135 | } 136 | 137 | if (config.scrollDirection == Axis.vertical) { 138 | return Column( 139 | key: key, 140 | mainAxisSize: MainAxisSize.min, 141 | children: list, 142 | ); 143 | } else { 144 | return Row( 145 | key: key, 146 | mainAxisSize: MainAxisSize.min, 147 | children: list, 148 | ); 149 | } 150 | } 151 | } 152 | 153 | class DotSwiperPaginationBuilder extends SwiperPlugin { 154 | ///color when current index,if set null , will be Theme.of(context).primaryColor 155 | final Color? activeColor; 156 | 157 | ///,if set null , will be Theme.of(context).scaffoldBackgroundColor 158 | final Color? color; 159 | 160 | ///Size of the dot when activate 161 | final double activeSize; 162 | 163 | ///Size of the dot 164 | final double size; 165 | 166 | /// Space between dots 167 | final double space; 168 | 169 | final Key? key; 170 | 171 | const DotSwiperPaginationBuilder({ 172 | this.activeColor, 173 | this.color, 174 | this.key, 175 | this.size = 10.0, 176 | this.activeSize = 10.0, 177 | this.space = 3.0, 178 | }); 179 | 180 | @override 181 | Widget build(BuildContext context, SwiperPluginConfig config) { 182 | 183 | int itemCount = config.itemCount; 184 | if (itemCount <= 1) { 185 | return Container(); 186 | } 187 | 188 | if (config.itemCount > 20) { 189 | debugPrint( 190 | "The itemCount is too big, we suggest use FractionPaginationBuilder instead of DotSwiperPaginationBuilder in this sitituation"); 191 | } 192 | Color? activeColor = this.activeColor; 193 | Color? color = this.color; 194 | 195 | if (activeColor == null || color == null) { 196 | ThemeData themeData = Theme.of(context); 197 | activeColor = this.activeColor ?? themeData.primaryColor; 198 | color = this.color ?? themeData.scaffoldBackgroundColor; 199 | } 200 | 201 | if (config.indicatorLayout != PageIndicatorLayout.NONE && 202 | config.layout == SwiperLayout.DEFAULT) { 203 | return PageIndicator( 204 | count: config.itemCount, 205 | controller: config.pageController!, 206 | layout: config.indicatorLayout, 207 | size: size, 208 | activeColor: activeColor, 209 | color: color, 210 | space: space, 211 | ); 212 | } 213 | 214 | List list = []; 215 | 216 | int? activeIndex = config.activeIndex; 217 | 218 | for (int i = 0; i < itemCount; ++i) { 219 | bool active = i == activeIndex; 220 | list.add(Container( 221 | key: Key("pagination_$i"), 222 | margin: EdgeInsets.all(space), 223 | child: ClipOval( 224 | child: Container( 225 | color: active ? activeColor : color, 226 | width: active ? activeSize : size, 227 | height: active ? activeSize : size, 228 | ), 229 | ), 230 | )); 231 | } 232 | 233 | if (config.scrollDirection == Axis.vertical) { 234 | return Column( 235 | key: key, 236 | mainAxisSize: MainAxisSize.min, 237 | children: list, 238 | ); 239 | } else { 240 | return Row( 241 | key: key, 242 | mainAxisSize: MainAxisSize.min, 243 | children: list, 244 | ); 245 | } 246 | } 247 | } 248 | 249 | typedef SwiperPaginationBuilder = Widget Function( 250 | BuildContext context, SwiperPluginConfig config); 251 | 252 | class SwiperCustomPagination extends SwiperPlugin { 253 | final SwiperPaginationBuilder builder; 254 | 255 | const SwiperCustomPagination({required this.builder}); 256 | 257 | @override 258 | Widget build(BuildContext context, SwiperPluginConfig config) { 259 | return builder(context, config); 260 | } 261 | } 262 | 263 | class SwiperPagination extends SwiperPlugin { 264 | /// dot style pagination 265 | static const SwiperPlugin dots = DotSwiperPaginationBuilder(); 266 | 267 | /// fraction style pagination 268 | static const SwiperPlugin fraction = FractionPaginationBuilder(); 269 | 270 | /// round rect style pagination 271 | static const SwiperPlugin rect = RectSwiperPaginationBuilder(); 272 | 273 | /// Alignment.bottomCenter by default when scrollDirection== Axis.horizontal 274 | /// Alignment.centerRight by default when scrollDirection== Axis.vertical 275 | final Alignment? alignment; 276 | 277 | /// Distance between pagination and the container 278 | final EdgeInsetsGeometry margin; 279 | 280 | /// Build the widget 281 | final SwiperPlugin builder; 282 | 283 | final Key? key; 284 | 285 | const SwiperPagination({ 286 | this.alignment, 287 | this.key, 288 | this.margin = const EdgeInsets.all(10.0), 289 | this.builder = SwiperPagination.dots, 290 | }); 291 | 292 | @override 293 | Widget build(BuildContext context, SwiperPluginConfig config) { 294 | Alignment defaultAlignment = config.scrollDirection == Axis.horizontal 295 | ? Alignment.bottomCenter 296 | : Alignment.centerRight; 297 | Widget child = Container( 298 | margin: margin, 299 | child: builder.build(context, config), 300 | ); 301 | if (!config.outer!) { 302 | child = Align( 303 | key: key, 304 | alignment: alignment ?? defaultAlignment, 305 | child: child, 306 | ); 307 | } 308 | return child; 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /lib/src/swiper_plugin.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 3 | 4 | /// plugin to display swiper components 5 | /// 6 | abstract class SwiperPlugin { 7 | const SwiperPlugin(); 8 | 9 | Widget build(BuildContext context, SwiperPluginConfig config); 10 | } 11 | 12 | class SwiperPluginConfig { 13 | final Axis scrollDirection; 14 | final AxisDirection? axisDirection; 15 | final SwiperController controller; 16 | final int activeIndex; 17 | final int itemCount; 18 | final PageIndicatorLayout? indicatorLayout; 19 | final bool loop; 20 | final bool? outer; 21 | final PageController? pageController; 22 | final SwiperLayout? layout; 23 | 24 | const SwiperPluginConfig({ 25 | required this.scrollDirection, 26 | required this.controller, 27 | required this.activeIndex, 28 | required this.itemCount, 29 | this.axisDirection, 30 | this.indicatorLayout, 31 | this.outer, 32 | this.pageController, 33 | this.layout, 34 | this.loop = false, 35 | }); 36 | } 37 | 38 | class SwiperPluginView extends StatelessWidget { 39 | final SwiperPlugin plugin; 40 | final SwiperPluginConfig config; 41 | 42 | const SwiperPluginView({ 43 | Key? key, 44 | required this.plugin, 45 | required this.config, 46 | }) : super(key: key); 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | return plugin.build(context, config); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_swiper_view 2 | description: The best swiper(carousel) for flutter, with multiple layouts, infinite loop. Compatible with Android & iOS. 3 | version: 1.1.8 4 | homepage: https://github.com/feicien/flutter_swiper_view 5 | 6 | dependencies: 7 | flutter: 8 | sdk: flutter 9 | 10 | 11 | environment: 12 | sdk: '>=2.12.0 <3.0.0' 13 | flutter: ">=1.17.0" 14 | 15 | dev_dependencies: 16 | flutter_lints: ^2.0.1 17 | flutter_test: 18 | sdk: flutter 19 | 20 | flutter: 21 | -------------------------------------------------------------------------------- /test/control_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | 5 | void main() { 6 | testWidgets('Control horizontal', (WidgetTester tester) async { 7 | SwiperController controller = SwiperController(); 8 | 9 | SwiperPluginConfig config = SwiperPluginConfig( 10 | activeIndex: 0, 11 | controller: controller, 12 | itemCount: 10, 13 | loop: true, 14 | scrollDirection: Axis.horizontal); 15 | 16 | Key key = UniqueKey(); 17 | await tester.pumpWidget(MaterialApp( 18 | home: Scaffold(body: Builder(builder: (BuildContext context) { 19 | return SwiperControl(key: key).build(context, config); 20 | })), 21 | )); 22 | 23 | expect(find.byKey(key), findsOneWidget); 24 | 25 | bool first = true; 26 | 27 | await tester.tap(find.byWidgetPredicate((Widget widget) { 28 | if (widget is GestureDetector && first) { 29 | first = false; 30 | return true; 31 | } 32 | 33 | return false; 34 | })); 35 | }); 36 | 37 | testWidgets('Control vertical', (WidgetTester tester) async { 38 | SwiperController controller = SwiperController(); 39 | 40 | SwiperPluginConfig config = SwiperPluginConfig( 41 | activeIndex: 0, 42 | controller: controller, 43 | itemCount: 10, 44 | loop: true, 45 | scrollDirection: Axis.vertical); 46 | 47 | Key key = UniqueKey(); 48 | await tester.pumpWidget(MaterialApp( 49 | home: Scaffold(body: Builder(builder: (BuildContext context) { 50 | return SwiperControl( 51 | key: key, color: Colors.white, disableColor: Colors.black87) 52 | .build(context, config); 53 | })), 54 | )); 55 | 56 | expect(find.byKey(key), findsOneWidget); 57 | 58 | bool first = true; 59 | 60 | await tester.tap(find.byWidgetPredicate((Widget widget) { 61 | if (widget is GestureDetector && first) { 62 | first = false; 63 | return true; 64 | } 65 | 66 | return false; 67 | })); 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /test/flutter_swiper_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | 5 | void main() { 6 | testWidgets('Default Swiper', (WidgetTester tester) async { 7 | // Build our app and trigger a frame. 8 | await tester.pumpWidget(MaterialApp( 9 | home: Swiper( 10 | itemBuilder: (context, index) { 11 | return const Text("0"); 12 | }, 13 | itemCount: 10))); 14 | 15 | expect(find.text("0", skipOffstage: false), findsOneWidget); 16 | }); 17 | 18 | testWidgets('Default Swiper loop:false', (WidgetTester tester) async { 19 | // Build our app and trigger a frame. 20 | await tester.pumpWidget(MaterialApp( 21 | home: Swiper( 22 | onTap: (int inde) {}, 23 | itemBuilder: (context, index) { 24 | return const Text("0"); 25 | }, 26 | itemCount: 10, 27 | loop: false, 28 | ))); 29 | 30 | expect(find.text("0", skipOffstage: true), findsOneWidget); 31 | }); 32 | 33 | testWidgets('Create Swiper with children', (WidgetTester tester) async { 34 | // Build our app and trigger a frame. 35 | await tester.pumpWidget(MaterialApp( 36 | home: Swiper.children( 37 | children: const [Text("0"), Text("1")], 38 | ))); 39 | 40 | expect(find.text("0", skipOffstage: false), findsOneWidget); 41 | }); 42 | 43 | testWidgets('Create Swiper with list', (WidgetTester tester) async { 44 | // Build our app and trigger a frame. 45 | await tester.pumpWidget(MaterialApp( 46 | home: Swiper.list( 47 | list: const ["0", "1"], 48 | builder: (BuildContext context, dynamic data, int index) { 49 | return Text(data); 50 | }, 51 | ))); 52 | 53 | expect(find.text("0", skipOffstage: false), findsOneWidget); 54 | }); 55 | 56 | testWidgets('Swiper with default plugins', (WidgetTester tester) async { 57 | // Build our app and trigger a frame. 58 | SwiperController controller = SwiperController(); 59 | await tester.pumpWidget(MaterialApp( 60 | home: Swiper( 61 | controller: controller, 62 | itemBuilder: (context, index) { 63 | return const Text("0"); 64 | }, 65 | itemCount: 10, 66 | pagination: const SwiperPagination(), 67 | control: const SwiperControl(), 68 | ))); 69 | 70 | expect(find.text("0", skipOffstage: false), findsOneWidget); 71 | }); 72 | 73 | const List titles = [ 74 | "Flutter Swiper is awosome", 75 | "Really nice", 76 | "Yeap" 77 | ]; 78 | 79 | testWidgets('Customize pagination', (WidgetTester tester) async { 80 | // Build our app and trigger a frame. 81 | SwiperController controller = SwiperController(); 82 | await tester.pumpWidget(MaterialApp( 83 | home: Swiper( 84 | controller: controller, 85 | itemBuilder: (context, index) { 86 | return const Text("0"); 87 | }, 88 | itemCount: 10, 89 | pagination: SwiperCustomPagination( 90 | builder: (BuildContext context, SwiperPluginConfig config) { 91 | return ConstrainedBox( 92 | constraints: const BoxConstraints.expand(height: 50.0), 93 | child: Row( 94 | children: [ 95 | Text( 96 | "${titles[config.activeIndex]} ${config.activeIndex + 1}/${config.itemCount}", 97 | style: const TextStyle(fontSize: 20.0), 98 | ), 99 | Expanded( 100 | child: Align( 101 | alignment: Alignment.centerRight, 102 | child: const DotSwiperPaginationBuilder( 103 | color: Colors.black12, 104 | activeColor: Colors.black, 105 | size: 10.0, 106 | activeSize: 20.0) 107 | .build(context, config), 108 | ), 109 | ) 110 | ], 111 | ), 112 | ); 113 | }), 114 | control: const SwiperControl(), 115 | ))); 116 | 117 | controller.startAutoplay(); 118 | 119 | controller.stopAutoplay(); 120 | 121 | await controller.move(0, animation: false); 122 | await controller.move(0, animation: false); 123 | 124 | await controller.next(animation: false); 125 | await controller.previous(animation: false); 126 | 127 | expect(find.text("0", skipOffstage: false), findsOneWidget); 128 | }); 129 | 130 | testWidgets('Swiper fraction', (WidgetTester tester) async { 131 | // Build our app and trigger a frame. 132 | SwiperController controller = SwiperController(); 133 | await tester.pumpWidget(MaterialApp( 134 | home: Swiper( 135 | controller: controller, 136 | itemBuilder: (context, index) { 137 | return const Text("0"); 138 | }, 139 | itemCount: 10, 140 | pagination: const SwiperPagination(builder: SwiperPagination.fraction), 141 | control: const SwiperControl(), 142 | ))); 143 | 144 | expect(find.text("0", skipOffstage: false), findsOneWidget); 145 | }); 146 | 147 | testWidgets('Zero itemCount', (WidgetTester tester) async { 148 | // Build our app and trigger a frame. 149 | SwiperController controller = SwiperController(); 150 | await tester.pumpWidget(MaterialApp( 151 | home: Swiper( 152 | controller: controller, 153 | itemBuilder: (context, index) { 154 | return const Text("0"); 155 | }, 156 | itemCount: 0, 157 | pagination: const SwiperPagination(builder: SwiperPagination.fraction), 158 | control: const SwiperControl(), 159 | ))); 160 | 161 | expect(find.text("0", skipOffstage: false), findsNothing); 162 | }); 163 | } 164 | -------------------------------------------------------------------------------- /test/layout_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | 5 | void main() { 6 | testWidgets('STACK', (WidgetTester tester) async { 7 | await tester.pumpWidget(MaterialApp( 8 | home: Swiper( 9 | layout: SwiperLayout.STACK, 10 | itemWidth: 300.0, 11 | itemHeight: 200.0, 12 | itemBuilder: (context, index) { 13 | return Container( 14 | color: Colors.grey, 15 | child: Center( 16 | child: Text("$index"), 17 | ), 18 | ); 19 | }, 20 | itemCount: 10))); 21 | }); 22 | 23 | testWidgets('TINDER', (WidgetTester tester) async { 24 | await tester.pumpWidget(MaterialApp( 25 | home: Swiper( 26 | layout: SwiperLayout.TINDER, 27 | itemWidth: 300.0, 28 | itemHeight: 200.0, 29 | itemBuilder: (context, index) { 30 | return Container( 31 | color: Colors.grey, 32 | child: Center( 33 | child: Text("$index"), 34 | ), 35 | ); 36 | }, 37 | itemCount: 10))); 38 | }); 39 | 40 | testWidgets('DEFAULT', (WidgetTester tester) async { 41 | await tester.pumpWidget(MaterialApp( 42 | home: Swiper( 43 | layout: SwiperLayout.DEFAULT, 44 | viewportFraction: 0.8, 45 | scale: 0.9, 46 | itemBuilder: (context, index) { 47 | return Container( 48 | color: Colors.grey, 49 | child: Center( 50 | child: Text("$index"), 51 | ), 52 | ); 53 | }, 54 | itemCount: 10))); 55 | }); 56 | 57 | testWidgets('CUSTOM', (WidgetTester tester) async { 58 | CustomLayoutOption customLayoutOption; 59 | customLayoutOption = CustomLayoutOption(startIndex: -1, stateCount: 3) 60 | ..addRotate([-45.0 / 180, 0.0, 45.0 / 180]) 61 | ..addTranslate([ 62 | const Offset(-370.0, -40.0), 63 | const Offset(0.0, 0.0), 64 | const Offset(370.0, -40.0) 65 | ]); 66 | await tester.pumpWidget(MaterialApp( 67 | home: Swiper( 68 | layout: SwiperLayout.CUSTOM, 69 | itemWidth: 300.0, 70 | itemHeight: 200.0, 71 | customLayoutOption: customLayoutOption, 72 | itemBuilder: (context, index) { 73 | return Container( 74 | color: Colors.grey, 75 | child: Center( 76 | child: Text("$index"), 77 | ), 78 | ); 79 | }, 80 | itemCount: 10))); 81 | }); 82 | } 83 | -------------------------------------------------------------------------------- /test/pagination_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | 5 | void main() { 6 | testWidgets('Pagination', (WidgetTester tester) async { 7 | SwiperController controller = SwiperController(); 8 | 9 | SwiperPluginConfig config = SwiperPluginConfig( 10 | activeIndex: 0, 11 | controller: controller, 12 | itemCount: 10, 13 | scrollDirection: Axis.horizontal); 14 | 15 | Key key = UniqueKey(); 16 | await tester.pumpWidget(MaterialApp( 17 | home: Scaffold(body: Builder(builder: (BuildContext context) { 18 | return DotSwiperPaginationBuilder( 19 | key: key, 20 | activeColor: const Color(0xff000000), 21 | color: const Color(0xffffffff), 22 | space: 10.0, 23 | size: 10.0, 24 | activeSize: 20.0) 25 | .build(context, config); 26 | })), 27 | )); 28 | 29 | for (int i = 0; i < 10; ++i) { 30 | expect(find.byWidgetPredicate((Widget widget) { 31 | if (widget.key != null && 32 | widget.key is ValueKey && 33 | (widget.key as ValueKey).value == 'pagination_$i') return true; 34 | 35 | return false; 36 | }), findsOneWidget); 37 | } 38 | 39 | expect(find.byKey(key), findsOneWidget); 40 | }); 41 | 42 | testWidgets('Pagination vertical', (WidgetTester tester) async { 43 | SwiperController controller = SwiperController(); 44 | 45 | SwiperPluginConfig config = SwiperPluginConfig( 46 | activeIndex: 0, 47 | controller: controller, 48 | itemCount: 10, 49 | scrollDirection: Axis.vertical); 50 | 51 | Key key = UniqueKey(); 52 | await tester.pumpWidget(MaterialApp( 53 | home: Scaffold(body: Builder(builder: (BuildContext context) { 54 | return DotSwiperPaginationBuilder( 55 | key: key, 56 | activeColor: const Color(0xff000000), 57 | color: const Color(0xffffffff), 58 | space: 10.0, 59 | size: 10.0, 60 | activeSize: 20.0) 61 | .build(context, config); 62 | })), 63 | )); 64 | 65 | for (int i = 0; i < 10; ++i) { 66 | expect(find.byWidgetPredicate((Widget widget) { 67 | if (widget.key != null && 68 | widget.key is ValueKey && 69 | (widget.key as ValueKey).value == 'pagination_$i') return true; 70 | 71 | return false; 72 | }), findsOneWidget); 73 | } 74 | 75 | expect(find.byKey(key), findsOneWidget); 76 | }); 77 | 78 | testWidgets('Pagination fraction', (WidgetTester tester) async { 79 | SwiperController controller = SwiperController(); 80 | 81 | SwiperPluginConfig config = SwiperPluginConfig( 82 | activeIndex: 0, 83 | controller: controller, 84 | itemCount: 10, 85 | scrollDirection: Axis.horizontal); 86 | 87 | Key key = UniqueKey(); 88 | await tester.pumpWidget(MaterialApp( 89 | home: Scaffold(body: Builder(builder: (BuildContext context) { 90 | return FractionPaginationBuilder( 91 | key: key, 92 | activeColor: const Color(0xff000000), 93 | color: const Color(0xffffffff), 94 | ).build(context, config); 95 | })), 96 | )); 97 | 98 | expect(find.text("1"), findsOneWidget); 99 | expect(find.text(" / 10"), findsOneWidget); 100 | 101 | expect(find.byKey(key), findsOneWidget); 102 | }); 103 | 104 | testWidgets('Pagination fraction vertical', (WidgetTester tester) async { 105 | SwiperController controller = SwiperController(); 106 | 107 | SwiperPluginConfig config = SwiperPluginConfig( 108 | activeIndex: 0, 109 | controller: controller, 110 | itemCount: 10, 111 | scrollDirection: Axis.vertical); 112 | 113 | Key key = UniqueKey(); 114 | await tester.pumpWidget(MaterialApp( 115 | home: Scaffold(body: Builder(builder: (BuildContext context) { 116 | return FractionPaginationBuilder( 117 | key: key, 118 | activeColor: const Color(0xff000000), 119 | color: const Color(0xffffffff), 120 | ).build(context, config); 121 | })), 122 | )); 123 | 124 | expect(find.text("1"), findsOneWidget); 125 | expect(find.text("10"), findsOneWidget); 126 | 127 | expect(find.byKey(key), findsOneWidget); 128 | }); 129 | } 130 | --------------------------------------------------------------------------------