├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── example │ │ │ │ │ └── MainActivity.kt │ │ │ └── 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 │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── 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 │ │ └── Runner-Bridging-Header.h ├── lib │ └── main.dart ├── pubspec.lock ├── pubspec.yaml ├── test │ └── widget_test.dart └── web │ ├── favicon.png │ ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png │ ├── index.html │ └── manifest.json ├── lib ├── action_slider.dart └── src │ ├── action_slider_widget.dart │ ├── conditional_wrapper.dart │ ├── cross_fade.dart │ ├── cursors.dart │ ├── icons.dart │ ├── state.dart │ ├── status.dart │ └── style.dart ├── pubspec.yaml ├── screenshots └── preview.webp └── test └── action_slider_test.dart /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: splashbyte 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: ['buymeacoffee.com/splashbyte'] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 25 | /pubspec.lock 26 | **/doc/api/ 27 | .dart_tool/ 28 | .packages 29 | build/ 30 | -------------------------------------------------------------------------------- /.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: 77d935af4db863f6abd0b9c31c7e6df2a13de57b 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.8.0-beta.4 2 | 3 | - adds `cursors` to all constructors 4 | 5 | ## 0.8.0-beta.3 [2024-09-21] 6 | 7 | - adds `childPosition` to `ActionSlider.standard` 8 | - adds `childAnimation` parameter to `ActionSlider.standard` and `ActionSlider.dual` 9 | - migrates from `SliderMode` to `SliderStatus` 10 | - adds `status` to all constructors 11 | - adds `expanded`, `highlighted` and `side` parameters to `SliderMode.loading`, `SliderMode.success` 12 | and `SliderMode.failure` 13 | - BREAKING: renames `customForegroundBuilder` to `customIconBuilder` 14 | - BREAKING: changes default background color from `ThemeData.cardColor` to `ThemeData.colorScheme.surface` 15 | - BREAKING: removes `rolling` in favor of the new `iconAnimation` parameter 16 | - BREAKING: renames `SlidingState` to `SlidingStatus` 17 | - BREAKING: moves parameters in `ActionSlider.standard` and `ActionSlider.dual` to `style`: 18 | - `backgroundColor` 19 | - `backgroundBorderRadius` (renamed to `borderRadius`) 20 | - `toggleColor` 21 | - `boxShadow` 22 | - adds option to add `SliderStyle` to `extensions` of `ThemeData` 23 | 24 | ## 0.8.0-beta.2 [2024-07-31] 25 | 26 | - fixes `rolling` with `ActionSlider.dual` and when using a custom `SliderMode` 27 | - BREAKING: removes `ActionSliderController.dual` 28 | - BREAKING: moves `anchorPosition` and `allowedInterval` from `ActionSliderControllerState` to `ActionSlider` 29 | 30 | ## 0.8.0-beta.1 [2024-07-31] 31 | 32 | - fixes `SliderBehavior.stretch` with `ActionSlider.dual` 33 | - BREAKING: default loading icon is now adaptive for iOS and macOS 34 | 35 | ## 0.8.0-beta.0 [2024-07-30] 36 | 37 | - BREAKING: increases minimum SDK to 3.0.0 38 | - BREAKING: renames `movementCurve` to `anchorPositionCurve` 39 | - BREAKING: renames `movementDuration` to `anchorPositionDuration` 40 | - BREAKING: renames `SliderDirection.begin` to `SliderDirection.start` 41 | - adds `resultToggleMargin`, `toggleMarginCurve` and `toggleMarginDuration` 42 | - adds `SliderMode.loadingExpanded`, `SliderMode.successExpanded` and `SliderMode.failureExpanded` 43 | 44 | ## 0.7.0 [2023-07-30] 45 | 46 | - BREAKING: increases minimum SDK to 2.17 47 | - BREAKING: changes default background color from `ThemeData.backgroundColor` to `ThemeData.cardColor` 48 | - BREAKING: changes state type of `ActionSliderController` from `SliderMode` to `ActionSliderControllerState` 49 | - adds `anchorPosition` and `allowedInterval` to `ActionSliderController` 50 | - adds `anchorPosition` and `allowedInterval` to `SliderState` 51 | - adds `ActionSliderController.dual` 52 | - closes [#6](https://github.com/splashbyte/action_slider/issues/6) 53 | 54 | ## 0.6.1 [2022-12-09] 55 | 56 | - adds support for `RTL` 57 | - adds `direction` to constructors 58 | 59 | ## 0.6.0 [2022-07-05] 60 | 61 | - minor fixes 62 | - fixes #1 63 | - BREAKING: renames `onSlide` to `action` in `ActionSlider.standard` 64 | 65 | ## 0.5.0 [2022-03-17] 66 | 67 | - adds `stateChangeCallback`, `actionThreshold` and `actionThresholdType` 68 | - BREAKING: renames `onSlide` to `action` 69 | - BREAKING: renames `SlideCallback` to `Action` 70 | 71 | ## 0.4.0 [2022-03-17] 72 | 73 | - major customizability improvements 74 | - adds `outerBackgroundBuilder` and `outerBackgroundChild` to constructor `ActionSlider.custom` 75 | - adds `crossFadeDuration`, `customBackgroundBuilder`, `customBackgroundBuilderChild` 76 | , `customOuterBackgroundBuilder` and `customOuterBackgroundBuilderChild` to 77 | constructor `ActionSlider.standard` 78 | - BREAKING: renames `SlidingState.loading` to `SlidingState.compact` 79 | - BREAKING: renames `CrossFade` to `SliderCrossFade` 80 | - BREAKING change for constructor `ActionSlider.standard`: 81 | - removes `circleRadius` and adds `borderWidth` instead 82 | - BREAKING change for constructor `ActionSlider.custom`: 83 | - removes `toggleHeight` and adds `toggleMargin` instead 84 | 85 | ## 0.3.0 [2022-03-12] 86 | 87 | - adds `onTap` parameter 88 | - adds jump method to `ActionSliderController` 89 | - minor gesture detection improvements 90 | 91 | ## 0.2.1 [2022-02-28] 92 | 93 | - changes README.md 94 | 95 | ## 0.2.0 [2022-02-28] 96 | 97 | - BREAKING: changes parameters of `ForegroundBuilder` and `BackgroundBuilder` 98 | - adds `SliderBehaviour.stretch` 99 | 100 | ## 0.1.2 [2022-02-25] 101 | 102 | - BREAKING: changes parameters of `SlideCallback` 103 | - optimizes fade animation 104 | 105 | ## 0.1.1 [2022-02-21] 106 | 107 | - fixes README.md 108 | 109 | ## 0.1.0 [2022-02-21] 110 | 111 | - initial release 112 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2024, Martin Klüpfel 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![pub.dev](https://img.shields.io/pub/v/action_slider.svg?style=flat?logo=dart)](https://pub.dev/packages/action_slider) 2 | [![github](https://img.shields.io/static/v1?label=platform&message=flutter&color=1ebbfd)](https://github.com/SplashByte/action_slider) 3 | [![likes](https://img.shields.io/pub/likes/action_slider)](https://pub.dev/packages/action_slider/score) 4 | [![downloads](https://img.shields.io/pub/dm/action_slider)](https://pub.dev/packages/action_slider/score) 5 | [![pub points](https://img.shields.io/pub/points/action_slider)](https://pub.dev/packages/action_slider/score) 6 | [![license](https://img.shields.io/github/license/SplashByte/action_slider.svg)](https://github.com/SplashByte/action_slider/blob/main/LICENSE) 7 | [![buy me a coffee](https://img.shields.io/badge/-buy_me_a%C2%A0coffee-gray?logo=buy-me-a-coffee)](https://www.buymeacoffee.com/splashbyte) 8 | 9 | ### If you like this package, please leave a like there on [pub.dev](https://pub.dev/packages/action_slider) and star on [GitHub](https://github.com/SplashByte/action_slider). 10 | 11 | A fully customizable slider to confirm actions and provide feedback on the success. It supports different states like loading, success and failure. 12 | 13 | `LTR` and `RTL` are both supported. 14 | For a switch with a similar look, you can check out [animated_toggle_switch](https://pub.dev/packages/animated_toggle_switch). 15 | 16 | ## Examples 17 | `ActionSlider.standard()` with `SliderBehavior.stretch` 18 | ![action_slider_example_snake](https://github.com/splashbyte/action_slider/assets/43761463/d2f92414-bded-48ae-9cf5-9df030fb0be8) 19 | ![action_slider_example_expanded](https://github.com/user-attachments/assets/c14f6bee-f2b9-4dfd-806c-fe2fa089a0ea) 20 | 21 | `ActionSlider.dual()` 22 | ![action_slider_example_dual](https://github.com/splashbyte/action_slider/assets/43761463/4903161e-d2f4-47aa-934a-464fba33d2df) 23 | ![action_slider_example_dual_customized](https://github.com/user-attachments/assets/b4561a3b-daf9-4f60-a30b-c3f83f5f0f8e) 24 | 25 | `ActionSlider.standard()` with `TextDirection.rtl` 26 | ![action_slider_example_rtl](https://github.com/splashbyte/action_slider/assets/43761463/5d81d3d2-ca52-4eb5-93b3-fada883a6a4f) 27 | 28 | `ActionSlider.standard()` with `SliderIconAnimation.roll` 29 | ![action_slider_example_rolling](https://github.com/splashbyte/action_slider/assets/43761463/0a5010e2-d369-46d3-bdfb-0df5832125ed) 30 | 31 | `ActionSlider.standard()` with `SliderBehavior.stretch` and `SliderIconAnimation.roll` 32 | ![action_slider_example_rolling_snake](https://github.com/splashbyte/action_slider/assets/43761463/e4f27603-83db-412a-8777-c737a9c55b14) 33 | 34 | You can build your own sliders with `ActionSlider.custom()` 35 | ![action_slider_example_custom](https://github.com/splashbyte/action_slider/assets/43761463/3b751087-f721-40f2-9055-4aa8af61e0d8) 36 | 37 | 38 | ## Easy Usage 39 | 40 | Easy to use and highly customizable. 41 | 42 | ```dart 43 | ActionSlider.standard( 44 | child: const Text('Slide to confirm'), 45 | action: (controller) async { 46 | controller.loading(); //starts loading animation 47 | await Future.delayed(const Duration(seconds: 3)); 48 | controller.success(); //starts success animation 49 | }, 50 | ... //many more parameters 51 | ) 52 | ``` 53 | 54 | Two directions with `ActionSlider.dual` 55 | ```dart 56 | ActionSlider.dual( 57 | child: const Text('Slide to confirm'), 58 | startAction: (controller) async { 59 | controller.success(expanded: true, side: SliderSide.start); //starts success animation with an expanded slider 60 | }, 61 | endAction: (controller) async { 62 | controller.success(); //starts success animation 63 | }, 64 | ... //many more parameters 65 | ) 66 | ``` 67 | 68 | Maximum customizability with `ActionSlider.custom`. 69 | ```dart 70 | ActionSlider.custom( 71 | foregroundBuilder: (context, state, child) => ..., 72 | backgroundBuilder: (context, state, child) => ..., 73 | outerBackgroundBuilder: (context, state, child) => ..., 74 | action: (controller) => ..., 75 | ... //many more parameters 76 | ) 77 | ``` 78 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | analyzer: 4 | language: 5 | strict-casts: true 6 | strict-raw-types: true 7 | 8 | linter: 9 | rules: 10 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Android Studio will place build artifacts here 43 | /android/app/debug 44 | /android/app/profile 45 | /android/app/release 46 | -------------------------------------------------------------------------------- /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: 77d935af4db863f6abd0b9c31c7e6df2a13de57b 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /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 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion flutter.compileSdkVersion 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 46 | applicationId "com.example.example" 47 | minSdkVersion flutter.minSdkVersion 48 | targetSdkVersion flutter.targetSdkVersion 49 | versionCode flutterVersionCode.toInteger() 50 | versionName flutterVersionName 51 | } 52 | 53 | buildTypes { 54 | release { 55 | // TODO: Add your own signing config for the release build. 56 | // Signing with the debug keys for now, so `flutter run --release` works. 57 | signingConfig signingConfigs.debug 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | } 69 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | tasks.register("clean", Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /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.6.1-all.zip 7 | -------------------------------------------------------------------------------- /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/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /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 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | ); 26 | name = "Embed Frameworks"; 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 9740EEB11CF90186004384FC /* Flutter */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 65 | ); 66 | name = Flutter; 67 | sourceTree = ""; 68 | }; 69 | 97C146E51CF9000F007C117D = { 70 | isa = PBXGroup; 71 | children = ( 72 | 9740EEB11CF90186004384FC /* Flutter */, 73 | 97C146F01CF9000F007C117D /* Runner */, 74 | 97C146EF1CF9000F007C117D /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 97C146EF1CF9000F007C117D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 97C146EE1CF9000F007C117D /* Runner.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 97C146F01CF9000F007C117D /* Runner */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 92 | 97C147021CF9000F007C117D /* Info.plist */, 93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 97 | ); 98 | path = Runner; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | 97C146ED1CF9000F007C117D /* Runner */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 107 | buildPhases = ( 108 | 9740EEB61CF901F6004384FC /* Run Script */, 109 | 97C146EA1CF9000F007C117D /* Sources */, 110 | 97C146EB1CF9000F007C117D /* Frameworks */, 111 | 97C146EC1CF9000F007C117D /* Resources */, 112 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = Runner; 120 | productName = Runner; 121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | 97C146E61CF9000F007C117D /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 1510; 131 | ORGANIZATIONNAME = ""; 132 | TargetAttributes = { 133 | 97C146ED1CF9000F007C117D = { 134 | CreatedOnToolsVersion = 7.3.1; 135 | LastSwiftMigration = 1100; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 140 | compatibilityVersion = "Xcode 9.3"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | Base, 146 | ); 147 | mainGroup = 97C146E51CF9000F007C117D; 148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 97C146ED1CF9000F007C117D /* Runner */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 97C146EC1CF9000F007C117D /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXShellScriptBuildPhase section */ 172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 173 | isa = PBXShellScriptBuildPhase; 174 | alwaysOutOfDate = 1; 175 | buildActionMask = 2147483647; 176 | files = ( 177 | ); 178 | inputPaths = ( 179 | "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", 180 | ); 181 | name = "Thin Binary"; 182 | outputPaths = ( 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | shellPath = /bin/sh; 186 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 187 | }; 188 | 9740EEB61CF901F6004384FC /* Run Script */ = { 189 | isa = PBXShellScriptBuildPhase; 190 | alwaysOutOfDate = 1; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | ); 194 | inputPaths = ( 195 | ); 196 | name = "Run Script"; 197 | outputPaths = ( 198 | ); 199 | runOnlyForDeploymentPostprocessing = 0; 200 | shellPath = /bin/sh; 201 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 202 | }; 203 | /* End PBXShellScriptBuildPhase section */ 204 | 205 | /* Begin PBXSourcesBuildPhase section */ 206 | 97C146EA1CF9000F007C117D /* Sources */ = { 207 | isa = PBXSourcesBuildPhase; 208 | buildActionMask = 2147483647; 209 | files = ( 210 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 211 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | }; 215 | /* End PBXSourcesBuildPhase section */ 216 | 217 | /* Begin PBXVariantGroup section */ 218 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 219 | isa = PBXVariantGroup; 220 | children = ( 221 | 97C146FB1CF9000F007C117D /* Base */, 222 | ); 223 | name = Main.storyboard; 224 | sourceTree = ""; 225 | }; 226 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 227 | isa = PBXVariantGroup; 228 | children = ( 229 | 97C147001CF9000F007C117D /* Base */, 230 | ); 231 | name = LaunchScreen.storyboard; 232 | sourceTree = ""; 233 | }; 234 | /* End PBXVariantGroup section */ 235 | 236 | /* Begin XCBuildConfiguration section */ 237 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 238 | isa = XCBuildConfiguration; 239 | buildSettings = { 240 | ALWAYS_SEARCH_USER_PATHS = NO; 241 | CLANG_ANALYZER_NONNULL = YES; 242 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 243 | CLANG_CXX_LIBRARY = "libc++"; 244 | CLANG_ENABLE_MODULES = YES; 245 | CLANG_ENABLE_OBJC_ARC = YES; 246 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 247 | CLANG_WARN_BOOL_CONVERSION = YES; 248 | CLANG_WARN_COMMA = YES; 249 | CLANG_WARN_CONSTANT_CONVERSION = YES; 250 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 251 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 252 | CLANG_WARN_EMPTY_BODY = YES; 253 | CLANG_WARN_ENUM_CONVERSION = YES; 254 | CLANG_WARN_INFINITE_RECURSION = YES; 255 | CLANG_WARN_INT_CONVERSION = YES; 256 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 257 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 258 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 259 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 260 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 261 | CLANG_WARN_STRICT_PROTOTYPES = YES; 262 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 263 | CLANG_WARN_UNREACHABLE_CODE = YES; 264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 265 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 266 | COPY_PHASE_STRIP = NO; 267 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 268 | ENABLE_NS_ASSERTIONS = NO; 269 | ENABLE_STRICT_OBJC_MSGSEND = YES; 270 | GCC_C_LANGUAGE_STANDARD = gnu99; 271 | GCC_NO_COMMON_BLOCKS = YES; 272 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 273 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 274 | GCC_WARN_UNDECLARED_SELECTOR = YES; 275 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 276 | GCC_WARN_UNUSED_FUNCTION = YES; 277 | GCC_WARN_UNUSED_VARIABLE = YES; 278 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 279 | MTL_ENABLE_DEBUG_INFO = NO; 280 | SDKROOT = iphoneos; 281 | SUPPORTED_PLATFORMS = iphoneos; 282 | TARGETED_DEVICE_FAMILY = "1,2"; 283 | VALIDATE_PRODUCT = YES; 284 | }; 285 | name = Profile; 286 | }; 287 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 288 | isa = XCBuildConfiguration; 289 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 290 | buildSettings = { 291 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 292 | CLANG_ENABLE_MODULES = YES; 293 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 294 | DEVELOPMENT_TEAM = Y9NGAZW57Y; 295 | ENABLE_BITCODE = NO; 296 | INFOPLIST_FILE = Runner/Info.plist; 297 | LD_RUNPATH_SEARCH_PATHS = ( 298 | "$(inherited)", 299 | "@executable_path/Frameworks", 300 | ); 301 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 302 | PRODUCT_NAME = "$(TARGET_NAME)"; 303 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 304 | SWIFT_VERSION = 5.0; 305 | VERSIONING_SYSTEM = "apple-generic"; 306 | }; 307 | name = Profile; 308 | }; 309 | 97C147031CF9000F007C117D /* Debug */ = { 310 | isa = XCBuildConfiguration; 311 | buildSettings = { 312 | ALWAYS_SEARCH_USER_PATHS = NO; 313 | CLANG_ANALYZER_NONNULL = YES; 314 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 315 | CLANG_CXX_LIBRARY = "libc++"; 316 | CLANG_ENABLE_MODULES = YES; 317 | CLANG_ENABLE_OBJC_ARC = YES; 318 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 319 | CLANG_WARN_BOOL_CONVERSION = YES; 320 | CLANG_WARN_COMMA = YES; 321 | CLANG_WARN_CONSTANT_CONVERSION = YES; 322 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 323 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 324 | CLANG_WARN_EMPTY_BODY = YES; 325 | CLANG_WARN_ENUM_CONVERSION = YES; 326 | CLANG_WARN_INFINITE_RECURSION = YES; 327 | CLANG_WARN_INT_CONVERSION = YES; 328 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 330 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 332 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 333 | CLANG_WARN_STRICT_PROTOTYPES = YES; 334 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 335 | CLANG_WARN_UNREACHABLE_CODE = YES; 336 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 337 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 338 | COPY_PHASE_STRIP = NO; 339 | DEBUG_INFORMATION_FORMAT = dwarf; 340 | ENABLE_STRICT_OBJC_MSGSEND = YES; 341 | ENABLE_TESTABILITY = YES; 342 | GCC_C_LANGUAGE_STANDARD = gnu99; 343 | GCC_DYNAMIC_NO_PIC = NO; 344 | GCC_NO_COMMON_BLOCKS = YES; 345 | GCC_OPTIMIZATION_LEVEL = 0; 346 | GCC_PREPROCESSOR_DEFINITIONS = ( 347 | "DEBUG=1", 348 | "$(inherited)", 349 | ); 350 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 351 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 352 | GCC_WARN_UNDECLARED_SELECTOR = YES; 353 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 354 | GCC_WARN_UNUSED_FUNCTION = YES; 355 | GCC_WARN_UNUSED_VARIABLE = YES; 356 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 357 | MTL_ENABLE_DEBUG_INFO = YES; 358 | ONLY_ACTIVE_ARCH = YES; 359 | SDKROOT = iphoneos; 360 | TARGETED_DEVICE_FAMILY = "1,2"; 361 | }; 362 | name = Debug; 363 | }; 364 | 97C147041CF9000F007C117D /* Release */ = { 365 | isa = XCBuildConfiguration; 366 | buildSettings = { 367 | ALWAYS_SEARCH_USER_PATHS = NO; 368 | CLANG_ANALYZER_NONNULL = YES; 369 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 370 | CLANG_CXX_LIBRARY = "libc++"; 371 | CLANG_ENABLE_MODULES = YES; 372 | CLANG_ENABLE_OBJC_ARC = YES; 373 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 374 | CLANG_WARN_BOOL_CONVERSION = YES; 375 | CLANG_WARN_COMMA = YES; 376 | CLANG_WARN_CONSTANT_CONVERSION = YES; 377 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 378 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 379 | CLANG_WARN_EMPTY_BODY = YES; 380 | CLANG_WARN_ENUM_CONVERSION = YES; 381 | CLANG_WARN_INFINITE_RECURSION = YES; 382 | CLANG_WARN_INT_CONVERSION = YES; 383 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 384 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 385 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 386 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 387 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 388 | CLANG_WARN_STRICT_PROTOTYPES = YES; 389 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 390 | CLANG_WARN_UNREACHABLE_CODE = YES; 391 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 392 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 393 | COPY_PHASE_STRIP = NO; 394 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 395 | ENABLE_NS_ASSERTIONS = NO; 396 | ENABLE_STRICT_OBJC_MSGSEND = YES; 397 | GCC_C_LANGUAGE_STANDARD = gnu99; 398 | GCC_NO_COMMON_BLOCKS = YES; 399 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 400 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 401 | GCC_WARN_UNDECLARED_SELECTOR = YES; 402 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 403 | GCC_WARN_UNUSED_FUNCTION = YES; 404 | GCC_WARN_UNUSED_VARIABLE = YES; 405 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 406 | MTL_ENABLE_DEBUG_INFO = NO; 407 | SDKROOT = iphoneos; 408 | SUPPORTED_PLATFORMS = iphoneos; 409 | SWIFT_COMPILATION_MODE = wholemodule; 410 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 411 | TARGETED_DEVICE_FAMILY = "1,2"; 412 | VALIDATE_PRODUCT = YES; 413 | }; 414 | name = Release; 415 | }; 416 | 97C147061CF9000F007C117D /* Debug */ = { 417 | isa = XCBuildConfiguration; 418 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 419 | buildSettings = { 420 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 421 | CLANG_ENABLE_MODULES = YES; 422 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 423 | DEVELOPMENT_TEAM = Y9NGAZW57Y; 424 | ENABLE_BITCODE = NO; 425 | INFOPLIST_FILE = Runner/Info.plist; 426 | LD_RUNPATH_SEARCH_PATHS = ( 427 | "$(inherited)", 428 | "@executable_path/Frameworks", 429 | ); 430 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 431 | PRODUCT_NAME = "$(TARGET_NAME)"; 432 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 433 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 434 | SWIFT_VERSION = 5.0; 435 | VERSIONING_SYSTEM = "apple-generic"; 436 | }; 437 | name = Debug; 438 | }; 439 | 97C147071CF9000F007C117D /* Release */ = { 440 | isa = XCBuildConfiguration; 441 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 442 | buildSettings = { 443 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 444 | CLANG_ENABLE_MODULES = YES; 445 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 446 | DEVELOPMENT_TEAM = Y9NGAZW57Y; 447 | ENABLE_BITCODE = NO; 448 | INFOPLIST_FILE = Runner/Info.plist; 449 | LD_RUNPATH_SEARCH_PATHS = ( 450 | "$(inherited)", 451 | "@executable_path/Frameworks", 452 | ); 453 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 454 | PRODUCT_NAME = "$(TARGET_NAME)"; 455 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 456 | SWIFT_VERSION = 5.0; 457 | VERSIONING_SYSTEM = "apple-generic"; 458 | }; 459 | name = Release; 460 | }; 461 | /* End XCBuildConfiguration section */ 462 | 463 | /* Begin XCConfigurationList section */ 464 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 465 | isa = XCConfigurationList; 466 | buildConfigurations = ( 467 | 97C147031CF9000F007C117D /* Debug */, 468 | 97C147041CF9000F007C117D /* Release */, 469 | 249021D3217E4FDB00AE95B9 /* Profile */, 470 | ); 471 | defaultConfigurationIsVisible = 0; 472 | defaultConfigurationName = Release; 473 | }; 474 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 475 | isa = XCConfigurationList; 476 | buildConfigurations = ( 477 | 97C147061CF9000F007C117D /* Debug */, 478 | 97C147071CF9000F007C117D /* Release */, 479 | 249021D4217E4FDB00AE95B9 /* Profile */, 480 | ); 481 | defaultConfigurationIsVisible = 0; 482 | defaultConfigurationName = Release; 483 | }; 484 | /* End XCConfigurationList section */ 485 | }; 486 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 487 | } 488 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /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 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | UIApplicationSupportsIndirectInputEvents 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:action_slider/action_slider.dart'; 4 | import 'package:cross_fade/cross_fade.dart'; 5 | import 'package:flutter/cupertino.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | void main() { 9 | runApp(const ExampleApp()); 10 | } 11 | 12 | class ExampleApp extends StatelessWidget { 13 | const ExampleApp({super.key}); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return MaterialApp( 18 | title: 'Action Slider Example', 19 | theme: ThemeData( 20 | primarySwatch: Colors.blue, 21 | scaffoldBackgroundColor: Colors.white, 22 | ), 23 | home: const ExamplePage(title: 'Action Slider Example'), 24 | ); 25 | } 26 | } 27 | 28 | class ExamplePage extends StatefulWidget { 29 | const ExamplePage({super.key, required this.title}); 30 | 31 | final String title; 32 | 33 | @override 34 | State createState() => _ExamplePageState(); 35 | } 36 | 37 | class _ExamplePageState extends State { 38 | final _controller = ActionSliderController(); 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | final theme = Theme.of(context); 43 | 44 | return Scaffold( 45 | appBar: AppBar( 46 | title: Text(widget.title), 47 | ), 48 | body: Center( 49 | child: Column( 50 | mainAxisAlignment: MainAxisAlignment.center, 51 | children: [ 52 | DefaultTextStyle.merge( 53 | style: const TextStyle(color: Colors.white), 54 | child: ActionSlider.dual( 55 | anchorPosition: 0.7, 56 | style: SliderStyle( 57 | borderRadius: BorderRadius.circular(10.0), 58 | backgroundColor: Colors.black, 59 | ), 60 | width: 300.0, 61 | startChild: const Text('Start'), 62 | endChild: const Text('End'), 63 | icon: const RotatedBox( 64 | quarterTurns: 1, 65 | child: Icon(Icons.unfold_more_rounded, size: 28.0)), 66 | startAction: (controller) async { 67 | controller.loading(); //starts loading animation 68 | await Future.delayed(const Duration(seconds: 3)); 69 | controller.success(); //starts success animation 70 | await Future.delayed(const Duration(seconds: 1)); 71 | controller.reset(); //resets the slider 72 | }, 73 | endAction: (controller) async { 74 | controller.loading(expanded: true); //starts loading animation 75 | await Future.delayed(const Duration(seconds: 3)); 76 | controller.success(expanded: true); //starts success animation 77 | await Future.delayed(const Duration(seconds: 1)); 78 | controller.reset(); //resets the slider 79 | }, 80 | ), 81 | ), 82 | const SizedBox(height: 24.0), 83 | ActionSlider.standard( 84 | resultBorderWidth: 0.0, 85 | sliderBehavior: SliderBehavior.stretch, 86 | width: 300.0, 87 | style: const SliderStyle( 88 | backgroundColor: Colors.white, 89 | toggleColor: Colors.lightGreenAccent, 90 | ), 91 | action: (controller) async { 92 | controller.loading(expanded: true); //starts loading animation 93 | await Future.delayed(const Duration(seconds: 3)); 94 | controller.success(expanded: true); //starts success animation 95 | await Future.delayed(const Duration(seconds: 1)); 96 | controller.reset(); 97 | }, 98 | child: const Text('Slide to confirm'), 99 | ), 100 | const SizedBox(height: 24.0), 101 | ActionSlider.standard( 102 | width: 300.0, 103 | action: (controller) async { 104 | controller.loading(); //starts loading animation 105 | await Future.delayed(const Duration(seconds: 3)); 106 | controller.success(); //starts success animation 107 | await Future.delayed(const Duration(seconds: 1)); 108 | controller.reset(); //resets the slider 109 | }, 110 | direction: TextDirection.rtl, 111 | child: const Text('Slide to confirm'), 112 | ), 113 | const SizedBox(height: 24.0), 114 | ActionSlider.standard( 115 | iconAnimation: SliderIconAnimation.roll, 116 | width: 300.0, 117 | style: const SliderStyle( 118 | backgroundColor: Colors.black, 119 | toggleColor: Colors.purpleAccent, 120 | ), 121 | reverseSlideAnimationCurve: Curves.easeInOut, 122 | reverseSlideAnimationDuration: const Duration(milliseconds: 500), 123 | icon: const Icon(Icons.add), 124 | action: (controller) async { 125 | controller.loading(); //starts loading animation 126 | await Future.delayed(const Duration(seconds: 3)); 127 | controller.success(); //starts success animation 128 | await Future.delayed(const Duration(seconds: 1)); 129 | controller.reset(); //resets the slider 130 | }, 131 | child: const Text('Rolling slider', 132 | style: TextStyle(color: Colors.white)), 133 | ), 134 | const SizedBox(height: 24.0), 135 | ActionSlider.standard( 136 | sliderBehavior: SliderBehavior.stretch, 137 | iconAnimation: SliderIconAnimation.roll, 138 | width: 300.0, 139 | style: const SliderStyle( 140 | backgroundColor: Colors.white, 141 | toggleColor: Colors.amber, 142 | ), 143 | iconAlignment: Alignment.centerRight, 144 | loadingIcon: SizedBox( 145 | width: 55, 146 | child: Center( 147 | child: SizedBox( 148 | width: 24.0, 149 | height: 24.0, 150 | child: CircularProgressIndicator( 151 | strokeWidth: 2.0, color: theme.iconTheme.color), 152 | ))), 153 | successIcon: const SizedBox( 154 | width: 55, child: Center(child: Icon(Icons.check_rounded))), 155 | icon: const SizedBox( 156 | width: 55, child: Center(child: Icon(Icons.refresh_rounded))), 157 | action: (controller) async { 158 | controller.loading(); //starts loading animation 159 | await Future.delayed(const Duration(seconds: 3)); 160 | controller.success(); //starts success animation 161 | await Future.delayed(const Duration(seconds: 1)); 162 | controller.reset(); //resets the slider 163 | }, 164 | child: const Text('Swipe right'), 165 | ), 166 | const SizedBox(height: 24.0), 167 | ActionSlider.custom( 168 | width: 300.0, 169 | controller: _controller, 170 | height: 60.0, 171 | toggleWidth: 60.0, 172 | toggleMargin: EdgeInsets.zero, 173 | foregroundChild: DecoratedBox( 174 | decoration: BoxDecoration( 175 | color: Colors.black, 176 | borderRadius: BorderRadius.circular(5)), 177 | child: const Icon(Icons.check_rounded, color: Colors.white)), 178 | foregroundBuilder: (context, state, child) => child!, 179 | outerBackgroundBuilder: (context, state, child) => Card( 180 | margin: EdgeInsets.zero, 181 | color: Color.lerp(Colors.red, Colors.green, state.position), 182 | child: Center( 183 | child: Text(state.position.toStringAsFixed(2), 184 | style: theme.textTheme.titleMedium)), 185 | ), 186 | action: (controller) async { 187 | controller.loading(); //starts loading animation 188 | await Future.delayed(const Duration(seconds: 3)); 189 | controller.success(); //starts success animation 190 | await Future.delayed(const Duration(seconds: 1)); 191 | controller.reset(); //resets the slider 192 | }, 193 | ), 194 | const SizedBox(height: 24.0), 195 | ActionSlider.custom( 196 | toggleMargin: EdgeInsets.zero, 197 | width: 300.0, 198 | controller: _controller, 199 | toggleWidth: 60.0, 200 | height: 60.0, 201 | foregroundChild: Container( 202 | decoration: const BoxDecoration( 203 | color: Colors.black, 204 | borderRadius: BorderRadius.all(Radius.circular(30.0)), 205 | ), 206 | child: const Icon(Icons.check_rounded, color: Colors.white)), 207 | foregroundBuilder: (context, state, child) => child!, 208 | backgroundChild: Center( 209 | child: Text('Highly Customizable :)', 210 | style: theme.textTheme.titleMedium), 211 | ), 212 | backgroundBuilder: (context, state, child) => ClipRect( 213 | child: OverflowBox( 214 | maxWidth: state.standardSize.width, 215 | maxHeight: state.toggleSize.height, 216 | minWidth: state.standardSize.width, 217 | minHeight: state.toggleSize.height, 218 | child: child!)), 219 | action: (controller) async { 220 | controller.loading(); //starts loading animation 221 | await Future.delayed(const Duration(seconds: 3)); 222 | controller.success(); //starts success animation 223 | await Future.delayed(const Duration(seconds: 1)); 224 | controller.reset(); //resets the slider 225 | }, 226 | outerBackgroundBuilder: (context, state, _) => DecoratedBox( 227 | decoration: BoxDecoration( 228 | borderRadius: BorderRadius.circular(5.0), 229 | color: Colors.green)), 230 | ), 231 | const SizedBox(height: 24.0), 232 | ActionSlider.custom( 233 | width: 300.0, 234 | controller: _controller, 235 | sizeAnimationDuration: const Duration(milliseconds: 700), 236 | sizeAnimationCurve: 237 | const Interval(0.6, 1.0, curve: Curves.easeInOut), 238 | foregroundBuilder: (context, state, _) { 239 | final status = state.status; 240 | return Stack( 241 | fit: StackFit.passthrough, 242 | children: [ 243 | Opacity( 244 | opacity: 1.0 - state.relativeSize, 245 | child: AnimatedCheckIcon( 246 | icon: const Icon(Icons.check_rounded, 247 | color: Colors.white, size: 32.0), 248 | visible: status is SliderStatusSuccess, 249 | animationCurve: const Interval(0.8, 1.0), 250 | animationDuration: const Duration(milliseconds: 1000), 251 | ), 252 | ), 253 | Opacity( 254 | opacity: 1.0 - state.relativeSize, 255 | child: CrossFade( 256 | value: status, 257 | builder: (context, status) => 258 | status is SliderStatusLoading 259 | ? const Center( 260 | child: CupertinoActivityIndicator( 261 | color: Colors.white)) 262 | : const SizedBox()), 263 | ), 264 | Opacity( 265 | opacity: state.relativeSize, 266 | child: ScaleAppearingWidget( 267 | animationDuration: const Duration(milliseconds: 1000), 268 | animationCurve: 269 | const Interval(0.7, 1.0, curve: Curves.easeOutBack), 270 | visible: status.expanded, 271 | child: Container( 272 | decoration: const BoxDecoration( 273 | color: Colors.white, 274 | borderRadius: 275 | BorderRadius.all(Radius.circular(30.0)), 276 | ), 277 | child: Transform.rotate( 278 | angle: -state.position * pi, 279 | child: const Icon(Icons.arrow_forward, 280 | color: Colors.pinkAccent))), 281 | ), 282 | ), 283 | ], 284 | ); 285 | }, 286 | backgroundBuilder: (context, state, _) => Center( 287 | child: Text('Highly Customizable :)', 288 | style: theme.textTheme.titleSmall?.copyWith( 289 | color: Colors.white.withOpacity(1.0 - state.position))), 290 | ), 291 | action: (controller) async { 292 | controller.success(); //starts success animation 293 | await Future.delayed(const Duration(seconds: 3)); 294 | controller.reset(); //resets the slider 295 | }, 296 | outerBackgroundBuilder: (context, state, _) => DecoratedBox( 297 | decoration: BoxDecoration( 298 | borderRadius: BorderRadius.circular(40.0), 299 | color: Colors.pinkAccent, 300 | )), 301 | ), 302 | ], 303 | ), 304 | ), 305 | ); 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | action_slider: 5 | dependency: "direct main" 6 | description: 7 | path: ".." 8 | relative: true 9 | source: path 10 | version: "0.8.0-beta.3" 11 | async: 12 | dependency: transitive 13 | description: 14 | name: async 15 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 16 | url: "https://pub.dev" 17 | source: hosted 18 | version: "2.11.0" 19 | boolean_selector: 20 | dependency: transitive 21 | description: 22 | name: boolean_selector 23 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 24 | url: "https://pub.dev" 25 | source: hosted 26 | version: "2.1.1" 27 | characters: 28 | dependency: transitive 29 | description: 30 | name: characters 31 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 32 | url: "https://pub.dev" 33 | source: hosted 34 | version: "1.3.0" 35 | clock: 36 | dependency: transitive 37 | description: 38 | name: clock 39 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 40 | url: "https://pub.dev" 41 | source: hosted 42 | version: "1.1.1" 43 | collection: 44 | dependency: transitive 45 | description: 46 | name: collection 47 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 48 | url: "https://pub.dev" 49 | source: hosted 50 | version: "1.18.0" 51 | cross_fade: 52 | dependency: "direct main" 53 | description: 54 | name: cross_fade 55 | sha256: "447da147382725b7c3dc54ec723cd61613cee8a880933941a44118efc593c297" 56 | url: "https://pub.dev" 57 | source: hosted 58 | version: "0.4.0" 59 | fake_async: 60 | dependency: transitive 61 | description: 62 | name: fake_async 63 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 64 | url: "https://pub.dev" 65 | source: hosted 66 | version: "1.3.1" 67 | flutter: 68 | dependency: "direct main" 69 | description: flutter 70 | source: sdk 71 | version: "0.0.0" 72 | flutter_lints: 73 | dependency: "direct dev" 74 | description: 75 | name: flutter_lints 76 | sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" 77 | url: "https://pub.dev" 78 | source: hosted 79 | version: "2.0.2" 80 | flutter_test: 81 | dependency: "direct dev" 82 | description: flutter 83 | source: sdk 84 | version: "0.0.0" 85 | leak_tracker: 86 | dependency: transitive 87 | description: 88 | name: leak_tracker 89 | sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" 90 | url: "https://pub.dev" 91 | source: hosted 92 | version: "10.0.5" 93 | leak_tracker_flutter_testing: 94 | dependency: transitive 95 | description: 96 | name: leak_tracker_flutter_testing 97 | sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" 98 | url: "https://pub.dev" 99 | source: hosted 100 | version: "3.0.5" 101 | leak_tracker_testing: 102 | dependency: transitive 103 | description: 104 | name: leak_tracker_testing 105 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 106 | url: "https://pub.dev" 107 | source: hosted 108 | version: "3.0.1" 109 | lints: 110 | dependency: transitive 111 | description: 112 | name: lints 113 | sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" 114 | url: "https://pub.dev" 115 | source: hosted 116 | version: "2.1.1" 117 | matcher: 118 | dependency: transitive 119 | description: 120 | name: matcher 121 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 122 | url: "https://pub.dev" 123 | source: hosted 124 | version: "0.12.16+1" 125 | material_color_utilities: 126 | dependency: transitive 127 | description: 128 | name: material_color_utilities 129 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 130 | url: "https://pub.dev" 131 | source: hosted 132 | version: "0.11.1" 133 | meta: 134 | dependency: transitive 135 | description: 136 | name: meta 137 | sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 138 | url: "https://pub.dev" 139 | source: hosted 140 | version: "1.15.0" 141 | path: 142 | dependency: transitive 143 | description: 144 | name: path 145 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 146 | url: "https://pub.dev" 147 | source: hosted 148 | version: "1.9.0" 149 | sky_engine: 150 | dependency: transitive 151 | description: flutter 152 | source: sdk 153 | version: "0.0.99" 154 | source_span: 155 | dependency: transitive 156 | description: 157 | name: source_span 158 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 159 | url: "https://pub.dev" 160 | source: hosted 161 | version: "1.10.0" 162 | stack_trace: 163 | dependency: transitive 164 | description: 165 | name: stack_trace 166 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 167 | url: "https://pub.dev" 168 | source: hosted 169 | version: "1.11.1" 170 | stream_channel: 171 | dependency: transitive 172 | description: 173 | name: stream_channel 174 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 175 | url: "https://pub.dev" 176 | source: hosted 177 | version: "2.1.2" 178 | string_scanner: 179 | dependency: transitive 180 | description: 181 | name: string_scanner 182 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 183 | url: "https://pub.dev" 184 | source: hosted 185 | version: "1.2.0" 186 | term_glyph: 187 | dependency: transitive 188 | description: 189 | name: term_glyph 190 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 191 | url: "https://pub.dev" 192 | source: hosted 193 | version: "1.2.1" 194 | test_api: 195 | dependency: transitive 196 | description: 197 | name: test_api 198 | sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" 199 | url: "https://pub.dev" 200 | source: hosted 201 | version: "0.7.2" 202 | vector_math: 203 | dependency: transitive 204 | description: 205 | name: vector_math 206 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 207 | url: "https://pub.dev" 208 | source: hosted 209 | version: "2.1.4" 210 | vm_service: 211 | dependency: transitive 212 | description: 213 | name: vm_service 214 | sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" 215 | url: "https://pub.dev" 216 | source: hosted 217 | version: "14.2.5" 218 | sdks: 219 | dart: ">=3.3.0 <4.0.0" 220 | flutter: ">=3.18.0-18.0.pre.54" 221 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: Example for action_slider. 3 | 4 | publish_to: 'none' 5 | 6 | version: 1.0.0+1 7 | 8 | environment: 9 | sdk: ">=3.0.0 <4.0.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | 15 | action_slider: 16 | path: .. 17 | cross_fade: any 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | 23 | flutter_lints: ^2.0.2 24 | 25 | flutter: 26 | 27 | uses-material-design: true 28 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:example/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const ExampleApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/example/web/favicon.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/example/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/example/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | example 33 | 34 | 35 | 36 | 39 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /lib/action_slider.dart: -------------------------------------------------------------------------------- 1 | library action_slider; 2 | 3 | export 'package:action_slider/src/action_slider_widget.dart'; 4 | export 'package:action_slider/src/icons.dart'; 5 | -------------------------------------------------------------------------------- /lib/src/action_slider_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:action_slider/src/cross_fade.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/foundation.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | part 'conditional_wrapper.dart'; 9 | part 'cursors.dart'; 10 | part 'state.dart'; 11 | part 'status.dart'; 12 | part 'style.dart'; 13 | 14 | enum SliderBehavior { move, stretch } 15 | 16 | enum ThresholdType { 17 | /// The action is triggered as soon as the threshold is reached. 18 | /// The slider does not have to be released for this. 19 | instant, 20 | 21 | /// The action is triggered when the threshold is reached 22 | /// and the slider is released. 23 | release, 24 | } 25 | 26 | typedef BackgroundBuilder = Widget Function( 27 | BuildContext context, ActionSliderState state, Widget? child); 28 | typedef NullableForegroundBuilder = Widget? Function( 29 | BuildContext context, ActionSliderState state, Widget? child); 30 | typedef ForegroundBuilder = Widget Function( 31 | BuildContext context, ActionSliderState state, Widget? child); 32 | typedef SliderAction = Function(ActionSliderController controller); 33 | typedef StateChangeCallback = Function(ActionSliderState? oldState, 34 | ActionSliderState state, ActionSliderController controller); 35 | typedef TapCallback = Function( 36 | ActionSliderController controller, ActionSliderState state, double pos); 37 | 38 | BorderRadiusGeometry _subtractPaddingFromBorderRadius( 39 | BorderRadiusGeometry borderRadius, EdgeInsetsGeometry edgeInsets) { 40 | final subtractedBorderRadius = switch (edgeInsets) { 41 | EdgeInsets() => BorderRadius.only( 42 | topLeft: Radius.circular(min(edgeInsets.top, edgeInsets.left)), 43 | topRight: Radius.circular(min(edgeInsets.top, edgeInsets.right)), 44 | bottomLeft: Radius.circular(min(edgeInsets.bottom, edgeInsets.left)), 45 | bottomRight: Radius.circular(min(edgeInsets.bottom, edgeInsets.right)), 46 | ), 47 | EdgeInsetsDirectional() => BorderRadiusDirectional.only( 48 | topStart: Radius.circular(min(edgeInsets.top, edgeInsets.start)), 49 | topEnd: Radius.circular(min(edgeInsets.top, edgeInsets.end)), 50 | bottomStart: Radius.circular(min(edgeInsets.bottom, edgeInsets.start)), 51 | bottomEnd: Radius.circular(min(edgeInsets.bottom, edgeInsets.end)), 52 | ), 53 | _ => BorderRadius.zero, 54 | }; 55 | return borderRadius.subtract(subtractedBorderRadius); 56 | } 57 | 58 | /// Indicates the position of the [child] when using the [child] parameter of 59 | /// [ActionSlider.standard]. 60 | enum SliderChildPosition { 61 | /// No explicit positioning of the child by the package. 62 | /// 63 | /// This gives you full control. 64 | none, 65 | 66 | /// The [child] is positioned in the center of the slider. 67 | /// 68 | /// If you want to prevent overlapping with the toggle, you should use 69 | /// [centerWithPadding], [centerFreeArea], [centerFreeAreaWithPadding], 70 | /// [balanced] or [balancedWithPadding]. 71 | center, 72 | 73 | /// The [child] is positioned in the center of the slider with an additional 74 | /// padding for preventing overlapping with the toggle. 75 | /// 76 | /// If you want a smaller padding, you should use 77 | /// [center], [centerFreeArea], [centerFreeAreaWithPadding], [balanced] or 78 | /// [balancedWithPadding]. 79 | centerWithPadding, 80 | 81 | /// The child is positioned in the center of the free area of the slider. 82 | centerFreeArea, 83 | 84 | /// The child is positioned in the center of the free area of the slider with 85 | /// an additional padding for preventing getting clipped by the edge of the 86 | /// slider. 87 | centerFreeAreaWithPadding, 88 | 89 | /// A small [child] is positioned more in the center of the slider than a 90 | /// large [child] but it never overlaps with the toggle. 91 | balanced, 92 | 93 | /// [balanced] but with an extra padding for preventing getting clipped by the 94 | /// edge of the slider. 95 | balancedWithPadding, 96 | } 97 | 98 | /// The animation that is applied to the children when the toggle moves in 99 | /// [ActionSlider.standard] and [ActionSlider.dual]. 100 | enum SliderChildAnimation { 101 | /// No animation 102 | none, 103 | 104 | /// The child gets clipped so it disappears behind the toggle. 105 | clip, 106 | 107 | /// The child fades away while moving the toggle to the end. 108 | fade, 109 | 110 | /// Combination of [clip] and [fade]. 111 | clipAndFade, 112 | } 113 | 114 | /// The animation that is applied to the icon when the toggle moves in 115 | /// [ActionSlider.standard] and [ActionSlider.dual]. 116 | enum SliderIconAnimation { 117 | /// No animation 118 | none, 119 | 120 | /// The icon rolls when dragging the slider. 121 | roll, 122 | 123 | /// The icon turns by 180° while dragging the slider. 124 | turn, 125 | } 126 | 127 | class _LoadingIndicator extends StatelessWidget { 128 | const _LoadingIndicator(); 129 | 130 | @override 131 | Widget build(BuildContext context) { 132 | final theme = Theme.of(context); 133 | return switch (theme.platform) { 134 | TargetPlatform.iOS || 135 | TargetPlatform.macOS => 136 | CupertinoActivityIndicator(color: theme.iconTheme.color), 137 | _ => SizedBox.square( 138 | dimension: 24.0, 139 | child: CircularProgressIndicator( 140 | strokeWidth: 2.0, color: theme.iconTheme.color), 141 | ), 142 | }; 143 | } 144 | } 145 | 146 | class _DefaultToggleIcon extends StatelessWidget { 147 | const _DefaultToggleIcon(); 148 | 149 | @override 150 | Widget build(BuildContext context) { 151 | return Icon(switch (ActionSliderState.of(context).direction) { 152 | TextDirection.rtl => Icons.keyboard_arrow_left_rounded, 153 | TextDirection.ltr => Icons.keyboard_arrow_right_rounded, 154 | }); 155 | } 156 | } 157 | 158 | class _FixedValueListenable extends ValueListenable { 159 | @override 160 | final double value; 161 | 162 | const _FixedValueListenable(this.value); 163 | 164 | @override 165 | void addListener(VoidCallback listener) {} 166 | 167 | @override 168 | void removeListener(VoidCallback listener) {} 169 | } 170 | 171 | class ActionSliderController extends ChangeNotifier 172 | implements ValueListenable { 173 | ActionSliderControllerState _value; 174 | 175 | ActionSliderController({SliderStatus status = const SliderStatus.standard()}) 176 | : _value = ActionSliderControllerState._(status: status); 177 | 178 | @override 179 | ActionSliderControllerState get value => _value; 180 | 181 | /// Sets the status to success. 182 | /// 183 | /// {@macro action_slider.status.expanded} 184 | /// 185 | /// {@macro action_slider.status.highlighted} 186 | /// 187 | /// {@macro action_slider.status.side} 188 | void success({ 189 | bool expanded = false, 190 | SliderSide side = SliderSide.end, 191 | bool highlighted = true, 192 | }) => 193 | _setStatus(SliderStatus.success( 194 | expanded: expanded, side: side, highlighted: highlighted)); 195 | 196 | /// Sets the status to failure. 197 | /// 198 | /// {@macro action_slider.status.expanded} 199 | /// 200 | /// {@macro action_slider.status.highlighted} 201 | /// 202 | /// {@macro action_slider.status.side} 203 | void failure({ 204 | bool expanded = false, 205 | SliderSide side = SliderSide.end, 206 | bool highlighted = true, 207 | }) => 208 | _setStatus(SliderStatus.failure( 209 | expanded: expanded, side: side, highlighted: highlighted)); 210 | 211 | /// Sets the status to loading. 212 | /// 213 | /// {@macro action_slider.status.expanded} 214 | /// 215 | /// {@macro action_slider.status.highlighted} 216 | /// 217 | /// {@macro action_slider.status.side} 218 | void loading({ 219 | bool expanded = false, 220 | SliderSide side = SliderSide.end, 221 | bool highlighted = false, 222 | }) => 223 | _setStatus(SliderStatus.loading( 224 | expanded: expanded, side: side, highlighted: highlighted)); 225 | 226 | /// Resets the slider to its standard expanded status. 227 | /// 228 | /// {@macro action_slider.status.highlighted} 229 | void reset({bool highlighted = false}) => 230 | _setStatus(SliderStatus.standard(highlighted: highlighted)); 231 | 232 | ///The Toggle jumps to [anchorPosition] + [height]. 233 | /// 234 | ///[height] should be between [-1.0] and [1.0]. 235 | void jump([double height = 0.3]) { 236 | _value = _value._copyWith(jump: _SliderJump(height: height)); 237 | notifyListeners(); 238 | } 239 | 240 | ///Allows to set a custom [SliderStatus]. 241 | ///This is useful for other results like success or failure. 242 | /// 243 | ///You get this status in the [foregroundBuilder] of [ActionSlider.custom] or 244 | ///in the [customIconBuilder] of [ActionSlider.standard] and 245 | ///[ActionSlider.dual]. 246 | void custom(SliderStatus status) => _setStatus(status); 247 | 248 | void _setStatus(SliderStatus status, {bool notify = true}) { 249 | if (value.status == status) return; 250 | _value = _value._copyWith(status: status); 251 | if (notify) notifyListeners(); 252 | } 253 | } 254 | 255 | class ActionSlider extends StatefulWidget { 256 | ///The width of the sliding toggle. 257 | /// 258 | /// The default value is [height] - [toggleMargin.vertical] 259 | final double? toggleWidth; 260 | 261 | ///The margin of the sliding toggle. 262 | final EdgeInsetsGeometry toggleMargin; 263 | 264 | ///The margin of the sliding toggle when the current [SliderStatus] is a result 265 | ///like [SliderStatus.success] or [SliderStatus.failure]. 266 | final EdgeInsetsGeometry? resultToggleMargin; 267 | 268 | ///The total width of the widget. If this is [null] it uses the whole available width. 269 | final double? width; 270 | 271 | ///The total height of the widget. 272 | final double height; 273 | 274 | ///The child which is optionally given to the [outerBackgroundBuilder] for efficiency reasons. 275 | final Widget? outerBackgroundChild; 276 | 277 | ///The builder for outer background. Overwrites [backgroundColor], [borderRadius] and [boxShadow]. 278 | final BackgroundBuilder outerBackgroundBuilder; 279 | 280 | ///The child which is optionally given to the [backgroundBuilder] for efficiency reasons. 281 | final Widget? backgroundChild; 282 | 283 | ///The builder for the background of the toggle. 284 | final BackgroundBuilder? backgroundBuilder; 285 | 286 | ///The child which is optionally given to the [foregroundBuilder] for efficiency reasons. 287 | final Widget? foregroundChild; 288 | 289 | ///The builder for the toggle. 290 | final ForegroundBuilder foregroundBuilder; 291 | 292 | ///The [Duration] for the sliding animation when the user taps anywhere on the widget. 293 | final Duration slideAnimationDuration; 294 | 295 | ///The [Duration] for the toggle coming back after the user released it or after the sliding animation. 296 | final Duration reverseSlideAnimationDuration; 297 | 298 | ///The [Duration] for going into the loading status. 299 | final Duration sizeAnimationDuration; 300 | 301 | ///The [Duration] for changing the position of the anchor. 302 | final Duration anchorPositionDuration; 303 | 304 | ///The [Duration] for changing the [toggleMargin] and animating 305 | ///between [toggleMargin] and [resultToggleMargin]. 306 | final Duration toggleMarginDuration; 307 | 308 | ///The [Curve] for the sliding animation when the user taps anywhere on the widget. 309 | final Curve slideAnimationCurve; 310 | 311 | ///The [Curve] for the toggle coming back after the user released it or after the sliding animation. 312 | final Curve reverseSlideAnimationCurve; 313 | 314 | ///The [Curve] for going into the loading status. 315 | final Curve sizeAnimationCurve; 316 | 317 | ///The [Curve] for changing the position of the anchor. 318 | final Curve anchorPositionCurve; 319 | 320 | ///The [Curve] for changing the [toggleMargin] and animating 321 | ///between [toggleMargin] and [resultToggleMargin]. 322 | final Curve toggleMarginCurve; 323 | 324 | ///Callback for sliding completely to the right. 325 | ///Here you should call the loading, success and failure methods of the 326 | ///[controller] for controlling the further behavior/animations of the 327 | ///slider. 328 | final SliderAction? action; 329 | 330 | ///Callback when the [ActionSliderState] changes. 331 | ///With this you can define more individual behavior than with [action], if it is necessary. 332 | final StateChangeCallback? stateChangeCallback; 333 | 334 | ///Callback for tapping on the [ActionSlider]. 335 | ///Defaults to a jump in the direction of the tap position. 336 | /// 337 | ///This is only called if the toggle is currently not dragged. 338 | ///If you want onTap to be called in any case, you should wrap the slider 339 | ///in a [GestureDetector]. 340 | final TapCallback? onTap; 341 | 342 | ///Controller for controlling the widget from everywhere. 343 | final ActionSliderController? controller; 344 | 345 | ///This [SliderBehavior] defines the behavior when moving the toggle. 346 | final SliderBehavior sliderBehavior; 347 | 348 | ///The threshold at which the action should be triggered. Should be between 0.0 and 1.0. 349 | final double actionThreshold; 350 | 351 | ///The [ThresholdType] of the [actionThreshold]. 352 | final ThresholdType actionThresholdType; 353 | 354 | ///The direction of the slider. 355 | /// 356 | /// If set to [null], the [TextDirection] is fetched from the [BuildContext]. 357 | final TextDirection? direction; 358 | 359 | final ActionSliderController Function() _defaultControllerBuilder; 360 | 361 | /// The default position of the toggle between [0.0] and [1.0]. 362 | final double anchorPosition; 363 | 364 | /// The interval in which the toggle of the slider can be moved by the user. 365 | final SliderInterval allowedInterval; 366 | 367 | /// Sets the [SliderStatus] of this slider. 368 | /// 369 | /// When this value is set, the status changes of the controller are ignored. 370 | final SliderStatus? status; 371 | 372 | final SliderCursors cursors; 373 | 374 | /// Constructor with maximum customizability. 375 | const ActionSlider.custom({ 376 | super.key, 377 | this.status, 378 | required this.outerBackgroundBuilder, 379 | this.backgroundBuilder, 380 | required this.foregroundBuilder, 381 | this.toggleWidth, 382 | this.toggleMargin = const EdgeInsets.all(5.0), 383 | this.height = 65.0, 384 | this.outerBackgroundChild, 385 | this.backgroundChild, 386 | this.foregroundChild, 387 | this.action, 388 | this.controller, 389 | this.sizeAnimationDuration = const Duration(milliseconds: 350), 390 | this.width, 391 | this.slideAnimationDuration = const Duration(milliseconds: 250), 392 | this.reverseSlideAnimationDuration = const Duration(milliseconds: 1000), 393 | this.anchorPositionDuration = const Duration(milliseconds: 150), 394 | this.slideAnimationCurve = Curves.decelerate, 395 | this.reverseSlideAnimationCurve = Curves.bounceIn, 396 | this.sizeAnimationCurve = Curves.easeInOut, 397 | this.anchorPositionCurve = Curves.linear, 398 | this.toggleMarginCurve = Curves.easeInOut, 399 | this.toggleMarginDuration = const Duration(milliseconds: 350), 400 | this.sliderBehavior = SliderBehavior.move, 401 | this.onTap = _defaultOnTap, 402 | this.actionThreshold = 1.0, 403 | this.actionThresholdType = ThresholdType.instant, 404 | this.stateChangeCallback, 405 | this.direction = TextDirection.ltr, 406 | this.resultToggleMargin, 407 | this.anchorPosition = 0.0, 408 | this.allowedInterval = const SliderInterval(), 409 | this.cursors = const SliderCursors(), 410 | }) : _defaultControllerBuilder = _controllerBuilder; 411 | 412 | static _defaultOnTap( 413 | ActionSliderController c, ActionSliderState state, double pos) => 414 | c.jump(pos < state.anchorPosition ? -0.3 : 0.3); 415 | 416 | static ActionSliderController _controllerBuilder() => 417 | ActionSliderController(); 418 | 419 | ///Standard constructor for creating a slider. 420 | /// 421 | /// {@template action_slider.constructor.standard.builders} 422 | ///[customIconBuilder] is useful if you use your own [SliderStatus]s and need icons additional to [successIcon], [failureIcon], [loadingIcon] and [icon]. 423 | ///You can also use [customIconBuilderChild] with the [customIconBuilder] for efficiency reasons. 424 | /// 425 | ///If [customBackgroundBuilder] is not null, the value of [child] is ignored. 426 | ///You can also use [customBackgroundBuilderChild] with the [customBackgroundBuilder] for efficiency reasons. 427 | /// 428 | ///If [customOuterBackgroundBuilder] is not null, the values of [backgroundColor], [backgroundBorderRadius] and [boxShadow] are ignored. 429 | ///You can also use [customOuterBackgroundBuilderChild] with the [customOuterBackgroundBuilder] for efficiency reasons. 430 | /// 431 | /// {@endtemplate} 432 | /// {@template action_slider.constructor.standard.icons} 433 | /// [icon] is the icon which is shown when [status] is a [SliderStatusStandard]. 434 | /// 435 | /// [loadingIcon] is the icon which is shown when [status] is a [SliderStatusLoading]. 436 | /// 437 | /// [successIcon] is the icon which is shown when [status] is a [SliderStatusSuccess]. 438 | /// 439 | /// [failureIcon] is the icon which is shown when [status] is a [SliderStatusFailure]. 440 | /// 441 | /// For overriding the icons or supporting a custom [SliderStatus], you can implement a [customIconBuilder]. 442 | /// You can also use [customIconBuilderChild] for improving performance of [customIconBuilder] if possible. 443 | /// {@endtemplate} 444 | ActionSlider.standard({ 445 | super.key, 446 | this.status, 447 | SliderStyle style = const SliderStyle(), 448 | Widget? child, 449 | Widget loadingIcon = const _LoadingIndicator(), 450 | Widget successIcon = const Icon(Icons.check_rounded), 451 | Widget failureIcon = const Icon(Icons.close_rounded), 452 | Widget icon = const _DefaultToggleIcon(), 453 | NullableForegroundBuilder? customIconBuilder, 454 | Widget? customIconBuilderChild, 455 | BackgroundBuilder? customBackgroundBuilder, 456 | Widget? customBackgroundBuilderChild, 457 | BackgroundBuilder? customOuterBackgroundBuilder, 458 | Widget? customOuterBackgroundBuilderChild, 459 | this.height = 65.0, 460 | double borderWidth = 5.0, 461 | double? resultBorderWidth, 462 | SliderIconAnimation iconAnimation = SliderIconAnimation.none, 463 | SliderChildAnimation childAnimation = SliderChildAnimation.clip, 464 | this.action, 465 | this.onTap = _defaultOnTap, 466 | this.controller, 467 | this.width, 468 | this.slideAnimationDuration = const Duration(milliseconds: 250), 469 | this.reverseSlideAnimationDuration = const Duration(milliseconds: 1000), 470 | this.anchorPositionDuration = const Duration(milliseconds: 150), 471 | this.sizeAnimationDuration = const Duration(milliseconds: 350), 472 | Duration crossFadeDuration = const Duration(milliseconds: 250), 473 | this.slideAnimationCurve = Curves.decelerate, 474 | this.reverseSlideAnimationCurve = Curves.bounceIn, 475 | this.sizeAnimationCurve = Curves.easeInOut, 476 | this.anchorPositionCurve = Curves.linear, 477 | AlignmentGeometry iconAlignment = Alignment.center, 478 | BorderRadiusGeometry? foregroundBorderRadius, 479 | this.sliderBehavior = SliderBehavior.move, 480 | this.actionThreshold = 1.0, 481 | this.actionThresholdType = ThresholdType.instant, 482 | this.stateChangeCallback, 483 | this.direction = TextDirection.ltr, 484 | this.toggleMarginCurve = Curves.easeInOut, 485 | this.toggleMarginDuration = const Duration(milliseconds: 350), 486 | this.anchorPosition = 0.0, 487 | this.allowedInterval = const SliderInterval(), 488 | SliderChildPosition childPosition = SliderChildPosition.balanced, 489 | this.toggleWidth, 490 | this.cursors = const SliderCursors(), 491 | }) : backgroundChild = customBackgroundBuilderChild, 492 | backgroundBuilder = (customBackgroundBuilder ?? 493 | (context, state, _) => _standardBackgroundBuilder( 494 | context, state, child, childPosition, childAnimation)), 495 | foregroundBuilder = 496 | ((context, state, child) => _standardForegroundBuilder( 497 | context, 498 | state, 499 | iconAnimation, 500 | icon, 501 | loadingIcon, 502 | successIcon, 503 | failureIcon, 504 | customIconBuilder, 505 | customIconBuilderChild, 506 | iconAlignment, 507 | crossFadeDuration, 508 | style, 509 | )), 510 | outerBackgroundBuilder = customOuterBackgroundBuilder ?? 511 | ((context, state, child) => 512 | _standardOuterBackgroundBuilder(context, state, child, style)), 513 | outerBackgroundChild = customOuterBackgroundBuilderChild, 514 | toggleMargin = EdgeInsets.all(borderWidth), 515 | resultToggleMargin = resultBorderWidth == null 516 | ? null 517 | : EdgeInsets.all(resultBorderWidth), 518 | foregroundChild = null, 519 | _defaultControllerBuilder = _controllerBuilder; 520 | 521 | ///Standard constructor for creating a dual slider. 522 | /// 523 | /// {@macro action_slider.constructor.standard.builders} 524 | /// 525 | /// {@macro action_slider.constructor.standard.icons} 526 | ActionSlider.dual({ 527 | super.key, 528 | this.status, 529 | SliderStyle style = const SliderStyle(), 530 | Widget? startChild, 531 | Widget? endChild, 532 | Widget loadingIcon = const _LoadingIndicator(), 533 | Widget successIcon = const Icon(Icons.check_rounded), 534 | Widget failureIcon = const Icon(Icons.close_rounded), 535 | Widget icon = const _DefaultToggleIcon(), 536 | ForegroundBuilder? customIconBuilder, 537 | Widget? customIconBuilderChild, 538 | BackgroundBuilder? customBackgroundBuilder, 539 | Widget? customBackgroundBuilderChild, 540 | BackgroundBuilder? customOuterBackgroundBuilder, 541 | Widget? customOuterBackgroundBuilderChild, 542 | this.height = 65.0, 543 | double borderWidth = 5.0, 544 | double? resultBorderWidth, 545 | SliderIconAnimation iconAnimation = SliderIconAnimation.none, 546 | SliderAction? startAction, 547 | SliderAction? endAction, 548 | this.onTap = _defaultOnTap, 549 | this.controller, 550 | this.width, 551 | this.slideAnimationDuration = const Duration(milliseconds: 250), 552 | this.reverseSlideAnimationDuration = const Duration(milliseconds: 1000), 553 | this.anchorPositionDuration = const Duration(milliseconds: 150), 554 | this.sizeAnimationDuration = const Duration(milliseconds: 350), 555 | Duration crossFadeDuration = const Duration(milliseconds: 250), 556 | this.slideAnimationCurve = Curves.decelerate, 557 | this.reverseSlideAnimationCurve = Curves.bounceIn, 558 | this.sizeAnimationCurve = Curves.easeInOut, 559 | this.anchorPositionCurve = Curves.linear, 560 | AlignmentGeometry iconAlignment = Alignment.center, 561 | BorderRadiusGeometry? foregroundBorderRadius, 562 | this.sliderBehavior = SliderBehavior.move, 563 | double startActionThreshold = 0.0, 564 | double endActionThreshold = 1.0, 565 | this.actionThresholdType = ThresholdType.instant, 566 | StateChangeCallback? stateChangeCallback, 567 | this.direction = TextDirection.ltr, 568 | this.toggleMarginCurve = Curves.easeInOut, 569 | this.toggleMarginDuration = const Duration(milliseconds: 350), 570 | this.anchorPosition = 0.5, 571 | this.allowedInterval = const SliderInterval(), 572 | this.toggleWidth, 573 | SliderChildAnimation childAnimation = SliderChildAnimation.clip, 574 | this.cursors = const SliderCursors(), 575 | }) : stateChangeCallback = _dualChangeCallback( 576 | startAction, 577 | endAction, 578 | stateChangeCallback, 579 | actionThresholdType, 580 | startActionThreshold, 581 | endActionThreshold), 582 | backgroundChild = customBackgroundBuilderChild, 583 | backgroundBuilder = (customBackgroundBuilder ?? 584 | (context, state, _) => _standardDualBackgroundBuilder( 585 | context, 586 | state, 587 | startChild, 588 | endChild, 589 | childAnimation, 590 | )), 591 | foregroundBuilder = 592 | ((context, state, child) => _standardForegroundBuilder( 593 | context, 594 | state, 595 | iconAnimation, 596 | icon, 597 | loadingIcon, 598 | successIcon, 599 | failureIcon, 600 | customIconBuilder, 601 | customIconBuilderChild, 602 | iconAlignment, 603 | crossFadeDuration, 604 | style, 605 | )), 606 | outerBackgroundBuilder = customOuterBackgroundBuilder ?? 607 | ((context, state, child) => 608 | _standardOuterBackgroundBuilder(context, state, child, style)), 609 | outerBackgroundChild = customOuterBackgroundBuilderChild, 610 | toggleMargin = EdgeInsets.all(borderWidth), 611 | resultToggleMargin = resultBorderWidth == null 612 | ? null 613 | : EdgeInsets.all(resultBorderWidth), 614 | foregroundChild = null, 615 | actionThreshold = 1.0, 616 | action = null, 617 | _defaultControllerBuilder = _controllerBuilder; 618 | 619 | static StateChangeCallback _dualChangeCallback( 620 | SliderAction? startAction, 621 | SliderAction? endAction, 622 | StateChangeCallback? callback, 623 | ThresholdType thresholdType, 624 | double startThreshold, 625 | double endThreshold) { 626 | return (ActionSliderState? oldState, ActionSliderState state, 627 | ActionSliderController controller) { 628 | if (oldState?.position != state.position || 629 | oldState?.slidingStatus != state.slidingStatus) { 630 | switch (thresholdType) { 631 | case ThresholdType.instant: 632 | if (state.slidingStatus != SlidingStatus.dragged) break; 633 | if (state.position <= startThreshold) { 634 | startAction?.call(controller); 635 | } else if (state.position >= endThreshold) { 636 | endAction?.call(controller); 637 | } 638 | break; 639 | case ThresholdType.release: 640 | if (oldState?.slidingStatus == state.slidingStatus || 641 | state.slidingStatus != SlidingStatus.released) break; 642 | if (state.position <= startThreshold) { 643 | startAction?.call(controller); 644 | } else if (state.position >= endThreshold) { 645 | endAction?.call(controller); 646 | } 647 | break; 648 | } 649 | } 650 | callback?.call(oldState, state, controller); 651 | }; 652 | } 653 | 654 | static Widget _standardOuterBackgroundBuilder(BuildContext context, 655 | ActionSliderState state, Widget? child, SliderStyle style) { 656 | style = SliderStyle.maybeOf(context)?.merge(style) ?? style; 657 | 658 | final theme = Theme.of(context); 659 | final borderRadius = 660 | style.borderRadius ?? BorderRadius.circular(state.size.height / 2); 661 | 662 | return DecoratedBox( 663 | decoration: BoxDecoration( 664 | color: style.backgroundGradient != null 665 | ? null 666 | : (style.backgroundColor ?? theme.colorScheme.surface), 667 | borderRadius: borderRadius, 668 | gradient: style.backgroundGradient, 669 | boxShadow: style.boxShadow, 670 | ), 671 | ); 672 | } 673 | 674 | static Widget _standardBackgroundBuilder( 675 | BuildContext context, 676 | ActionSliderState state, 677 | Widget? child, 678 | SliderChildPosition childPosition, 679 | SliderChildAnimation childAnimation) { 680 | Alignment clipAlignment = state.direction == TextDirection.rtl 681 | ? Alignment.centerLeft 682 | : Alignment.centerRight; 683 | 684 | final innerSize = state.stretchedInnerSize; 685 | final toggleSize = state.standardToggleSize; 686 | 687 | final Clip clipBehavior = switch (childAnimation) { 688 | SliderChildAnimation.clip || 689 | SliderChildAnimation.clipAndFade => 690 | Clip.hardEdge, 691 | _ => Clip.none, 692 | }; 693 | 694 | final bool fading = switch (childAnimation) { 695 | SliderChildAnimation.fade || SliderChildAnimation.clipAndFade => true, 696 | _ => false, 697 | }; 698 | 699 | return Opacity( 700 | opacity: fading ? 1.0 - state.position : 1.0, 701 | child: ClipRect( 702 | clipBehavior: clipBehavior, 703 | child: OverflowBox( 704 | maxWidth: innerSize.width, 705 | maxHeight: innerSize.height, 706 | minWidth: innerSize.width, 707 | minHeight: innerSize.height, 708 | child: Align( 709 | alignment: clipAlignment, 710 | child: ClipRect( 711 | clipBehavior: clipBehavior, 712 | child: Align( 713 | alignment: clipAlignment, 714 | widthFactor: (toggleSize.width / (2 * innerSize.width)) + 715 | (1 - state.position) * 716 | ((innerSize.width - toggleSize.width) / 717 | innerSize.width), 718 | child: switch (childPosition) { 719 | SliderChildPosition.none => child, 720 | SliderChildPosition.center => Center(child: child), 721 | SliderChildPosition.centerWithPadding => Padding( 722 | padding: 723 | EdgeInsets.symmetric(horizontal: toggleSize.width), 724 | child: Center(child: child), 725 | ), 726 | SliderChildPosition.centerFreeArea => Padding( 727 | padding: 728 | EdgeInsetsDirectional.only(start: toggleSize.width) 729 | .resolve(state.direction), 730 | child: Center(child: child), 731 | ), 732 | SliderChildPosition.centerFreeAreaWithPadding => Padding( 733 | padding: EdgeInsetsDirectional.only( 734 | start: toggleSize.width, 735 | end: toggleSize.width / 2) 736 | .resolve(state.direction), 737 | child: Center(child: child), 738 | ), 739 | SliderChildPosition.balanced => Center( 740 | child: FractionalTranslation( 741 | translation: Offset( 742 | (state.direction == TextDirection.rtl 743 | ? -1.0 744 | : 1.0) * 745 | (toggleSize.width / 2) / 746 | (innerSize.width - toggleSize.width), 747 | 0.0), 748 | child: ConstrainedBox( 749 | constraints: BoxConstraints( 750 | maxWidth: innerSize.width - toggleSize.width), 751 | child: child, 752 | ), 753 | ), 754 | ), 755 | SliderChildPosition.balancedWithPadding => Padding( 756 | padding: EdgeInsets.symmetric( 757 | horizontal: toggleSize.width / 2), 758 | child: Center( 759 | child: FractionalTranslation( 760 | translation: Offset( 761 | (state.direction == TextDirection.rtl 762 | ? -1.0 763 | : 1.0) * 764 | (toggleSize.width / 4) / 765 | (innerSize.width - toggleSize.width * 1.5), 766 | 0.0), 767 | child: ConstrainedBox( 768 | constraints: BoxConstraints( 769 | maxWidth: 770 | innerSize.width - toggleSize.width * 1.5), 771 | child: child, 772 | ), 773 | ), 774 | ), 775 | ), 776 | }, 777 | ), 778 | ), 779 | ), 780 | ), 781 | ), 782 | ); 783 | } 784 | 785 | static Widget _standardDualBackgroundBuilder( 786 | BuildContext context, 787 | ActionSliderState state, 788 | Widget? startChild, 789 | Widget? endChild, 790 | SliderChildAnimation childAnimation) { 791 | Alignment startAlignment = 792 | AlignmentDirectional.centerStart.resolve(state.direction); 793 | Alignment endAlignment = 794 | AlignmentDirectional.centerEnd.resolve(state.direction); 795 | double innerWidth = state.standardSize.width - 796 | state.standardToggleSize.width - 797 | state.toggleMargin.horizontal; 798 | final startSize = Size( 799 | innerWidth * state.anchorPosition + state.standardToggleSize.width / 2, 800 | state.standardToggleSize.height); 801 | final endSize = Size( 802 | state.standardSize.width - 803 | state.toggleMargin.horizontal - 804 | startSize.width, 805 | state.standardToggleSize.height); 806 | final Clip clipBehavior = switch (childAnimation) { 807 | SliderChildAnimation.clip || 808 | SliderChildAnimation.clipAndFade => 809 | Clip.hardEdge, 810 | _ => Clip.none, 811 | }; 812 | 813 | final bool fading = switch (childAnimation) { 814 | SliderChildAnimation.fade || SliderChildAnimation.clipAndFade => true, 815 | _ => false, 816 | }; 817 | return Row( 818 | textDirection: state.direction, 819 | children: [ 820 | Opacity( 821 | opacity: fading 822 | ? (state.position / state.anchorPosition).clamp(0.0, 1.0) 823 | : 1.0, 824 | child: SizedBox( 825 | width: (state.size.width - 826 | state.standardToggleSize.width - 827 | state.toggleMargin.horizontal) * 828 | state.anchorPosition + 829 | state.standardToggleSize.width / 2, 830 | child: ClipRect( 831 | clipBehavior: clipBehavior, 832 | child: OverflowBox( 833 | maxWidth: startSize.width, 834 | maxHeight: startSize.height, 835 | minWidth: startSize.width, 836 | minHeight: startSize.height, 837 | child: Align( 838 | alignment: startAlignment, 839 | child: ClipRect( 840 | child: Align( 841 | alignment: startAlignment, 842 | widthFactor: 1.0 - 843 | ((1.0 - state.position / state.anchorPosition) * 844 | (1.0 - 845 | 0.5 * 846 | state.standardToggleSize.width / 847 | startSize.width)) 848 | .clamp(0.0, 1.0), 849 | child: Padding( 850 | padding: EdgeInsetsDirectional.only( 851 | end: state.standardToggleSize.width / 2) 852 | .resolve(state.direction), 853 | child: Center(child: startChild), 854 | ), 855 | ), 856 | ), 857 | ), 858 | ), 859 | ), 860 | ), 861 | ), 862 | Expanded( 863 | child: Opacity( 864 | opacity: fading 865 | ? (1.0 - 866 | (state.position - state.anchorPosition) / 867 | (1.0 - state.anchorPosition)) 868 | .clamp(0.0, 1.0) 869 | : 1.0, 870 | child: ClipRect( 871 | clipBehavior: clipBehavior, 872 | child: OverflowBox( 873 | maxWidth: endSize.width, 874 | maxHeight: endSize.height, 875 | minWidth: endSize.width, 876 | minHeight: endSize.height, 877 | child: Align( 878 | alignment: endAlignment, 879 | child: ClipRect( 880 | child: Align( 881 | alignment: endAlignment, 882 | widthFactor: 1.0 - 883 | (((state.position - state.anchorPosition) / 884 | (1.0 - state.anchorPosition)) * 885 | (1.0 - 886 | 0.5 * 887 | state.standardToggleSize.width / 888 | endSize.width)) 889 | .clamp(0.0, 1.0), 890 | child: Padding( 891 | padding: EdgeInsetsDirectional.only( 892 | start: state.standardToggleSize.width / 2) 893 | .resolve(state.direction), 894 | child: Center(child: endChild), 895 | ), 896 | ), 897 | ), 898 | ), 899 | ), 900 | ), 901 | ), 902 | ), 903 | ], 904 | ); 905 | } 906 | 907 | static Widget _standardForegroundBuilder( 908 | BuildContext context, 909 | ActionSliderState state, 910 | SliderIconAnimation iconAnimation, 911 | Widget icon, 912 | Widget loadingIcon, 913 | Widget successIcon, 914 | Widget failureIcon, 915 | NullableForegroundBuilder? customIconBuilder, 916 | Widget? customIconBuilderChild, 917 | AlignmentGeometry iconAlignment, 918 | Duration crossFadeDuration, 919 | SliderStyle style, 920 | ) { 921 | style = SliderStyle.maybeOf(context)?.merge(style) ?? style; 922 | 923 | final theme = Theme.of(context); 924 | double radius = state.size.height / 2; 925 | 926 | final borderRadius = style.borderRadius ?? BorderRadius.circular(radius); 927 | 928 | final toggleBorderRadius = style.toggleBorderRadius ?? 929 | _subtractPaddingFromBorderRadius(borderRadius, state.toggleMargin); 930 | 931 | return DecoratedBox( 932 | decoration: BoxDecoration( 933 | boxShadow: style.toggleBoxShadow, 934 | borderRadius: toggleBorderRadius, 935 | gradient: style.toggleGradient, 936 | color: style.toggleGradient != null 937 | ? null 938 | : style.toggleColor ?? theme.primaryColor, 939 | ), 940 | child: SliderCrossFade( 941 | duration: crossFadeDuration * (1 / 0.3), 942 | current: state.status, 943 | builder: (context, status) { 944 | final customIcon = customIconBuilder?.call( 945 | context, 946 | state, 947 | customIconBuilderChild, 948 | ); 949 | icon = customIcon ?? icon; 950 | Widget child = switch (status) { 951 | SliderStatusLoading() => customIcon ?? loadingIcon, 952 | SliderStatusFailure() => customIcon ?? failureIcon, 953 | SliderStatusSuccess() => customIcon ?? successIcon, 954 | SliderStatusStandard() => switch (iconAnimation) { 955 | SliderIconAnimation.roll => Transform.rotate( 956 | angle: ((state.size.width * state.position) - 957 | state.size.width * state.anchorPosition) / 958 | radius, 959 | child: icon, 960 | ), 961 | SliderIconAnimation.turn => Transform.rotate( 962 | angle: state.position * -pi, 963 | child: icon, 964 | ), 965 | SliderIconAnimation.none => icon, 966 | }, 967 | _ => customIcon ?? 968 | (throw StateError('For using a custom SliderStatus you have to ' 969 | 'return something in customIconBuilder!')), 970 | }; 971 | return Align(alignment: iconAlignment, child: child); 972 | }, 973 | size: (s1, s2) => s2.highlighted, 974 | ), 975 | ); 976 | } 977 | 978 | @override 979 | State createState() => _ActionSliderState(); 980 | } 981 | 982 | class _ActionSliderState extends State 983 | with TickerProviderStateMixin { 984 | late final AnimationController _slideAnimationController; 985 | late final AnimationController _anchorController; 986 | late final CurvedAnimation _slideAnimation; 987 | late final CurvedAnimation _anchorCurvedAnimation; 988 | late final Animation _anchorAnimation; 989 | late final Tween _anchorAnimationTween; 990 | ActionSliderController? _localController; 991 | ActionSliderState? _lastActionSliderState; 992 | 993 | ActionSliderController get _controller => 994 | widget.controller ?? _localController!; 995 | late SliderState _state; 996 | 997 | /// The start position of the current running [_slideAnimation]. 998 | late ValueListenable _startPosition; 999 | 1000 | late _SliderJump _lastJump; 1001 | 1002 | @override 1003 | void initState() { 1004 | super.initState(); 1005 | if (widget.controller == null) { 1006 | _localController = widget._defaultControllerBuilder(); 1007 | } 1008 | _controller.addListener(_onControllerStateChange); 1009 | final initialStatus = widget.status ?? _controller.value.status; 1010 | _state = SliderState( 1011 | status: initialStatus, 1012 | position: switch (initialStatus) { 1013 | SliderStatusStandard() => widget.anchorPosition, 1014 | SliderStatusResult() => initialStatus.side._position, 1015 | }, 1016 | anchorPosition: widget.anchorPosition, 1017 | slidingStatus: SlidingStatus.released, 1018 | ); 1019 | _lastJump = _controller.value._jump; 1020 | 1021 | _slideAnimationController = AnimationController( 1022 | vsync: this, 1023 | duration: widget.slideAnimationDuration, 1024 | reverseDuration: widget.reverseSlideAnimationDuration); 1025 | _slideAnimation = CurvedAnimation( 1026 | parent: _slideAnimationController, 1027 | curve: widget.slideAnimationCurve, 1028 | reverseCurve: widget.reverseSlideAnimationCurve); 1029 | _slideAnimation.addListener(() { 1030 | _changeState(_updatePosition(), null, 1031 | setState: _state.slidingStatus != SlidingStatus.dragged); 1032 | }); 1033 | _slideAnimation.addStatusListener((status) { 1034 | if (status == AnimationStatus.completed && 1035 | _controller.value.status is SliderStatusStandard) { 1036 | _dropSlider(); 1037 | } 1038 | }); 1039 | 1040 | _anchorController = AnimationController( 1041 | vsync: this, 1042 | duration: widget.anchorPositionDuration, 1043 | value: widget.anchorPosition, 1044 | ); 1045 | _startPosition = _anchorAnimation = (_anchorAnimationTween = 1046 | Tween(begin: widget.anchorPosition, end: widget.anchorPosition)) 1047 | .animate(_anchorCurvedAnimation = CurvedAnimation( 1048 | parent: _anchorController, 1049 | curve: widget.anchorPositionCurve, 1050 | )); 1051 | _anchorController.addListener(() => _changeState(_updatePosition(), null)); 1052 | } 1053 | 1054 | SliderState _updatePosition({SlidingStatus? state}) { 1055 | return _state.copyWith( 1056 | anchorPosition: _anchorAnimation.value, 1057 | position: _state.slidingStatus == SlidingStatus.dragged 1058 | ? null 1059 | : _startPosition.value + 1060 | _slideAnimation.value * 1061 | (_state.releasePosition - _startPosition.value), 1062 | slidingStatus: state, 1063 | ); 1064 | } 1065 | 1066 | @override 1067 | void dispose() { 1068 | _anchorController.dispose(); 1069 | _slideAnimationController.dispose(); 1070 | _controller.removeListener(_onControllerStateChange); 1071 | _localController?.dispose(); 1072 | super.dispose(); 1073 | } 1074 | 1075 | @override 1076 | void didUpdateWidget(covariant ActionSlider oldWidget) { 1077 | super.didUpdateWidget(oldWidget); 1078 | if (oldWidget.controller != widget.controller) { 1079 | _localController?.dispose(); 1080 | oldWidget.controller?.removeListener(_onControllerStateChange); 1081 | if (widget.controller == null) { 1082 | _localController = ActionSliderController(); 1083 | } else { 1084 | _localController = null; 1085 | } 1086 | _controller.addListener(_onControllerStateChange); 1087 | _lastJump = _controller.value._jump; 1088 | _onControllerStateChange(); 1089 | } 1090 | _slideAnimationController.duration = widget.slideAnimationDuration; 1091 | _slideAnimationController.reverseDuration = 1092 | widget.reverseSlideAnimationDuration; 1093 | _slideAnimation.curve = widget.slideAnimationCurve; 1094 | _slideAnimation.reverseCurve = widget.reverseSlideAnimationCurve; 1095 | _anchorCurvedAnimation.curve = widget.anchorPositionCurve; 1096 | _anchorController.duration = widget.anchorPositionDuration; 1097 | 1098 | if (oldWidget.anchorPosition != widget.anchorPosition) { 1099 | _anchorAnimationTween.begin = _anchorAnimation.value; 1100 | _anchorAnimationTween.end = widget.anchorPosition; 1101 | _anchorController.forward(from: 0.0); 1102 | _changeState(_updatePosition(), null, setState: false); 1103 | } 1104 | 1105 | if (widget.status != null) { 1106 | _updateStatus(widget.status!); 1107 | } else { 1108 | _updateStatus(_controller.value.status); 1109 | } 1110 | } 1111 | 1112 | void _onControllerStateChange() { 1113 | final controllerValue = _controller.value; 1114 | if (controllerValue._jump != _lastJump) { 1115 | if (_state.slidingStatus == SlidingStatus.released) { 1116 | _lastJump = controllerValue._jump; 1117 | _animateSliderTo( 1118 | _state.anchorPosition + controllerValue._jump.height, _state); 1119 | } 1120 | } 1121 | if (widget.status == null) _updateStatus(_controller.value.status); 1122 | } 1123 | 1124 | void _updateStatus(SliderStatus status) { 1125 | if (_state.status != status) { 1126 | if (status.expanded) { 1127 | if (status is SliderStatusResult) { 1128 | _animateSliderTo( 1129 | status.side._position, 1130 | _updatePosition(state: SlidingStatus.fixed) 1131 | .copyWith(status: status)); 1132 | } else { 1133 | if (_lastActionSliderState?.relativeSize != 0.0) { 1134 | _dropSlider(status: status); 1135 | } else { 1136 | _slideAnimationController.value = 0.0; 1137 | _changeState( 1138 | _state.copyWith( 1139 | anchorPosition: _state.anchorPosition, 1140 | position: _state.anchorPosition, 1141 | slidingStatus: SlidingStatus.released, 1142 | status: status, 1143 | ), 1144 | null); 1145 | _anchorAnimationTween.begin = widget.anchorPosition; 1146 | _anchorAnimationTween.end = widget.anchorPosition; 1147 | _anchorController.stop(); 1148 | } 1149 | } 1150 | } else { 1151 | _slideAnimationController.stop(); 1152 | _changeState( 1153 | _state = _state.copyWith( 1154 | releasePosition: _state.position, 1155 | slidingStatus: SlidingStatus.compact, 1156 | status: status, 1157 | ), 1158 | null); 1159 | } 1160 | } 1161 | } 1162 | 1163 | void _animateSliderTo(double position, SliderState state, 1164 | {SlidingStatus? slidingState}) { 1165 | position = position.clamp(0.0, 1.0); 1166 | _startPosition = _FixedValueListenable(state.position); 1167 | if (_slideAnimationController.status == AnimationStatus.forward && 1168 | state.releasePosition == position) { 1169 | _changeState(state.copyWith(slidingStatus: slidingState), null, 1170 | setState: false); 1171 | return; 1172 | } 1173 | _changeState( 1174 | state.copyWith(releasePosition: position, slidingStatus: slidingState), 1175 | null, 1176 | setState: false); 1177 | if (position == state.position && 1178 | state.slidingStatus == SlidingStatus.fixed) { 1179 | _slideAnimationController.value = 1.0; 1180 | return; 1181 | } 1182 | _slideAnimationController.forward(from: 0.0); 1183 | } 1184 | 1185 | void _dropSlider({SliderStatus? status}) { 1186 | _startPosition = _anchorAnimation; 1187 | _changeState( 1188 | _state.copyWith( 1189 | releasePosition: _state.position, 1190 | slidingStatus: SlidingStatus.released, 1191 | status: status, 1192 | ), 1193 | null, 1194 | setState: false); 1195 | _slideAnimationController.reverse(from: 1.0); 1196 | } 1197 | 1198 | void _changeState(SliderState state, ActionSliderState? oldActionSliderState, 1199 | {bool setState = true}) { 1200 | _state = state; 1201 | if (setState) this.setState(() {}); 1202 | oldActionSliderState ??= _lastActionSliderState; 1203 | if (oldActionSliderState == null) return; 1204 | final actionSliderState = ActionSliderState( 1205 | position: _state.position, 1206 | size: oldActionSliderState.size, 1207 | standardSize: oldActionSliderState.standardSize, 1208 | slidingStatus: _state.slidingStatus, 1209 | status: _state.status, 1210 | anchorPosition: _state.anchorPosition, 1211 | releasePosition: _state.releasePosition, 1212 | dragStartPosition: _state.dragStartPosition, 1213 | allowedInterval: widget.allowedInterval, 1214 | toggleSize: oldActionSliderState.toggleSize, 1215 | direction: oldActionSliderState.direction, 1216 | toggleMargin: oldActionSliderState.toggleMargin, 1217 | relativeSize: oldActionSliderState.relativeSize, 1218 | standardToggleSize: oldActionSliderState.standardToggleSize, 1219 | stretchedInnerSize: oldActionSliderState.stretchedInnerSize, 1220 | ); 1221 | if (_lastActionSliderState != actionSliderState) { 1222 | widget.stateChangeCallback 1223 | ?.call(_lastActionSliderState, actionSliderState, _controller); 1224 | _lastActionSliderState = actionSliderState; 1225 | } 1226 | } 1227 | 1228 | @override 1229 | Widget build(BuildContext context) { 1230 | if (!widget.allowedInterval.contains(widget.anchorPosition)) { 1231 | throw ArgumentError( 1232 | 'The allowed interval of an ActionSlider has to contain the anchor position'); 1233 | } 1234 | return MouseRegion( 1235 | cursor: switch (_state.slidingStatus) { 1236 | SlidingStatus.compact || 1237 | SlidingStatus.fixed || 1238 | SlidingStatus.released => 1239 | widget.cursors.defaultCursor, 1240 | SlidingStatus.dragged => widget.cursors.draggingCursor, 1241 | }, 1242 | child: LayoutBuilder( 1243 | builder: (context, constraints) { 1244 | final maxWidth = 1245 | min(widget.width ?? double.infinity, constraints.maxWidth); 1246 | if (maxWidth == double.infinity) { 1247 | throw StateError('The constraints of the ActionSlider ' 1248 | 'are unbound and no width is set'); 1249 | } 1250 | final toggleMargin = _controller.value.status is SliderStatusResult 1251 | ? widget.resultToggleMargin ?? widget.toggleMargin 1252 | : widget.toggleMargin; 1253 | return TweenAnimationBuilder( 1254 | curve: widget.toggleMarginCurve, 1255 | duration: widget.toggleMarginDuration, 1256 | tween: EdgeInsetsGeometryTween( 1257 | begin: toggleMargin, end: toggleMargin), 1258 | builder: (context, toggleMargin, child) { 1259 | final standardCompactToggleWidth = widget.toggleWidth ?? 1260 | widget.height - widget.toggleMargin.vertical; 1261 | final compactToggleWidth = 1262 | widget.toggleWidth ?? widget.height - toggleMargin.vertical; 1263 | final standardWidth = maxWidth - 1264 | compactToggleWidth - 1265 | widget.toggleMargin.horizontal; 1266 | final relativeSize = 1267 | _controller.value.status.expanded ? 1.0 : 0.0; 1268 | return TweenAnimationBuilder( 1269 | curve: widget.sizeAnimationCurve, 1270 | duration: widget.sizeAnimationDuration, 1271 | tween: Tween(begin: relativeSize, end: relativeSize), 1272 | builder: (context, relativeSize, child) { 1273 | final width = 1274 | maxWidth - ((1.0 - relativeSize) * standardWidth); 1275 | final backgroundWidth = 1276 | width - compactToggleWidth - toggleMargin.horizontal; 1277 | double statePosToLocalPos(double statePos) => 1278 | statePos.clamp(0.0, 1.0) * backgroundWidth; 1279 | final position = statePosToLocalPos(_state.position); 1280 | 1281 | double togglePosition; 1282 | double toggleWidth; 1283 | 1284 | switch (widget.sliderBehavior) { 1285 | case SliderBehavior.move: 1286 | togglePosition = position; 1287 | toggleWidth = compactToggleWidth; 1288 | case SliderBehavior.stretch: 1289 | double anchorPos = 1290 | statePosToLocalPos(_state.anchorPosition); 1291 | togglePosition = min(anchorPos, position); 1292 | toggleWidth = 1293 | ((position - anchorPos).abs()) + compactToggleWidth; 1294 | } 1295 | 1296 | final toggleHeight = widget.height - toggleMargin.vertical; 1297 | 1298 | final direction = 1299 | widget.direction ?? Directionality.of(context); 1300 | 1301 | final resolvedToggleMargin = 1302 | toggleMargin.resolve(Directionality.of(context)); 1303 | 1304 | double localPositionToSliderPosition(double dx) { 1305 | final result = 1306 | (((direction == TextDirection.rtl ? -1 : 1) * 1307 | (dx - standardCompactToggleWidth / 2)) / 1308 | backgroundWidth) 1309 | .clamp(0.0, 1.0); 1310 | return result; 1311 | } 1312 | 1313 | final actionSliderState = ActionSliderState( 1314 | position: _state.position, 1315 | size: Size(width, widget.height), 1316 | standardSize: Size(maxWidth, widget.height), 1317 | slidingStatus: _state.slidingStatus, 1318 | status: _state.status, 1319 | anchorPosition: _state.anchorPosition, 1320 | releasePosition: _state.releasePosition, 1321 | dragStartPosition: _state.dragStartPosition, 1322 | allowedInterval: widget.allowedInterval, 1323 | toggleSize: Size(toggleWidth, toggleHeight), 1324 | direction: direction, 1325 | toggleMargin: toggleMargin, 1326 | relativeSize: relativeSize, 1327 | standardToggleSize: 1328 | Size(standardCompactToggleWidth, toggleHeight), 1329 | stretchedInnerSize: Size( 1330 | maxWidth - widget.toggleMargin.horizontal, 1331 | toggleHeight), 1332 | ); 1333 | 1334 | _changeState(_state, actionSliderState, setState: false); 1335 | 1336 | return _ActionSliderStateProvider( 1337 | state: actionSliderState, 1338 | child: GestureDetector( 1339 | onTapUp: (details) { 1340 | if (_state.slidingStatus != SlidingStatus.released) { 1341 | return; 1342 | } 1343 | widget.onTap?.call( 1344 | _controller, 1345 | actionSliderState, 1346 | localPositionToSliderPosition( 1347 | details.localPosition.dx + 1348 | resolvedToggleMargin.right)); 1349 | }, 1350 | child: SizedBox.fromSize( 1351 | size: actionSliderState.size, 1352 | child: Stack( 1353 | clipBehavior: Clip.none, 1354 | fit: StackFit.passthrough, 1355 | children: [ 1356 | Builder( 1357 | builder: (context) => 1358 | widget.outerBackgroundBuilder( 1359 | context, 1360 | actionSliderState, 1361 | widget.outerBackgroundChild, 1362 | )), 1363 | Padding( 1364 | padding: widget.toggleMargin, 1365 | child: Stack( 1366 | clipBehavior: Clip.none, 1367 | children: [ 1368 | if (widget.backgroundBuilder != null) 1369 | Positioned.fill( 1370 | child: Opacity( 1371 | opacity: relativeSize, 1372 | child: Builder( 1373 | builder: (context) => 1374 | widget.backgroundBuilder!( 1375 | context, 1376 | actionSliderState, 1377 | widget.backgroundChild, 1378 | ), 1379 | ), 1380 | ), 1381 | ), 1382 | ], 1383 | ), 1384 | ), 1385 | Padding( 1386 | padding: toggleMargin, 1387 | child: Stack( 1388 | children: [ 1389 | Positioned.directional( 1390 | textDirection: direction, 1391 | start: togglePosition, 1392 | width: toggleWidth, 1393 | height: toggleHeight, 1394 | child: GestureDetector( 1395 | onHorizontalDragStart: (details) { 1396 | if (_state.slidingStatus != 1397 | SlidingStatus.released || 1398 | !_controller 1399 | .value.status.expanded) { 1400 | return; 1401 | } 1402 | final newPosition = 1403 | widget.allowedInterval.clamp( 1404 | localPositionToSliderPosition( 1405 | position + 1406 | details.localPosition 1407 | .dx)); 1408 | _changeState( 1409 | _state.copyWith( 1410 | position: newPosition, 1411 | slidingStatus: 1412 | SlidingStatus.dragged, 1413 | dragStartPosition: 1414 | _state.position, 1415 | ), 1416 | actionSliderState); 1417 | }, 1418 | onHorizontalDragUpdate: (details) { 1419 | if (_state.slidingStatus == 1420 | SlidingStatus.dragged) { 1421 | double newPosition = widget 1422 | .allowedInterval 1423 | .clamp(localPositionToSliderPosition( 1424 | statePosToLocalPos(_state 1425 | .dragStartPosition) + 1426 | details 1427 | .localPosition.dx)); 1428 | _startPosition = 1429 | _FixedValueListenable( 1430 | newPosition); 1431 | _changeState( 1432 | widget.actionThresholdType == 1433 | ThresholdType 1434 | .release || 1435 | newPosition < 1436 | widget 1437 | .actionThreshold || 1438 | widget.action == null 1439 | ? _state.copyWith( 1440 | position: newPosition, 1441 | slidingStatus: 1442 | SlidingStatus 1443 | .dragged, 1444 | ) 1445 | : _state.copyWith( 1446 | position: newPosition, 1447 | slidingStatus: 1448 | SlidingStatus 1449 | .released, 1450 | releasePosition: 1451 | newPosition, 1452 | ), 1453 | actionSliderState); 1454 | if (_state.slidingStatus == 1455 | SlidingStatus.released) { 1456 | _dropSlider(); 1457 | _onSlide(); 1458 | } 1459 | } 1460 | }, 1461 | onHorizontalDragEnd: (details) { 1462 | if (_state.slidingStatus != 1463 | SlidingStatus.dragged) { 1464 | return; 1465 | } 1466 | _dropSlider(); 1467 | if (widget.actionThresholdType == 1468 | ThresholdType.release && 1469 | _state.position >= 1470 | widget.actionThreshold) { 1471 | _onSlide(); 1472 | } 1473 | }, 1474 | child: MouseRegion( 1475 | cursor: switch ( 1476 | _state.slidingStatus) { 1477 | SlidingStatus.compact || 1478 | SlidingStatus.fixed => 1479 | widget.cursors.defaultCursor, 1480 | SlidingStatus.released => 1481 | widget.cursors.dragCursor, 1482 | SlidingStatus.dragged => 1483 | widget.cursors.draggingCursor, 1484 | }, 1485 | child: Builder( 1486 | builder: (context) => 1487 | widget.foregroundBuilder( 1488 | context, 1489 | actionSliderState, 1490 | widget.foregroundChild, 1491 | ), 1492 | ), 1493 | ), 1494 | ), 1495 | ), 1496 | ], 1497 | ), 1498 | ), 1499 | ], 1500 | ), 1501 | ), 1502 | ), 1503 | ); 1504 | }, 1505 | ); 1506 | }); 1507 | }, 1508 | ), 1509 | ); 1510 | } 1511 | 1512 | void _onSlide() { 1513 | widget.action?.call(_controller); 1514 | } 1515 | } 1516 | 1517 | class _ActionSliderStateProvider extends InheritedWidget { 1518 | final ActionSliderState state; 1519 | 1520 | const _ActionSliderStateProvider({ 1521 | required super.child, 1522 | required this.state, 1523 | }); 1524 | 1525 | static _ActionSliderStateProvider of(BuildContext context) { 1526 | final _ActionSliderStateProvider? result = context 1527 | .dependOnInheritedWidgetOfExactType<_ActionSliderStateProvider>(); 1528 | assert(result != null, 'No ActionSliderStateProvider found in context'); 1529 | return result!; 1530 | } 1531 | 1532 | @override 1533 | bool updateShouldNotify(_ActionSliderStateProvider oldWidget) { 1534 | return oldWidget.state != state; 1535 | } 1536 | } 1537 | -------------------------------------------------------------------------------- /lib/src/conditional_wrapper.dart: -------------------------------------------------------------------------------- 1 | // this widget is not covered because it is not used currently 2 | // coverage:ignore-start 3 | part of 'action_slider_widget.dart'; 4 | 5 | class _ConditionalWrapper extends StatefulWidget { 6 | final Widget Function(BuildContext context, Widget child) wrapper; 7 | final bool condition; 8 | final Widget child; 9 | 10 | const _ConditionalWrapper({ 11 | required this.wrapper, 12 | required this.condition, 13 | required this.child, 14 | }); 15 | 16 | @override 17 | State<_ConditionalWrapper> createState() => _ConditionalWrapperState(); 18 | } 19 | 20 | class _ConditionalWrapperState extends State<_ConditionalWrapper> { 21 | final _childKey = GlobalKey(); 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | final child = _EmptyWidget(key: _childKey, child: widget.child); 26 | if (widget.condition) return widget.wrapper(context, child); 27 | return child; 28 | } 29 | } 30 | 31 | // coverage:ignore-end 32 | 33 | class _EmptyWidget extends StatelessWidget { 34 | final Widget child; 35 | 36 | const _EmptyWidget({super.key, required this.child}); 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return child; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/src/cross_fade.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// A simple widget for handling crossfades. 4 | /// For a more customizable crossfade widget you can use the package cross_fade. 5 | class SliderCrossFade extends StatefulWidget { 6 | final T current; 7 | final Widget Function(BuildContext, T) builder; 8 | final Duration duration; 9 | final bool Function(T, T) equals; 10 | final bool Function(T, T)? size; 11 | 12 | const SliderCrossFade({ 13 | super.key, 14 | this.duration = const Duration(milliseconds: 750), 15 | required this.current, 16 | required this.builder, 17 | this.equals = _standardEquals, 18 | this.size, 19 | }); 20 | 21 | static bool _standardEquals(dynamic t1, dynamic t2) => t1 == t2; 22 | 23 | @override 24 | State> createState() => _SliderCrossFadeState(); 25 | } 26 | 27 | class _SliderCrossFadeState extends State> 28 | with SingleTickerProviderStateMixin { 29 | late List todo; 30 | late AnimationController _controller; 31 | late Animation _opacityAnimation; 32 | late Animation _sizeAnimation; 33 | 34 | @override 35 | void initState() { 36 | super.initState(); 37 | todo = [widget.current]; 38 | _controller = AnimationController(vsync: this, duration: widget.duration) 39 | ..addListener(() => setState(() {})) 40 | ..addStatusListener((status) { 41 | if (status == AnimationStatus.completed) { 42 | if (todo.length <= 1) return; 43 | todo.removeAt(0); 44 | if (todo.length > 1) { 45 | _controller.forward(from: 0.0); 46 | } else { 47 | _controller.value = 0.0; 48 | } 49 | } 50 | }); 51 | 52 | _opacityAnimation = CurvedAnimation( 53 | parent: _controller, 54 | curve: const Interval(0.0, 0.3, curve: Curves.easeIn), 55 | ); 56 | 57 | _sizeAnimation = TweenSequence( 58 | >[ 59 | TweenSequenceItem( 60 | tween: ConstantTween(1.0), 61 | weight: 25.0, 62 | ), 63 | TweenSequenceItem( 64 | tween: Tween(begin: 1.0, end: 1.3) 65 | .chain(CurveTween(curve: Curves.ease)), 66 | weight: 50.0, 67 | ), 68 | TweenSequenceItem( 69 | tween: Tween(begin: 1.3, end: 1.0) 70 | .chain(CurveTween(curve: Curves.ease.flipped)), 71 | weight: 50.0, 72 | ), 73 | ], 74 | ).animate(_controller); 75 | } 76 | 77 | @override 78 | void dispose() { 79 | _controller.dispose(); 80 | super.dispose(); 81 | } 82 | 83 | @override 84 | void didUpdateWidget(covariant SliderCrossFade oldWidget) { 85 | super.didUpdateWidget(oldWidget); 86 | if (!widget.equals(widget.current, todo.last)) { 87 | if (todo.length < 3) { 88 | todo.add(widget.current); 89 | } else { 90 | if (!widget.equals(widget.current, todo[1])) { 91 | todo[todo.length - 1] = widget.current; 92 | } else { 93 | todo.removeLast(); 94 | } 95 | } 96 | if (!_controller.isAnimating) _controller.forward(from: 0.0); 97 | } 98 | _controller.duration = widget.duration; 99 | } 100 | 101 | @override 102 | Widget build(BuildContext context) { 103 | return Transform.scale( 104 | scale: todo.length > 1 && (widget.size?.call(todo[0], todo[1]) ?? false) 105 | ? _sizeAnimation.value 106 | : 1.0, 107 | child: Stack( 108 | children: [ 109 | Opacity( 110 | key: _LocalKey(todo[0]), 111 | opacity: 1 - _opacityAnimation.value, 112 | child: widget.builder(context, todo[0])), 113 | if (todo.length > 1) 114 | Opacity( 115 | key: _LocalKey(todo[1]), 116 | opacity: _opacityAnimation.value, 117 | child: widget.builder(context, todo[1])) 118 | ], 119 | ), 120 | ); 121 | } 122 | } 123 | 124 | class _LocalKey extends ValueKey { 125 | const _LocalKey(super.value); 126 | } 127 | -------------------------------------------------------------------------------- /lib/src/cursors.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | part of 'action_slider_widget.dart'; 3 | 4 | class SliderCursors { 5 | /// [MouseCursor] to show when not hovering an indicator or a tappable icon. 6 | final MouseCursor defaultCursor; 7 | 8 | /// [MouseCursor] to show when grabbing the indicators. 9 | final MouseCursor draggingCursor; 10 | 11 | /// [MouseCursor] to show when hovering the indicators. 12 | final MouseCursor dragCursor; 13 | 14 | const SliderCursors({ 15 | this.defaultCursor = MouseCursor.defer, 16 | this.draggingCursor = SystemMouseCursors.grabbing, 17 | this.dragCursor = SystemMouseCursors.grab, 18 | }); 19 | 20 | const SliderCursors.all(MouseCursor cursor) 21 | : defaultCursor = cursor, 22 | draggingCursor = cursor, 23 | dragCursor = cursor; 24 | 25 | @override 26 | bool operator ==(Object other) => 27 | identical(this, other) || 28 | other is SliderCursors && 29 | runtimeType == other.runtimeType && 30 | defaultCursor == other.defaultCursor && 31 | draggingCursor == other.draggingCursor && 32 | dragCursor == other.dragCursor; 33 | 34 | @override 35 | int get hashCode => 36 | defaultCursor.hashCode ^ 37 | draggingCursor.hashCode ^ 38 | dragCursor.hashCode; 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/icons.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AnimatedCheckIcon extends StatelessWidget { 4 | final bool initialVisible; 5 | final bool visible; 6 | final Duration animationDuration; 7 | final Curve animationCurve; 8 | final Widget icon; 9 | 10 | const AnimatedCheckIcon({ 11 | super.key, 12 | this.icon = const Icon(Icons.check), 13 | this.initialVisible = false, 14 | this.visible = true, 15 | this.animationDuration = const Duration(milliseconds: 250), 16 | this.animationCurve = Curves.slowMiddle, 17 | }); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return TweenAnimationBuilder( 22 | tween: Tween(begin: initialVisible ? 1.0 : 0.0, end: visible ? 1.0 : 0.0), 23 | duration: animationDuration, 24 | curve: animationCurve, 25 | builder: (context, value, _) { 26 | return Center( 27 | child: ClipRect( 28 | clipper: _MyCustomClipper(relativeSize: value), 29 | child: icon, 30 | ), 31 | ); 32 | }, 33 | ); 34 | } 35 | } 36 | 37 | class _MyCustomClipper extends CustomClipper { 38 | final double relativeSize; 39 | 40 | _MyCustomClipper({required this.relativeSize}); 41 | 42 | @override 43 | Rect getClip(Size size) { 44 | return Rect.fromLTWH(0.0, 0.0, size.width * relativeSize, size.height); 45 | } 46 | 47 | @override 48 | bool shouldReclip(_MyCustomClipper oldClipper) { 49 | return oldClipper.relativeSize != relativeSize; 50 | } 51 | } 52 | 53 | class ScaleAppearingWidget extends StatefulWidget { 54 | final Widget child; 55 | final bool initialVisible; 56 | final bool visible; 57 | final Curve animationCurve; 58 | final Duration animationDuration; 59 | 60 | const ScaleAppearingWidget({ 61 | super.key, 62 | required this.child, 63 | this.visible = true, 64 | this.initialVisible = false, 65 | this.animationDuration = const Duration(milliseconds: 250), 66 | this.animationCurve = Curves.easeOutBack, 67 | }); 68 | 69 | @override 70 | State createState() => _ScaleAppearingWidgetState(); 71 | } 72 | 73 | class _ScaleAppearingWidgetState extends State 74 | with SingleTickerProviderStateMixin { 75 | late final AnimationController _controller; 76 | late final CurvedAnimation _animation; 77 | late bool _visible; 78 | 79 | @override 80 | void initState() { 81 | super.initState(); 82 | _visible = widget.initialVisible; 83 | _controller = AnimationController( 84 | vsync: this, 85 | duration: widget.animationDuration, 86 | value: _visible ? 1.0 : 0.0); 87 | _animation = 88 | CurvedAnimation(parent: _controller, curve: widget.animationCurve); 89 | _updateVisible(); 90 | } 91 | 92 | void _updateVisible() { 93 | if (_visible != widget.visible) { 94 | _visible = widget.visible; 95 | if (_visible) { 96 | _controller.forward(); 97 | } else { 98 | _controller.reverse(); 99 | } 100 | } 101 | } 102 | 103 | @override 104 | void didUpdateWidget(covariant ScaleAppearingWidget oldWidget) { 105 | super.didUpdateWidget(oldWidget); 106 | _updateVisible(); 107 | } 108 | 109 | @override 110 | void dispose() { 111 | _controller.dispose(); 112 | super.dispose(); 113 | } 114 | 115 | @override 116 | Widget build(BuildContext context) { 117 | return AnimatedBuilder( 118 | animation: _animation, 119 | builder: (context, _) { 120 | return Transform.scale( 121 | scale: _animation.value, 122 | child: widget.child, 123 | ); 124 | }); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/src/state.dart: -------------------------------------------------------------------------------- 1 | part of 'action_slider_widget.dart'; 2 | 3 | enum SlidingStatus { dragged, released, compact, fixed } 4 | 5 | class SliderState { 6 | final double position, 7 | anchorPosition, 8 | releasePosition, 9 | startPosition, 10 | dragStartPosition; 11 | final SlidingStatus slidingStatus; 12 | final SliderStatus status; 13 | 14 | SliderState({ 15 | required this.position, 16 | required this.slidingStatus, 17 | this.anchorPosition = 0.0, 18 | this.releasePosition = 0.0, 19 | this.startPosition = 0.0, 20 | this.dragStartPosition = 0.0, 21 | required this.status, 22 | }); 23 | 24 | SliderState copyWith({ 25 | double? position, 26 | SlidingStatus? slidingStatus, 27 | double? anchorPosition, 28 | double? releasePosition, 29 | double? startPosition, 30 | double? dragStartPosition, 31 | SliderStatus? status, 32 | }) => 33 | SliderState( 34 | position: position ?? this.position, 35 | slidingStatus: slidingStatus ?? this.slidingStatus, 36 | anchorPosition: anchorPosition ?? this.anchorPosition, 37 | releasePosition: releasePosition ?? this.releasePosition, 38 | startPosition: startPosition ?? this.startPosition, 39 | dragStartPosition: dragStartPosition ?? this.dragStartPosition, 40 | status: status ?? this.status, 41 | ); 42 | 43 | @override 44 | bool operator ==(Object other) => 45 | identical(this, other) || 46 | other is SliderState && 47 | runtimeType == other.runtimeType && 48 | position == other.position && 49 | anchorPosition == other.anchorPosition && 50 | releasePosition == other.releasePosition && 51 | startPosition == other.startPosition && 52 | dragStartPosition == other.dragStartPosition && 53 | slidingStatus == other.slidingStatus && 54 | status == other.status; 55 | 56 | @override 57 | int get hashCode => 58 | position.hashCode ^ 59 | anchorPosition.hashCode ^ 60 | releasePosition.hashCode ^ 61 | startPosition.hashCode ^ 62 | dragStartPosition.hashCode ^ 63 | slidingStatus.hashCode ^ 64 | status.hashCode; 65 | 66 | @override 67 | String toString() { 68 | return 'SliderState{position: $position, anchorPosition: $anchorPosition, releasePosition: $releasePosition, startPosition: $startPosition, dragStartPosition: $dragStartPosition, state: $slidingStatus, status: $status}'; 69 | } 70 | } 71 | 72 | class BaseActionSliderState { 73 | /// The current relative position of the toggle between [0] and [1]. 74 | final double position; 75 | 76 | /// The current [SlidingStatus] of the [ActionSlider]. 77 | final SlidingStatus slidingStatus; 78 | 79 | /// The current [SliderStatus] of the [ActionSlider]. 80 | /// It can be set manually with the ActionSliderController. 81 | final SliderStatus status; 82 | 83 | /// The relative anchor position of the toggle between [0] and [1]. 84 | final double anchorPosition; 85 | 86 | /// The relative position at which the toggle was released. 87 | /// Is only relevant if the [slidingStatus] is [SlidingStatus.released]. 88 | /// The default value is 0.0. 89 | final double releasePosition; 90 | 91 | /// The relative position at which the toggle was dragged. 92 | /// Is only relevant if the [slidingStatus] is [SlidingStatus.dragged]. 93 | final double dragStartPosition; 94 | 95 | /// The interval in which the toggle can be moved by the user. 96 | final SliderInterval allowedInterval; 97 | 98 | /// The direction of the slider. 99 | final TextDirection direction; 100 | 101 | /// The margin of the toggle. 102 | final EdgeInsetsGeometry toggleMargin; 103 | 104 | const BaseActionSliderState({ 105 | required this.position, 106 | required this.slidingStatus, 107 | required this.status, 108 | required this.anchorPosition, 109 | required this.releasePosition, 110 | required this.dragStartPosition, 111 | required this.allowedInterval, 112 | required this.direction, 113 | required this.toggleMargin, 114 | }); 115 | 116 | @override 117 | bool operator ==(Object other) => 118 | identical(this, other) || 119 | other is BaseActionSliderState && 120 | runtimeType == other.runtimeType && 121 | position == other.position && 122 | slidingStatus == other.slidingStatus && 123 | status == other.status && 124 | anchorPosition == other.anchorPosition && 125 | releasePosition == other.releasePosition && 126 | dragStartPosition == other.dragStartPosition && 127 | allowedInterval == other.allowedInterval && 128 | direction == other.direction && 129 | toggleMargin == other.toggleMargin; 130 | 131 | @override 132 | int get hashCode => 133 | position.hashCode ^ 134 | slidingStatus.hashCode ^ 135 | status.hashCode ^ 136 | anchorPosition.hashCode ^ 137 | releasePosition.hashCode ^ 138 | dragStartPosition.hashCode ^ 139 | allowedInterval.hashCode ^ 140 | direction.hashCode ^ 141 | toggleMargin.hashCode; 142 | } 143 | 144 | class ActionSliderState extends BaseActionSliderState { 145 | /// The size of the [ActionSlider]. 146 | final Size size; 147 | 148 | /// The size of the [ActionSlider] which it has in expanded form. 149 | final Size standardSize; 150 | 151 | /// The current size of the toggle. 152 | final Size toggleSize; 153 | 154 | /// The compact/unstretched size of the toggle. 155 | final Size standardToggleSize; 156 | 157 | /// The stretched size of the background of the slider. 158 | final Size stretchedInnerSize; 159 | 160 | /// [1.0] indicates that the slider is expanded and [0.0] indicates that the slider is compact. 161 | final double relativeSize; 162 | 163 | const ActionSliderState({ 164 | required super.position, 165 | required super.slidingStatus, 166 | required super.status, 167 | required super.anchorPosition, 168 | required super.releasePosition, 169 | required super.dragStartPosition, 170 | required super.allowedInterval, 171 | required super.direction, 172 | required super.toggleMargin, 173 | required this.size, 174 | required this.standardSize, 175 | required this.toggleSize, 176 | required this.relativeSize, 177 | required this.standardToggleSize, 178 | required this.stretchedInnerSize, 179 | }); 180 | 181 | @override 182 | bool operator ==(Object other) => 183 | identical(this, other) || 184 | super == other && 185 | other is ActionSliderState && 186 | runtimeType == other.runtimeType && 187 | size == other.size && 188 | standardSize == other.standardSize && 189 | toggleSize == other.toggleSize && 190 | standardToggleSize == other.standardToggleSize && 191 | stretchedInnerSize == other.stretchedInnerSize && 192 | relativeSize == other.relativeSize; 193 | 194 | @override 195 | int get hashCode => 196 | super.hashCode ^ 197 | size.hashCode ^ 198 | standardSize.hashCode ^ 199 | toggleSize.hashCode ^ 200 | standardToggleSize.hashCode ^ 201 | stretchedInnerSize.hashCode ^ 202 | relativeSize.hashCode; 203 | 204 | /// Alternative way for accessing the [ActionSliderState] in the different 205 | /// builders of [ActionSlider] via [BuildContext]. 206 | static ActionSliderState of(BuildContext context) => 207 | _ActionSliderStateProvider.of(context).state; 208 | } 209 | -------------------------------------------------------------------------------- /lib/src/status.dart: -------------------------------------------------------------------------------- 1 | part of 'action_slider_widget.dart'; 2 | 3 | final class _SliderJump { 4 | /// Specifies how far the toggle should jump between [-1.0] and [1.0]. 5 | final double height; 6 | 7 | _SliderJump({required this.height}); 8 | } 9 | 10 | final class ActionSliderControllerState { 11 | final SliderStatus status; 12 | final _SliderJump _jump; 13 | 14 | ActionSliderControllerState._({ 15 | required this.status, 16 | _SliderJump? jump, 17 | }) : _jump = jump ?? _SliderJump(height: 0.0); 18 | 19 | ActionSliderControllerState _copyWith({ 20 | SliderStatus? status, 21 | _SliderJump? jump, 22 | }) => 23 | ActionSliderControllerState._( 24 | status: status ?? this.status, 25 | jump: jump ?? _jump, 26 | ); 27 | } 28 | 29 | class SliderInterval { 30 | /// The minimum allowed value. 31 | final double start; 32 | 33 | /// The maximum allowed value. 34 | final double end; 35 | 36 | /// Returns [value] clamped to be in this [SliderInterval]. 37 | double clamp(double value) => value.clamp(start, end); 38 | 39 | /// Indicates whether [value] is contained in this [SliderInterval]. 40 | bool contains(double value) => value >= start && value <= end; 41 | 42 | /// Creates a new [SliderInterval]. 43 | const SliderInterval({this.start = 0.0, this.end = 1.0}); 44 | 45 | @override 46 | bool operator ==(Object other) => 47 | identical(this, other) || 48 | other is SliderInterval && 49 | runtimeType == other.runtimeType && 50 | start == other.start && 51 | end == other.end; 52 | 53 | @override 54 | int get hashCode => start.hashCode ^ end.hashCode; 55 | } 56 | 57 | @Deprecated('Use SliderStatus instead') 58 | typedef SliderMode = SliderStatus; 59 | 60 | /// Standard status in which the user can drag and move the toggle. 61 | class SliderStatusStandard extends SliderStatus { 62 | const SliderStatusStandard({super.highlighted = false}); 63 | 64 | @override 65 | bool get expanded => true; 66 | 67 | @override 68 | bool get toggleVisible => true; 69 | } 70 | 71 | /// Status like [success], [failure] and [loading]. 72 | /// In this case the slider indicator is fixed at [side] and not draggable. 73 | class SliderStatusResult extends SliderStatus { 74 | @override 75 | final bool expanded; 76 | 77 | @override 78 | final bool toggleVisible; 79 | 80 | /// Indicates on which [SliderSide] the toggle should be located. 81 | /// 82 | /// This parameter is ignored when [expanded] is [false]. 83 | final SliderSide side; 84 | 85 | const SliderStatusResult({ 86 | super.highlighted = true, 87 | this.expanded = false, 88 | this.side = SliderSide.end, 89 | this.toggleVisible = true, 90 | }); 91 | 92 | @override 93 | bool operator ==(Object other) => 94 | identical(this, other) || 95 | super == other && 96 | other is SliderStatusResult && 97 | runtimeType == other.runtimeType && 98 | expanded == other.expanded && 99 | toggleVisible == other.toggleVisible && 100 | side == other.side; 101 | 102 | @override 103 | int get hashCode => 104 | super.hashCode ^ 105 | expanded.hashCode ^ 106 | toggleVisible.hashCode ^ 107 | side.hashCode; 108 | } 109 | 110 | /// [SliderStatus] for success. 111 | class SliderStatusSuccess extends SliderStatusResult { 112 | const SliderStatusSuccess({ 113 | super.highlighted, 114 | super.expanded, 115 | super.side, 116 | }); 117 | } 118 | 119 | /// [SliderStatus] for failure. 120 | class SliderStatusFailure extends SliderStatusResult { 121 | const SliderStatusFailure({ 122 | super.highlighted, 123 | super.expanded, 124 | super.side, 125 | }); 126 | } 127 | 128 | /// [SliderStatus] for loading. 129 | class SliderStatusLoading extends SliderStatusResult { 130 | const SliderStatusLoading({ 131 | super.highlighted = false, 132 | super.expanded, 133 | super.side, 134 | }); 135 | } 136 | 137 | /// A status of an [ActionSlider]. 138 | /// 139 | /// You can create a predefined [SliderStatus] via [SliderStatus.loading], 140 | /// [SliderStatus.success], [SliderStatus.failure] and [SliderStatus.standard]. 141 | /// 142 | /// If you want to implement your own [SliderStatus], you can instantiate 143 | /// [SliderStatusStandard] or [SliderStatusResult]. 144 | /// Alternatively you can also create your own subclasses of them. 145 | sealed class SliderStatus { 146 | /// Indicates whether the slider is expanded in this status. Otherwise it is compact. 147 | bool get expanded; 148 | 149 | // TODO 150 | /// Indicates whether the toggle is visible in this status. 151 | bool get toggleVisible; 152 | 153 | /// Indicates whether this status gets highlighted more clearly in the slider. 154 | /// 155 | /// Ignored by [ActionSlider.custom]. 156 | final bool highlighted; 157 | 158 | const SliderStatus({this.highlighted = false}); 159 | 160 | /// [SliderStatus] for loading. 161 | /// 162 | /// {@template action_slider.status.expanded} 163 | /// [expanded] indicates whether the slider is expanded in this status. Otherwise it is compact. 164 | /// {@endtemplate} 165 | /// 166 | /// {@template action_slider.status.highlighted} 167 | /// [highlighted] indicates whether this status gets highlighted more clearly in the slider when appearing. Ignored by [ActionSlider.custom] 168 | /// {@endtemplate} 169 | /// 170 | /// {@template action_slider.status.side} 171 | /// [side] indicates on which [SliderSide] the toggle should be located. This parameter is ignored when [expanded] is [false]. 172 | /// {@endtemplate} 173 | const factory SliderStatus.loading( 174 | {bool expanded, bool highlighted, SliderSide side}) = SliderStatusLoading; 175 | 176 | /// [SliderStatus] for success. 177 | /// 178 | /// {@macro action_slider.status.expanded} 179 | /// 180 | /// {@macro action_slider.status.highlighted} 181 | /// 182 | /// {@macro action_slider.status.side} 183 | const factory SliderStatus.success( 184 | {bool expanded, bool highlighted, SliderSide side}) = SliderStatusSuccess; 185 | 186 | /// [SliderStatus] for failure. 187 | /// 188 | /// {@macro action_slider.status.expanded} 189 | /// 190 | /// {@macro action_slider.status.highlighted} 191 | /// 192 | /// {@macro action_slider.status.side} 193 | const factory SliderStatus.failure( 194 | {bool expanded, bool highlighted, SliderSide side}) = SliderStatusFailure; 195 | 196 | /// Standard status in which the user can drag and move the toggle. 197 | /// 198 | /// {@macro action_slider.status.highlighted} 199 | const factory SliderStatus.standard({bool highlighted}) = 200 | SliderStatusStandard; 201 | 202 | @override 203 | bool operator ==(Object other) => 204 | identical(this, other) || 205 | other is SliderStatus && 206 | runtimeType == other.runtimeType && 207 | highlighted == other.highlighted; 208 | 209 | @override 210 | int get hashCode => highlighted.hashCode; 211 | } 212 | 213 | enum SliderSide { 214 | /// The start of the [ActionSlider]. 215 | start(0.0), 216 | 217 | /// The end of the [ActionSlider]. 218 | end(1.0); 219 | 220 | final double _position; 221 | 222 | const SliderSide(this._position); 223 | } 224 | -------------------------------------------------------------------------------- /lib/src/style.dart: -------------------------------------------------------------------------------- 1 | part of 'action_slider_widget.dart'; 2 | 3 | class SliderStyle extends ThemeExtension { 4 | /// The [Color] of the [Container] in the background. 5 | final Color? backgroundColor; 6 | 7 | /// The [BorderRadiusGeometry] of the [Container] in the background. 8 | final BorderRadiusGeometry? borderRadius; 9 | 10 | /// The [Gradient] of the [Container] in the background. 11 | /// 12 | /// Overwrites [backgroundColor] if not [null]. 13 | final Gradient? backgroundGradient; 14 | 15 | /// The [Color] of the toggle. 16 | final Color? toggleColor; 17 | 18 | /// The [BorderRadiusGeometry] of the toggle. 19 | final BorderRadiusGeometry? toggleBorderRadius; 20 | 21 | /// The [Gradient] of the [Container] in the background. 22 | /// 23 | /// Overwrites [toggleColor] if not [null]. 24 | final Gradient? toggleGradient; 25 | 26 | /// The shadow of the toggle. 27 | /// 28 | /// Overwrites [toggleElevation] if not [null]. 29 | final List? toggleBoxShadow; 30 | 31 | /// The shadow of the [Container] in the background. 32 | /// 33 | /// Overwrites [elevation] if not [null]. 34 | final List? boxShadow; 35 | 36 | static SliderStyle? maybeOf(BuildContext context) => 37 | Theme.of(context).extension(); 38 | 39 | static SliderStyle of(BuildContext context) => maybeOf(context)!; 40 | 41 | const SliderStyle({ 42 | this.backgroundColor, 43 | this.borderRadius, 44 | this.backgroundGradient, 45 | this.toggleColor, 46 | this.toggleBorderRadius, 47 | this.toggleGradient, 48 | this.boxShadow = const [ 49 | BoxShadow( 50 | color: Colors.black26, 51 | spreadRadius: 1, 52 | blurRadius: 2, 53 | offset: Offset(0, 2), 54 | ), 55 | ], 56 | this.toggleBoxShadow, 57 | }); 58 | 59 | const SliderStyle._({ 60 | required this.backgroundColor, 61 | required this.borderRadius, 62 | required this.backgroundGradient, 63 | required this.toggleColor, 64 | required this.toggleBorderRadius, 65 | required this.toggleGradient, 66 | required this.boxShadow, 67 | required this.toggleBoxShadow, 68 | }); 69 | 70 | SliderStyle merge(SliderStyle other) => SliderStyle._( 71 | backgroundColor: other.backgroundColor ?? backgroundColor, 72 | borderRadius: other.borderRadius ?? borderRadius, 73 | backgroundGradient: other.backgroundGradient ?? backgroundGradient, 74 | toggleColor: other.toggleColor ?? toggleColor, 75 | toggleBorderRadius: other.toggleBorderRadius ?? toggleBorderRadius, 76 | toggleGradient: other.toggleGradient ?? toggleGradient, 77 | boxShadow: other.boxShadow ?? boxShadow, 78 | toggleBoxShadow: other.toggleBoxShadow ?? toggleBoxShadow, 79 | ); 80 | 81 | @override 82 | ThemeExtension copyWith({ 83 | Color? backgroundColor, 84 | BorderRadiusGeometry? borderRadius, 85 | Gradient? backgroundGradient, 86 | Color? toggleColor, 87 | BorderRadiusGeometry? toggleBorderRadius, 88 | Gradient? toggleGradient, 89 | List? toggleBoxShadow, 90 | List? boxShadow, 91 | }) => 92 | SliderStyle._( 93 | backgroundColor: backgroundColor ?? this.backgroundColor, 94 | borderRadius: borderRadius ?? this.borderRadius, 95 | backgroundGradient: backgroundGradient ?? this.backgroundGradient, 96 | toggleColor: toggleColor ?? this.toggleColor, 97 | toggleBorderRadius: toggleBorderRadius ?? this.toggleBorderRadius, 98 | toggleGradient: toggleGradient ?? this.toggleGradient, 99 | boxShadow: boxShadow ?? this.boxShadow, 100 | toggleBoxShadow: toggleBoxShadow ?? this.toggleBoxShadow, 101 | ); 102 | 103 | @override 104 | ThemeExtension lerp(SliderStyle other, double t) => 105 | SliderStyle._( 106 | backgroundColor: Color.lerp(backgroundColor, other.backgroundColor, t), 107 | borderRadius: 108 | BorderRadiusGeometry.lerp(borderRadius, other.borderRadius, t), 109 | backgroundGradient: 110 | Gradient.lerp(backgroundGradient, other.backgroundGradient, t), 111 | toggleColor: Color.lerp(toggleColor, other.toggleColor, t), 112 | toggleBorderRadius: BorderRadiusGeometry.lerp( 113 | toggleBorderRadius, other.toggleBorderRadius, t), 114 | toggleGradient: Gradient.lerp(toggleGradient, other.toggleGradient, t), 115 | boxShadow: BoxShadow.lerpList(boxShadow, other.boxShadow, t), 116 | toggleBoxShadow: 117 | BoxShadow.lerpList(toggleBoxShadow, other.toggleBoxShadow, t), 118 | ); 119 | 120 | @override 121 | bool operator ==(Object other) => 122 | identical(this, other) || 123 | other is SliderStyle && 124 | runtimeType == other.runtimeType && 125 | backgroundColor == other.backgroundColor && 126 | borderRadius == other.borderRadius && 127 | backgroundGradient == other.backgroundGradient && 128 | toggleColor == other.toggleColor && 129 | toggleBorderRadius == other.toggleBorderRadius && 130 | toggleGradient == other.toggleGradient && 131 | toggleBoxShadow == other.toggleBoxShadow && 132 | boxShadow == other.boxShadow; 133 | 134 | @override 135 | int get hashCode => 136 | backgroundColor.hashCode ^ 137 | borderRadius.hashCode ^ 138 | backgroundGradient.hashCode ^ 139 | toggleColor.hashCode ^ 140 | toggleBorderRadius.hashCode ^ 141 | toggleGradient.hashCode ^ 142 | toggleBoxShadow.hashCode ^ 143 | boxShadow.hashCode; 144 | } 145 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: action_slider 2 | description: A fully customizable slider to confirm actions and provide feedback on the success. It supports different states like loading, success and failure to provide feedback to the user. 3 | version: 0.8.0-beta.3 4 | repository: https://github.com/splashbyte/action_slider 5 | 6 | environment: 7 | sdk: ">=3.0.0 <4.0.0" 8 | flutter: ">=1.17.0" 9 | 10 | topics: 11 | - slider 12 | - ui 13 | - button 14 | - animation 15 | - widget 16 | 17 | dependencies: 18 | flutter: 19 | sdk: flutter 20 | 21 | dev_dependencies: 22 | flutter_test: 23 | sdk: flutter 24 | flutter_lints: ^4.0.0 25 | dartdoc: ^8.0.13 26 | 27 | flutter: 28 | 29 | funding: 30 | - https://buymeacoffee.com/splashbyte 31 | - https://ko-fi.com/splashbyte 32 | 33 | screenshots: 34 | - description: 'This image shows three examples of ActionSlider.' 35 | path: screenshots/preview.webp 36 | -------------------------------------------------------------------------------- /screenshots/preview.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splashbyte/action_slider/c51d8ebca5faf3f1de8e67631c6ec4460114fdc6/screenshots/preview.webp -------------------------------------------------------------------------------- /test/action_slider_test.dart: -------------------------------------------------------------------------------- 1 | void main() {} 2 | --------------------------------------------------------------------------------