How to create the left to right or top to bottom overlay splattering animation in Flutter

Viewed 4932

Unable to create the overlay splattering animation above the layout in flutter.

enter image description here

class AnimateContainer extends StatelessWidget {
  final String assetPath;

  AnimateContainer(this.assetPath);
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 300,
      height: 100,
      child: Image.asset(
        'assets/$assetPath',
      ),
    );
  }
}
3 Answers

Added

I updated code to look like attached gif.


You can use 'AnimatedPositioned' Widget like below.

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: "App",
      theme: new ThemeData(primarySwatch: Colors.amber),
      home: Test(),
    );
  }
}

class Test extends StatefulWidget {
  @override
  _TestState createState() => _TestState();
}

class _TestState extends State<Test> {
  double rightValue = 0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: "Title",
      theme: new ThemeData(primarySwatch: Colors.amber),
      home: Scaffold(
        body: SafeArea(
          child: Stack(
            children: <Widget>[
              Container(
                decoration: BoxDecoration(
                  color: Colors.transparent,
                  image: DecorationImage(
                    fit: BoxFit.fill,
                    image: AssetImage(
                      'assets/bg.png',
                    ),
                  ),
                ),
                height: 200.0,
              ),
              AnimatedPositioned(
                // left: 0,
                left: 70 + rightValue,
                duration: Duration(milliseconds: 1000),
                child: Center(
                  child: Container(
                    color: Colors.black.withOpacity(0.5),
                    width: MediaQuery.of(context).size.width,
                    height: 200.0,
                  ),
                ),
              ),
              AnimatedPositioned(
                // left: 0,
                left: rightValue,
                duration: Duration(milliseconds: 1000),
                child: Center(
                  child: ShaderMask(
                    shaderCallback: (rect) {
                      return LinearGradient(
                        begin: Alignment.centerRight,
                        end: Alignment.centerLeft,
                        colors: [Color(0xFF45ced5), Colors.transparent],
                      ).createShader(
                          Rect.fromLTRB(0, 0, rect.width, rect.height));
                    },
                    blendMode: BlendMode.dstIn,
                    child: Container(
                      color: Color(0xFF45ced5),
                      width: 70.0,
                      height: 200.0,
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            setState(() {
              rightValue = MediaQuery.of(context).size.width;
            });
          },
          child: Icon(Icons.navigation),
          backgroundColor: Colors.green,
        ),
      ),
    );
  }
}

enter image description here

I add a new answer for repeating.
I hope it help you.
For repeating, I use Animator package.
https://pub.dev/packages/animator

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:animator/animator.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: "App",
      theme: new ThemeData(primarySwatch: Colors.amber),
      home: Test(),
    );
  }
}

class Test extends StatefulWidget {
  @override
  _TestState createState() => _TestState();
}

class _TestState extends State<Test> {
  double rightValue = 0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: "Title",
      theme: new ThemeData(primarySwatch: Colors.amber),
      home: Scaffold(
        body: SafeArea(
          child: Animator(
              tween: Tween<double>(
                  begin: 0.0, end: MediaQuery.of(context).size.width),
              repeats: 0,
              duration: const Duration(milliseconds: 1000),
              builder: (context, animatorState, child) {
                return Stack(
                  children: <Widget>[
                    Container(
                      decoration: BoxDecoration(
                        color: Colors.transparent,
                        image: DecorationImage(
                          fit: BoxFit.fill,
                          image: AssetImage(
                            'assets/bg.png',
                          ),
                        ),
                      ),
                      height: 200.0,
                    ),
                    Positioned(
                      // left: 0,
                      left: 70 + animatorState.value,
                      child: Center(
                        child: Container(
                          color: Colors.black.withOpacity(0.5),
                          width: MediaQuery.of(context).size.width,
                          height: 200.0,
                        ),
                      ),
                    ),
                    Positioned(
                      // left: 0,
                      left: animatorState.value,
                      child: Center(
                        child: ShaderMask(
                          shaderCallback: (rect) {
                            return LinearGradient(
                              begin: Alignment.centerRight,
                              end: Alignment.centerLeft,
                              colors: [Color(0xFF45ced5), Colors.transparent],
                            ).createShader(
                                Rect.fromLTRB(0, 0, rect.width, rect.height));
                          },
                          blendMode: BlendMode.dstIn,
                          child: Container(
                            color: Color(0xFF45ced5),
                            width: 70.0,
                            height: 200.0,
                          ),
                        ),
                      ),
                    ),
                  ],
                );
              }),
        ),
      ),
    );
  }
}

You can use a Stack with a Container with decoration and animate it's size repeatly with an AnimationController

class MyWidget extends StatefulWidget {
  @override
  MyWidgetState createState() => MyWidgetState();
}

class MyWidgetState extends State<MyWidget>
    with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation<double> myTween;

  @override
  void initState() {
    super.initState();
    // the controller of the animation, cotnrol the duration, add a curve if you want
    controller = AnimationController(
        vsync: this,
        animationBehavior: AnimationBehavior.preserve,
        duration: const Duration(milliseconds: 500));
    //A tween with a beginning value of 1 and ends in zero (to make it looks like go from left to right)
    myTween = Tween<double>(begin: 1, end: 0).animate(controller);
    controller.repeat(); //repeat, move forward or backward, whatever you want
  }

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

  @override
  Widget build(BuildContext context) {
    return SizedBox(
        width: 300,
        height: 100,
        child: Stack(children: [
          Image.asset('assets/$assetPath'),
          Align(
            alignment: Alignment.centerRight, //so it knows it should move the the alingment which is the right, just like your gif
              child: SizeTransition(
                sizeFactor: myTween,
                axis: Axis.horizontal, //to change size in the X direction
                axisAlignment: -1.0, //from the start to finish (left to right)
                // this value shrinks the black color and make it looks that the blue bar is moving to the right alignment
                child: Container(
                  decoration: BoxDecoration(
                  gradient: LinearGradient(
                    colors: [
                      Colors.transparent,
                      Color(0xFF45ced5).withOpacity(0.5),
                      Color(0xFF45ced5),
                      Colors.black54,
                      Colors.black54
                    ],
                  begin: Alignment.centerLeft,
                  end: Alignment.centerRight,
                  stops: [0.0, 0.1, 0.15, 0.15, 1.0], //you can change the end of the tween to 0.15 so the color never dissapears
              )),
            )
          )
          )
        ])
    );
  }
}
Related