A simple widget for animating a set of images with full custom controls as an alternative to using a GIF file.

Last update: Jul 5, 2022

image_sequence_animator

Cosmos Software

Pub License

A simple widget for animating a set of images with full custom controls as an alternative to using a GIF file.

If you have a GIF file you would like to use with this package, I recommend EZGIF to convert your GIF file to an image sequence.

It is highly recommended to read the documentation and run the example project on a real device to fully understand and inspect the full range of capabilities.

Media | Description | How-to-Use

Recent

  • [isOnline] is added. If your [folderName] is an online path, this value should be set to true.

  • [waitUntilCacheIsComplete] is added. If you want the [ImageSequenceAnimator] to wait until the entire image sequence is cached, this value should be set to true i. Otherwise, the [ImageSequenceAnimator] will invoke [onReadyToPlay] and start playing if [isAutoPlay] is set to true when it approximates that the remaining caching can be completed without causing stutters. This value is only used if [isOnline] is set to true.

  • [cacheProgressIndicatorBuilder] is added. If you want to display a widget until the [ImageSequenceAnimator] is ready to be played, use this function.


Media

Watch on Youtube:

Image Sequence Animator

Description

This simple widget for animating a set of images (a.k.a an image sequence) with full custom controls as an alternative to using a GIF file.

GIF files are, as far as I know, not possible to control. With this package, you will have full control over your image sequence like controlling a video. You can loop, boomerang, change the color, play, pause, stop, skip, rewind, restart and more.

How-to-Use

First, add your image sequence to your assets and update the "pubspec.yaml" accordingly.

Then create an ImageSequenceAnimator widget as shown in the example:

ImageSequenceAnimator(
  "assets/ImageSequences/MyImageSequence",  //folderName 
  "Frame_",                                 //fileName
  0,                                        //suffixStart
  5,                                        //suffixCount 
  "png",                                    //fileFormat 
  60,                                       //frameCount
 {Key key,
  fps               : 60,
  isLooping         : false,
  isBoomerang       : false,
  isAutoPlay        : true,
  color             : Colors.white,
  onReadyToPlay     : _onReadyToPlay,
  onStartPlaying    : _onStartPlaying,
  onPlaying         : _onPlaying,
  onFinishPlaying   : _onFinishPlaying})

ImageSequenceAnimator(
  "https://www.domain.com/ImageSequences/MyImageSequence",  //folderName 
  "Frame_",                                                 //fileName
  0,                                                        //suffixStart
  5,                                                        //suffixCount 
  "png",                                                    //fileFormat 
  60,                                                       //frameCount
 {Key key,
  fps               :               60,
  isLooping         :               false,
  isBoomerang       :               false,
  isAutoPlay:                       true,
  isOnline:                         true,
  waitUntilCacheIsComplete:         true,
  cacheProgressIndicatorBuilder:    _cacheProgressIndicatorBuilder,
  color             :               Colors.white,
  onReadyToPlay     :               _onReadyToPlay,
  onStartPlaying    :               _onStartPlaying,
  onPlaying         :               _onPlaying,
  onFinishPlaying   :               _onFinishPlaying})
  
Widget _cacheProgressIndicatorBuilder(BuildContext _context , double _progress);  
void   _onReadyToPlay(ImageSequenceAnimatorState _imageSequenceAnimator);
void   _onStartPlaying(ImageSequenceAnimatorState _imageSequenceAnimator);
void   _onPlaying(ImageSequenceAnimatorState _imageSequenceAnimator);
void   _onFinishPlaying(ImageSequenceAnimatorState _imageSequenceAnimator); 

Further Explanations:

For a complete set of descriptions for all parameters and methods, see the documentation.

  • [isLooping] will override [isBoomerang] if both are set to true.
  • All [ImageSequenceProcessCallback] callbacks will return a reference to the created [ImageSequenceAnimator] state. You can save this instance for further actions.
  • Use [ImageSequenceAnimatorState]'s [void setIsLooping(bool isLooping)], [void setIsBoomerang(bool isBoomerang)], [void setColor(Color color)], [void play({double from: -1.0})], [void rewind({double from: -1.0})], [void pause()], [void skip(double value, {double percentage: -1.0})], [void restart()], [void stop()] methods for the corresponding actions.
  • Use [ImageSequenceAnimatorState]'s [bool get isLooping], [bool get isBoomerang], [double get currentProgress], [double get totalProgress], [double get currentTime] and [double get totalTime] methods to get the respective values.

Notes

I started using and learning Flutter only some weeks ago so this package might have some parts that don't make sense, that should be completely different, that could be much better, etc. Please let me know! Nicely!

Any help, suggestion or criticism is appreciated!

Cheers.





GitHub

https://github.com/aliyigitbireroglu/flutter-image-sequence-animator
Comments
  • 1. Using image sequence animator with Image.network

    I really love this plugin, Im using it to create a "stop motion" from the lasted saved webcam screens. However these screens are all stored online and ofcourse updated every X minute.

    I would really love to use this plugin, where I can give a set of network images, instead of giving a location for my image assets.

    Reviewed by roderikpeeters at 2020-05-16 21:01
  • 2. Animation not working on splash screen

    I have been using this library for some time now, and it worked great. But recently I tried to add animation to splash screen - initial screen opens as splash screen and when animation ends I open another screen.

    Animation works well when I run my app from computer. However, when I close and open the app from device, only single image is being shown and there is no animation. What can be the reason for this? Should I wait for some initialization?

    Reviewed by Zeynal7 at 2020-09-13 14:03
  • 3. Support for random named images

    I love this package, its exactly what I need. But I have images in firebase storage where the names are generated and don't follow the naming format you have implemented. Is there a possibility for an implementation where the frames can be looped from a list of urls instead?

    Reviewed by jslattery26 at 2020-12-17 20:34
  • 4. How to control the speed of my animation?

    My animation is running to fast, how can I control the speed of my animation?

    ImageSequenceAnimator(
                                    "assets/animation",
                                    "frame_",
                                    0,
                                    0,
                                    "png",
                                    22,
                                  ),
    
    Reviewed by d-apps at 2021-06-15 14:28
  • 5. Why is Color white chosen as default?

    I have been struggling to find the reason why my sequence is not playing and nothing is shown. Later I realized that my background is white, and default color is also white, so I was not seeing anything. The default should just be null. Is there any reason that white is chosen?

    Reviewed by Zeynal7 at 2020-08-08 12:40
  • 6. Many pictures will make it impossible to display

    Many pictures will make it impossible to display

    issue code branch【image-sequence-animator】

    Flutter issue

    issue code

    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    
    import 'package:spring_button/spring_button.dart';
    import 'package:image_sequence_animator/image_sequence_animator.dart';
    
    class HomePage extends StatefulWidget {
      HomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _HomePageState createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      ImageSequenceAnimatorState imageSequenceAnimator;
      bool wasPlaying = false;
      Color color1 = Colors.greenAccent;
      Color color2 = Colors.indigo;
      String loopText = "Start Loop";
      String boomerangText = "Start Boomerang";
    
      void onReadyToPlay(ImageSequenceAnimatorState _imageSequenceAnimator) {
        imageSequenceAnimator = _imageSequenceAnimator;
      }
    
      void onPlaying(ImageSequenceAnimatorState _imageSequenceAnimator) {
        setState(() {});
      }
    
      Widget row(String text, Color color) {
        return Padding(
          padding: EdgeInsets.all(3.125),
          child: Container(
            decoration: BoxDecoration(
              color: color,
              borderRadius: const BorderRadius.all(const Radius.circular(10.0)),
            ),
            child: Center(
              child: Text(
                text,
                style: const TextStyle(
                  color: Colors.white,
                  fontWeight: FontWeight.bold,
                  fontSize: 12.5,
                ),
              ),
            ),
          ),
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text(widget.title)),
          body: Column(
            children: [
              Expanded(
                flex: 4,
                child: Padding(
                  padding: EdgeInsets.all(25),
                  child: ImageSequenceAnimator(
                    "assets/images/side",
                    "side_",
                    0,
                    0,
                    "jpg",
                    60,
                    isAutoPlay: false,
                    color: null,
                    onReadyToPlay: onReadyToPlay,
                    onPlaying: onPlaying,
                  ),
                ),
              ),
              Expanded(
                child: Row(
                  children: [
                    Expanded(
                      flex: 4,
                      child: CupertinoSlider(
                        value: imageSequenceAnimator == null ? 0.0 : imageSequenceAnimator.animationController.value,
                        min: imageSequenceAnimator == null ? 0.0 : imageSequenceAnimator.animationController.lowerBound,
                        max: imageSequenceAnimator == null ? 100.0 : imageSequenceAnimator.animationController.upperBound,
                        onChangeStart: (double value) {
                          wasPlaying = imageSequenceAnimator.animationController.isAnimating;
                          imageSequenceAnimator.pause();
                        },
                        onChanged: (double value) {
                          imageSequenceAnimator.skip(value);
                        },
                        onChangeEnd: (double value) {
                          if (wasPlaying) imageSequenceAnimator.play();
                        },
                      ),
                    ),
                    Expanded(
                      child: Center(
                        child: Text(
                          imageSequenceAnimator == null ? "0.0" : ((imageSequenceAnimator.currentTime.floor()).toString() + "/" + (imageSequenceAnimator.totalTime.floor()).toString()),
                          textAlign: TextAlign.center,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
              Expanded(
                child: Row(
                  children: [
                    Expanded(
                      child: SpringButton(
                        SpringButtonType.OnlyScale,
                        row(
                          loopText,
                          Colors.cyan,
                        ),
                        useCache: false,
                        onTap: () {
                          setState(() {
                            loopText = imageSequenceAnimator.isLooping ? "Start Loop" : "Stop Loop";
                            boomerangText = "Start Boomerang";
                            imageSequenceAnimator.setIsLooping(!imageSequenceAnimator.isLooping);
                          });
                        },
                      ),
                    ),
                    Expanded(
                      child: SpringButton(
                        SpringButtonType.OnlyScale,
                        row(
                          boomerangText,
                          Colors.deepPurpleAccent,
                        ),
                        useCache: false,
                        onTap: () {
                          setState(() {
                            loopText = "Start Loop";
                            boomerangText = imageSequenceAnimator.isBoomerang ? "Start Boomerang" : "Stop Boomerang";
                            imageSequenceAnimator.setIsBoomerang(!imageSequenceAnimator.isBoomerang);
                          });
                        },
                      ),
                    ),
                  ],
                ),
              ),
              Expanded(
                child: SpringButton(
                  SpringButtonType.OnlyScale,
                  row(
                    "Change Colour",
                    Colors.redAccent,
                  ),
                  useCache: false,
                  onTap: () {
                    imageSequenceAnimator.changeColor(imageSequenceAnimator.color == color1 ? color2 : color1);
                  },
                ),
              ),
              Expanded(
                child: Row(
                  children: [
                    Expanded(
                      child: SpringButton(
                        SpringButtonType.OnlyScale,
                        row(
                          "Play/Pause",
                          Colors.deepOrangeAccent,
                        ),
                        useCache: false,
                        onTap: () {
                          setState(() {
                            imageSequenceAnimator.animationController.isAnimating ? imageSequenceAnimator.pause() : imageSequenceAnimator.play();
                          });
                        },
                      ),
                    ),
                    Expanded(
                      child: SpringButton(
                        SpringButtonType.OnlyScale,
                        row(
                          "Stop",
                          Colors.green,
                        ),
                        useCache: false,
                        onTap: () {
                          imageSequenceAnimator.stop();
                        },
                      ),
                    ),
                  ],
                ),
              ),
              Expanded(
                child: Row(
                  children: [
                    Expanded(
                      child: SpringButton(
                        SpringButtonType.OnlyScale,
                        row(
                          "Restart",
                          Colors.teal,
                        ),
                        useCache: false,
                        onTap: () {
                          imageSequenceAnimator.restart();
                        },
                      ),
                    ),
                    Expanded(
                      child: SpringButton(
                        SpringButtonType.OnlyScale,
                        row(
                          "Rewind",
                          Colors.indigoAccent,
                        ),
                        useCache: false,
                        onTap: () {
                          imageSequenceAnimator.rewind();
                        },
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        );
      }
    }
    
    Reviewed by jing-pei at 2020-08-07 20:41
  • 7. How to handle the digits of suffix in image name?

    ImageSequenceAnimator(
                        "assets/img/fraisier/AnimatedImage1", //folderName
                        "R19I1-", //fileName
                        1, //suffixStart
                        13, //suffixCount
                        "jpg", //fileFormat
                        60, //frameCount
                        isAutoPlay: true,
                      )
    

    My Images names look like this,

    images/R19I1-01.jpg images/R19I1-02.jpg

    Unable to load asset: assets/img/fraisier/AnimatedImage1/R19I1-0000000000001.jpg I am facing this issue! Can you please tell me how to handle the digits of the name? Or is there any fix?

    Reviewed by balaji101010 at 2020-02-24 09:30
  • 8. Is it still working on Flutter 3.0.2?

    I'm new to Flutter but I get HttpException: Invalid statusCode: 403.

    ImageSequenceAnimator(
            'https://cdn2.doki1001.com/wp-content/uploads/WP-manga/data/manga_6149d88d66b2b/db5198634b8b7c462f3668bc3e72e3fb',
            '',
            0,
            2,
            'jpg',
            10,
            key: const Key('online'),
            fps: 3,
            isAutoPlay: true,
            isOnline: true,
            onReadyToPlay: onOnlineReadyToPlay,
            onPlaying: onOnlinePlaying,
          ),
    
    Reviewed by farmerswalker at 2022-06-17 00:47
  • 9. Null error on reopening the activity

    The animation works fine when I start the app. After I press back button and open the activity again I get Null error.

    `import 'package:flutter/material.dart';

    import 'package:flutter/cupertino.dart';

    import 'package:image_sequence_animator/image_sequence_animator.dart';

    class Image_Sequence_2 extends StatefulWidget { Image_Sequence_2() : super();

    @override CarouselHistory createState() => CarouselHistory(); }

    class CarouselHistory extends State<Image_Sequence_2> { ImageSequenceAnimatorState? get imageSequenceAnimator => offlineImageSequenceAnimator; ImageSequenceAnimatorState? offlineImageSequenceAnimator;

    bool wasPlaying = false;

    bool _useFullPaths = true; List? _fullPathsOffline;

    void onOfflineReadyToPlay(ImageSequenceAnimatorState _imageSequenceAnimator) { offlineImageSequenceAnimator = _imageSequenceAnimator; }

    void onOfflinePlaying(ImageSequenceAnimatorState _imageSequenceAnimator) { setState(() {}); }

    @override void dispose() { super.dispose(); }

    @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey, appBar: AppBar( backgroundColor: Colors.black54, title: const Text("Demo", style: TextStyle(fontSize: 25.0, color: Colors.teal)), centerTitle: true, ), body: Container( color: Colors.black54, height: double.infinity, alignment: Alignment.topCenter, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Divider( height: 20, thickness: 5, endIndent: 0, color: Colors.black, ), Container( alignment: Alignment.center, child: ImageSequenceAnimator( "https://www.noorderlichtapp.nl", "aurora_", 1, 1, "jpg", 8, key: Key("online"), fps: 15, isAutoPlay: false, isOnline: true, waitUntilCacheIsComplete: true, cacheProgressIndicatorBuilder: (context, progress) { return CircularProgressIndicator( value: progress, ); }, fullPaths: _useFullPaths ? _fullPathsOffline : null, onReadyToPlay: onOfflineReadyToPlay, onPlaying: onOfflinePlaying, ), ), Container( alignment: Alignment.center, child: CupertinoSlider( value: imageSequenceAnimator == null ? 0.0 : imageSequenceAnimator!.currentProgress, min: 0.0, max: imageSequenceAnimator == null ? 100.0 : imageSequenceAnimator!.totalProgress, onChangeStart: (double value) { wasPlaying = imageSequenceAnimator!.isPlaying; imageSequenceAnimator!.pause(); }, onChanged: (double value) { imageSequenceAnimator!.skip(value); }, onChangeEnd: (double value) { if (wasPlaying) imageSequenceAnimator!.play(); }, ), ), const Divider( height: 20, thickness: 5, endIndent: 0, color: Colors.black, ), ], ), ), )); } } `

    Reviewed by bharathreddyh at 2022-03-22 06:09
  • 10. onFinishPlaying is being called twice

    I am having a problem on my app where the init method is being called twice, after some tests I realized that onFinishPlyaing is being called twice, could you help me with that? Thank you.

    ImageSequenceAnimator(
                                    "assets/animation",
                                    "frame_",
                                    0,
                                    0,
                                    "png",
                                    22,
                                    fps: 20,
                                    onFinishPlaying: (imageSequenceAnimatorState){
    
                                      print("onFinishPlaying");
                                      splashController.init();
    
                                    },
                                  ),
    
    onFinishPlaying // it prints twice
    

    flutter doctor -v

    [✓] Flutter (Channel stable, 2.5.3, on macOS 11.6 20G165 darwin-arm, locale en-BR) • Flutter version 2.5.3 at /Users/djalma/development/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision 18116933e7 (7 weeks ago), 2021-10-15 10:46:35 -0700 • Engine revision d3ea636dc5 • Dart version 2.14.4

    [✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0) • Android SDK at /Users/djalma/Library/Android/sdk • Platform android-31, build-tools 31.0.0 • ANDROID_HOME = /Users/djalma/Library/Android/sdk • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7249189) • All Android licenses accepted.

    [✓] Xcode - develop for iOS and macOS • Xcode at /Applications/Xcode.app/Contents/Developer • Xcode 13.1, Build version 13A1030d • CocoaPods version 1.10.2

    [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

    [✓] Android Studio (version 2020.3) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7249189)

    [✓] Connected device (3 available) • iPad Pro (9.7-inch) (mobile) • 0C8AB907-97CE-4FD7-B4EC-A38A33242FDC • ios • com.apple.CoreSimulator.SimRuntime.iOS-15-0 (simulator) • macOS (desktop) • macos • darwin-arm64 • macOS 11.6 20G165 darwin-arm • Chrome (web) • chrome • web-javascript • Google Chrome 96.0.4664.55

    • No issues found!

    Reviewed by d-apps at 2021-11-30 19:28
  • 11. [Feature Request] Parameter for number of times to play the animation

    Need configuration like sequence plays as boomerang but not in the loop and only once.

    Planned to use callbacks onPlaying and onFinishPlaying to manually stop it after 1 iteration but does not seem to be a nice solution.

    Also, the issue is these callbacks are fired for every frame/image. So, onPlaying is called 64 times when I have 60 images.

    ImageSequenceAnimator(
                    "assets/images/newScrollSprites", //folderName
                    "", //fileName
                    1, //suffixStart
                    1, //suffixCount
                    "png", //fileFormat
                    60, //frameCount
                    // fps: 60,
                    isBoomerang: true,
                    isLooping: false,
                    isAutoPlay: false,
                    onReadyToPlay: onOfflineReadyToPlay,
                    onPlaying: onOfflinePlaying,
                    onFinishPlaying: onFinishPlaying,
     )
    
    

    Parameter like "animationIterations" which respects boomerang property would help.

    Reviewed by uzumakinaruto123 at 2021-07-08 08:04

Related

Flutter Spring -simple & power full animation kit
Flutter Spring -simple & power full animation kit

A simple & powerfull pre-built animation kit. ?? Installation dependencies: spring: ^2.0.2 ?? Spring Animations Slide Scale Animated Card Flip Fa

Jul 5, 2022
A set of transition patterns within the animations package using flutter.
A set of transition patterns within the animations package using flutter.

Flutter Motion Transitions A fultter app to demonstrate Material motion system. Material Motion System The four main Material transition patterns are

Feb 9, 2022
Set of basic geometric animations using Flutter available as Android App gallery
 Set of basic geometric animations using Flutter available as Android App gallery

#ui, #animations, #geometry, #flutter aria Set of basic geometric animations usi

Jul 23, 2022
Load and get full control of your Rive files in a Flutter project using this library.
Load and get full control of your Rive files in a Flutter project using this library.

⚠️ Please migrate to the new Rive Flutter runtime. This runtime is for the old Rive (formerly Flare) and will only receive updates for breaking issues

Aug 8, 2022
🔥🔥🔥 Easy to build an animation set
🔥🔥🔥 Easy to build an animation set

✨ Flutter Animation Set [Lanuage ~~] English | 中文文档 Simplified Flutter stagger animation.To drive the Flutter stagger animation through a timeline in

May 29, 2022
🔥🔥🔥 Easy to build an animation set
🔥🔥🔥 Easy to build an animation set

✨ Flutter Animation Set [Lanuage ~~] English | 中文文档 Simplified Flutter stagger animation.To drive the Flutter stagger animation through a timeline in

Aug 12, 2022
A flutter package which makes it easier to display the difference between two images.
A flutter package which makes it easier to display the difference between two images.

?? Before After A flutter package which makes it easier to display the differences between two images.. The source code is 100% Dart, and everything r

Aug 15, 2022
A flutter package which display the library collapse according to the number of images associated with hero animation
A flutter package which display the library collapse according to the number of images associated with hero animation

?? Gallery Collapse A flutter package which display the library collapse accordi

Jun 7, 2022
Wave-transition-app - A wave transition based mobile app with included images and other files
Wave-transition-app - A wave transition based mobile app with included images and other files

Flutter wave application A new Flutter project done with dart and it's a wave tr

May 18, 2022
Loading widget based on a Flare animation, allow you to create beautiful custom loading widgets or dialogs

flare_loading Loading widget based on a Flare animation, allow you to create custom loading widgets or dialogs If you're using Rive instead of Flare p

Apr 16, 2021
A simple custom loading indicator package.
A simple custom loading indicator package.

custom_loading_indicator A Flutter package to customise the loading indicators with your organisation's logo. Let's say you're a dentist and your app

Aug 10, 2020
Custom Layout with interactive add button to impove your UI and UX .
 Custom Layout with interactive add button to impove your UI and UX .

Interactive Add button layout Custom Layout with interactive add button to impove your UI and UX . the package is available here inspired from Oleg Fr

Sep 13, 2021
Create your own custom SlideTransition combined with some animation in Flutter.
Create your own custom SlideTransition combined with some animation in Flutter.

Create your own custom SlideTransition combined with some animation in Flutter.

Jun 21, 2022
Writing custom Widgets in Flutter
Writing custom Widgets in Flutter

Flutter - Custom Widgets Part 1 - EllipsizedText / LeafRenderObjectWidget Writing custom Widgets in Flutter (Part 1) — EllipsizedText Part 2 - ChildSi

Aug 6, 2022
Custom menus anywhere!
Custom menus anywhere!

Flutter Anywhere Menus (FAM) Menus, anywhere you want them! Default Usage Menu( child: MaterialButton( child: Text('Show Basic Menu'), ), me

Dec 18, 2021
A flutter project with Implementation of a Contacts app in 4 ways (API, Custom, Preferences and Sqflite).
A flutter project with Implementation of a Contacts app in 4 ways (API, Custom, Preferences and Sqflite).

Contacts A flutter project with Implementation of a Contacts app in 4 ways (API, Custom, Preferences and Sqflite). It consist some common operations l

Jul 18, 2022
Clip your widgets with custom shapes provided.
Clip your widgets with custom shapes provided.

clippy_flutter Clip your widgets with custom shapes provided. #Arc, #Arrow, #Bevel, #ButtCheek, #Chevron, #Diagonal, #Label, #Message, #Paralellogram,

Aug 1, 2022
🎨Custom animation challenge annouced by egdroid
🎨Custom animation challenge annouced by egdroid

EgdroidChallenge Follow me on social media github linkedin twitter facebook ?? challenge accepted ?? A challenge code series idea suggested by an awes

Aug 28, 2020
A custom Flutter value slider that makes a wave effect when dragged.

A Flutter slider that makes a wave effect when dragged. Does a little bounce when dropped. Demo Getting Started To use this plugin, add wave_slider as

Jul 17, 2022