├── .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 | [](https://pub.dev/packages/action_slider)
2 | [](https://github.com/SplashByte/action_slider)
3 | [](https://pub.dev/packages/action_slider/score)
4 | [](https://pub.dev/packages/action_slider/score)
5 | [](https://pub.dev/packages/action_slider/score)
6 | [](https://github.com/SplashByte/action_slider/blob/main/LICENSE)
7 | [](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 | 
19 | 
20 |
21 | `ActionSlider.dual()`
22 | 
23 | 
24 |
25 | `ActionSlider.standard()` with `TextDirection.rtl`
26 | 
27 |
28 | `ActionSlider.standard()` with `SliderIconAnimation.roll`
29 | 
30 |
31 | `ActionSlider.standard()` with `SliderBehavior.stretch` and `SliderIconAnimation.roll`
32 | 
33 |
34 | You can build your own sliders with `ActionSlider.custom()`
35 | 
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 |
--------------------------------------------------------------------------------