Flutter Fix
No Result
View All Result
  • Login
  • Register
  • Media
    • Music
    • Video
    • Images
    • Gallery
  • Flutter Apps
    • Finance App
    • Weather App
    • Video App
    • Music App
    • Shopping App
    • World Clock
    • ECommerce
    • Mobile Web Browser
  • Menu
    • Buttons
    • App Bar
    • Floating Action Button
    • Toast
    • Tab Bar
    • Swipe
  • Components
    • Developers Point
    • Animations
    • Loading
    • Flutter Packages
    • WebView
  • Inputs
    • Forms
    • TextField
    • Toggle Button
    • Download
    • Widgets
  • Games
  • UI
    • Material Design
    • Card
    • Flutter Widgets
    • Neomorphism
    • Navigation
  • Navigation
  • Q&A
  • Dart
    • Flutter Methods
FORUM
ASK QUESTION
  • Media
    • Music
    • Video
    • Images
    • Gallery
  • Flutter Apps
    • Finance App
    • Weather App
    • Video App
    • Music App
    • Shopping App
    • World Clock
    • ECommerce
    • Mobile Web Browser
  • Menu
    • Buttons
    • App Bar
    • Floating Action Button
    • Toast
    • Tab Bar
    • Swipe
  • Components
    • Developers Point
    • Animations
    • Loading
    • Flutter Packages
    • WebView
  • Inputs
    • Forms
    • TextField
    • Toggle Button
    • Download
    • Widgets
  • Games
  • UI
    • Material Design
    • Card
    • Flutter Widgets
    • Neomorphism
    • Navigation
  • Navigation
  • Q&A
  • Dart
    • Flutter Methods
No Result
View All Result
Flutter Fix
No Result
View All Result
Home Components Loading

A Simple Water Droplet Circle Loader with flutter

August 20, 2020
in Loading, UI
5 min read
Water Droplet Loader Flutter

Droplet Circle Loader

Flutter Water Loading Spinner Example.

RELATED FLUTTER RESOURCES

A Simple Hacker News reader app built in Flutter

A Flutter package that detects credit card types from the first input

A Simple & Beautiful ToDo App Made with Flutter

VIEW DEMO

Made with

Dart & Flutter

Author

Ryan Dixon

Demo

See the Pen Droplet Circle Loader by Ryan Dixon (@SkysRad) on CodePen.

ADVERTISEMENT

Full Code

import 'package:flutter/material.dart';
import 'dart:ui';
import 'dart:math' as math;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Container(
        width: double.infinity,
        height: double.infinity,
        alignment: Alignment.center,
        child: AutomatedAnimator(
          animateToggle: true,
          doRepeatAnimation: true,
          duration: Duration(seconds: 10),
          buildWidget: (double animationPosition) {
            return WaveLoadingBubble(
              foregroundWaveColor: Color(0xFF6AA0E1),
              backgroundWaveColor: Color(0xFF4D90DF),
              loadingWheelColor: Color(0xFF77AAEE),
              period: animationPosition,
              backgroundWaveVerticalOffset: 90 - animationPosition * 200,
              foregroundWaveVerticalOffset: 90 +
                  reversingSplitParameters(
                    position: animationPosition,
                    numberBreaks: 6,
                    parameterBase: 8.0,
                    parameterVariation: 8.0,
                    reversalPoint: 0.75,
                  ) -
                  animationPosition * 200,
              waveHeight: reversingSplitParameters(
                position: animationPosition,
                numberBreaks: 5,
                parameterBase: 12,
                parameterVariation: 8,
                reversalPoint: 0.75,
              ),
            );
          },
        ),
      ),
    );
  }
}

class AutomatedAnimator extends StatefulWidget {
  AutomatedAnimator({
    @required this.buildWidget,
    @required this.animateToggle,
    this.duration = const Duration(milliseconds: 300),
    this.doRepeatAnimation = false,
    Key key,
  }) : super(key: key);

  final Widget Function(double animationValue) buildWidget;
  final Duration duration;
  final bool animateToggle;
  final bool doRepeatAnimation;

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

class _AutomatedAnimatorState extends State<AutomatedAnimator> with SingleTickerProviderStateMixin {
  _AutomatedAnimatorState();
  AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(vsync: this, duration: widget.duration)..addListener(() => setState(() {}));
    if (widget.animateToggle == true) controller.forward();
    if (widget.doRepeatAnimation == true) controller.repeat();
  }

  @override
  void didUpdateWidget(AutomatedAnimator oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.animateToggle == true) {
      controller.forward();
      return;
    }
    controller.reverse();
  }

  @override
  Widget build(BuildContext context) {
    return widget.buildWidget(controller.value);
  }
}

//*======================================================================
//* Additional functions to allow custom periodicity of animations
//*======================================================================

//*======================================================================
//* varies (parameterVariation) a paramter (parameterBase) based on an
//* animation position (position), broken into a number of parts
//* (numberBreaks).
//* the animation reverses at the halfway point (0.5)
//*
//* returns a value of 0.0 - 1.0
//*======================================================================

double reversingSplitParameters({
  @required double position,
  @required double numberBreaks,
  @required double parameterBase,
  @required double parameterVariation,
  @required double reversalPoint,
}) {
  assert(reversalPoint <= 1.0 && reversalPoint >= 0.0, "reversalPoint must be a number between 0.0 and 1.0");
  final double finalAnimationPosition = breakAnimationPosition(position, numberBreaks);

  if (finalAnimationPosition <= 0.5) {
    return parameterBase - (finalAnimationPosition * 2 * parameterVariation);
  } else {
    return parameterBase - ((1 - finalAnimationPosition) * 2 * parameterVariation);
  }
}

//*======================================================================
//* Breaks down a long animation controller value into a number of
//* smaller animations,
//* used for creating a single looping animation with multiple
//* sub animations with different periodicites that are able to
//* maintain a consistent unbroken loop
//*
//* Returns a value of 0.0 - 1.0 based on a given animationPosition
//* split into a discrete number of breaks (numberBreaks)
//*======================================================================

double breakAnimationPosition(double position, double numberBreaks) {
  double finalAnimationPosition = 0;
  final double breakPoint = 1.0 / numberBreaks;

  for (var i = 0; i < numberBreaks; i++) {
    if (position <= breakPoint * (i + 1)) {
      finalAnimationPosition = (position - i * breakPoint) * numberBreaks;
      break;
    }
  }

  return finalAnimationPosition;
}

class WaveLoadingBubble extends StatelessWidget {
  const WaveLoadingBubble({
    this.bubbleDiameter = 200.0,
    this.loadingCircleWidth = 10.0,
    this.waveInsetWidth = 5.0,
    this.waveHeight = 10.0,
    this.foregroundWaveColor = Colors.lightBlue,
    this.backgroundWaveColor = Colors.blue,
    this.loadingWheelColor = Colors.white,
    this.foregroundWaveVerticalOffset = 10.0,
    this.backgroundWaveVerticalOffset = 0.0,
    this.period = 0.0,
    Key key,
  }) : super(key: key);

  final double bubbleDiameter;
  final double loadingCircleWidth;
  final double waveInsetWidth;
  final double waveHeight;
  final Color foregroundWaveColor;
  final Color backgroundWaveColor;
  final Color loadingWheelColor;
  final double foregroundWaveVerticalOffset;
  final double backgroundWaveVerticalOffset;
  final double period;

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: WaveLoadingBubblePainter(
        bubbleDiameter: bubbleDiameter,
        loadingCircleWidth: loadingCircleWidth,
        waveInsetWidth: waveInsetWidth,
        waveHeight: waveHeight,
        foregroundWaveColor: foregroundWaveColor,
        backgroundWaveColor: backgroundWaveColor,
        loadingWheelColor: loadingWheelColor,
        foregroundWaveVerticalOffset: foregroundWaveVerticalOffset,
        backgroundWaveVerticalOffset: backgroundWaveVerticalOffset,
        period: period,
      ),
    );
  }
}

class WaveLoadingBubblePainter extends CustomPainter {
  WaveLoadingBubblePainter({
    this.bubbleDiameter,
    this.loadingCircleWidth,
    this.waveInsetWidth,
    this.waveHeight,
    this.foregroundWaveColor,
    this.backgroundWaveColor,
    this.loadingWheelColor,
    this.foregroundWaveVerticalOffset,
    this.backgroundWaveVerticalOffset,
    this.period,
  })  : foregroundWavePaint = Paint()..color = foregroundWaveColor,
        backgroundWavePaint = Paint()..color = backgroundWaveColor,
        loadingCirclePaint = Paint()
          ..shader = SweepGradient(
            colors: [
              Colors.transparent,
              loadingWheelColor,
              Colors.transparent,
            ],
            stops: [0.0, 0.9, 1.0],
            startAngle: 0,
            endAngle: math.pi * 1,
            transform: GradientRotation(period * math.pi * 2 * 5),
          ).createShader(Rect.fromCircle(
            center: Offset(0.0, 0.0),
            radius: bubbleDiameter / 2,
          ));

  final double bubbleDiameter;
  final double loadingCircleWidth;
  final double waveInsetWidth;
  final double waveHeight;
  final Paint foregroundWavePaint;
  final Paint backgroundWavePaint;
  final Paint loadingCirclePaint;
  final Color foregroundWaveColor;
  final Color backgroundWaveColor;
  final Color loadingWheelColor;
  final double foregroundWaveVerticalOffset;
  final double backgroundWaveVerticalOffset;
  final double period;

  @override
  void paint(Canvas canvas, Size size) {
    final double loadingBubbleRadius = (bubbleDiameter / 2);
    final double insetBubbleRadius = loadingBubbleRadius - waveInsetWidth;
    final double waveBubbleRadius = insetBubbleRadius - loadingCircleWidth;

    Path backgroundWavePath = WavePathHorizontal(
      amplitude: waveHeight,
      period: 1.0,
      startPoint: Offset(0.0 - waveBubbleRadius, 0.0 + backgroundWaveVerticalOffset),
      width: bubbleDiameter,
      crossAxisEndPoint: waveBubbleRadius,
      doClosePath: true,
      phaseShift: period * 2 * 5,
    ).build();

    Path foregroundWavePath = WavePathHorizontal(
      amplitude: waveHeight,
      period: 1.0,
      startPoint: Offset(0.0 - waveBubbleRadius, 0.0 + foregroundWaveVerticalOffset),
      width: bubbleDiameter,
      crossAxisEndPoint: waveBubbleRadius,
      doClosePath: true,
      phaseShift: -period * 2 * 5,
    ).build();

    Path circleClip = Path()..addRRect(RRect.fromLTRBXY(-waveBubbleRadius, -waveBubbleRadius, waveBubbleRadius, waveBubbleRadius, waveBubbleRadius, waveBubbleRadius));

    //Path insetCirclePath = Path()..addRRect(RRect.fromLTRBXY(-insetBubbleRadius, -insetBubbleRadius, insetBubbleRadius, insetBubbleRadius, insetBubbleRadius, insetBubbleRadius));
    //Path loadingCirclePath = Path()..addRRect(RRect.fromLTRBXY(-loadingBubbleRadius, -loadingBubbleRadius, loadingBubbleRadius, loadingBubbleRadius, loadingBubbleRadius, loadingBubbleRadius));

    // canvas.drawPath(Path.combine(PathOperation.difference, loadingCirclePath, insetCirclePath), loadingCirclePaint);
    canvas.clipPath(circleClip, doAntiAlias: true);
    canvas.drawPath(backgroundWavePath, backgroundWavePaint);
    canvas.drawPath(foregroundWavePath, foregroundWavePaint);
  }

  @override
  bool shouldRepaint(WaveLoadingBubblePainter oldDelegate) => true;

  @override
  bool shouldRebuildSemantics(WaveLoadingBubblePainter oldDelegate) => false;
}

class WavePathHorizontal {
  WavePathHorizontal({
    @required this.width,
    @required this.amplitude,
    @required this.period,
    @required this.startPoint,
    this.phaseShift = 0.0,
    this.doClosePath = false,
    this.crossAxisEndPoint = 0,
  }) : assert(crossAxisEndPoint != null || doClosePath == false, "if doClosePath is true you must provide an end point (crossAxisEndPoint)");

  final double width;
  final double amplitude;
  final double period;
  final Offset startPoint;
  final double crossAxisEndPoint; //*
  final double phaseShift; //* shift the starting value of the wave, in radians, repeats every 2 radians
  final bool doClosePath;

  Path build() {
    double startPointX = startPoint.dx;
    double startPointY = startPoint.dy;
    Path returnPath = new Path();
    returnPath.moveTo(startPointX, startPointY);

    for (double i = 0; i <= width; i++) {
      returnPath.lineTo(
        i + startPointX,
        startPointY + amplitude * math.sin((i * 2 * period * math.pi / width) + phaseShift * math.pi),
      );
    }
    if (doClosePath == true) {
      returnPath.lineTo(startPointX + width, crossAxisEndPoint);
      returnPath.lineTo(startPointX, crossAxisEndPoint);
      returnPath.close();
    }
    return returnPath;
  }
}

RelatedFlutter Resource

A Simple Hacker News reader app built in Flutter
Flutter Apps

A Simple Hacker News reader app built in Flutter

September 15, 2020
A Flutter package that detects credit card types from the first input
Card

A Flutter package that detects credit card types from the first input

September 7, 2020
A Simple & Beautiful ToDo App Made with Flutter
Flutter Apps

A Simple & Beautiful ToDo App Made with Flutter

September 1, 2020
A Simple Analog Clock Built with Flutter (Painter)
World Clock

A Simple Analog Clock Built with Flutter (Painter)

September 1, 2020
Storify – An App to Add descriptions to songs in your Spotify playlists
Flutter Apps

Storify – An App to Add descriptions to songs in your Spotify playlists

September 1, 2020
A Mobile Banking App UI made with Flutter
Finance App

A Mobile Banking App UI made with Flutter

August 20, 2020
Next Post
Flutter Shopping eCommerce App UI Kit + Source Code

Flutter Shopping eCommerce App UI Kit + Source Code

A Mobile Banking App UI made with Flutter

A Mobile Banking App UI made with Flutter

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recommended Stories

An Analog & Digital Display World Clock App + Source Code

An Analog & Digital Display World Clock App + Source Code

August 8, 2020
A BMI Calculator app developed with Flutter + Source Code

A BMI Calculator app developed with Flutter + Source Code

September 7, 2020
flutter_auth_buttons

Social Media Sign-In Buttons in Flutter (Facebook, Twitter, Google, etc)

June 27, 2020

Popular Stories

  • FLUTTER-FUTUER-APP-DEVELOPMENT

    Why Google Flutter Is the Future of Mobile App Development

    0 shares
    Share 0 Tweet 0
  • Container Widget & Box Model in Flutter – All You Need To Know

    0 shares
    Share 0 Tweet 0
  • Create a Beautiful & Customized Tab/Toggle in Flutter

    0 shares
    Share 0 Tweet 0
  • Flutter Future, Await, and Async – All You Need To Know

    0 shares
    Share 0 Tweet 0
  • Flutter Form & FormField Widget – Implementation & Example

    0 shares
    Share 0 Tweet 0
  • HOME
  • ABOUT US
  • CONTACT US
  • DMCA
  • TERMS & CONDITIONS
  • PRIVACY POLICY
Call us: +234 705 505 5426

© 2020 FlutterFix - Best Flutter Tool, Library, Source Code, Forum and Tutorials FlutterFix.

No Result
View All Result
  • Media
    • Music
    • Video
    • Images
    • Gallery
  • Flutter Apps
    • Finance App
    • Weather App
    • Video App
    • Music App
    • Shopping App
    • World Clock
    • ECommerce
    • Mobile Web Browser
  • Menu
    • Buttons
    • App Bar
    • Floating Action Button
    • Toast
    • Tab Bar
    • Swipe
  • Components
    • Developers Point
    • Animations
    • Loading
    • Flutter Packages
    • WebView
  • Inputs
    • Forms
    • TextField
    • Toggle Button
    • Download
    • Widgets
  • Games
  • UI
    • Material Design
    • Card
    • Flutter Widgets
    • Neomorphism
    • Navigation
  • Navigation
  • Q&A
  • Dart
    • Flutter Methods

© 2020 FlutterFix - Best Flutter Tool, Library, Source Code, Forum and Tutorials FlutterFix.

Welcome Back!

Login to your account below

Forgotten Password? Sign Up

Create New Account!

Fill the forms bellow to register

All fields are required. Log In

Retrieve your password

Please enter your username or email address to reset your password.

Log In