如何在 Flutter 中创建从左到右或从上到下的叠加飞溅动画

Posted

技术标签:

【中文标题】如何在 Flutter 中创建从左到右或从上到下的叠加飞溅动画【英文标题】:How to create the left to right or top to bottom overlay splattering animation in Flutter 【发布时间】:2020-10-09 08:46:50 【问题描述】:

无法在 Flutter 布局上方创建叠加飞溅动画。

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',
      ),
    );
  

【问题讨论】:

你可以试试微光包! 【参考方案1】:

添加

我更新了代码,使其看起来像附加的 gif。


您可以使用如下所示的“AnimatedPositioned”小部件。

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,
        ),
      ),
    );
  


【讨论】:

请仔细查看动画并发表评论。我想用它代替水平进度条。谢谢! @jazzbpn 我只是想如果我给一个提示你可以做到。所以我做了一个示例代码。 太棒了。我们可以让它像水平进度条一样一直加载到特定任务结束吗? 我修改了代码。所以现在代码与 GIF 图像相同。我刚刚使用了“AnimatedPositioned”小部件。 我们能不能让它像水平进度条一样一直加载到特定任务结束?【参考方案2】:

我添加了一个新的重复答案。 希望对你有帮助。 对于重复,我使用 Animator 包。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,
                          ),
                        ),
                      ),
                    ),
                  ],
                );
              ),
        ),
      ),
    );
  

【讨论】:

【参考方案3】:

您可以将 Stack 与带有装饰的 Container 一起使用,并使用 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
              )),
            )
          )
          )
        ])
    );
  

【讨论】:

以上是关于如何在 Flutter 中创建从左到右或从上到下的叠加飞溅动画的主要内容,如果未能解决你的问题,请参考以下文章

从上到下打印二叉树,同节点的从左到右。

从上到下,从左到右对页面(2D 平面)上的框进行排序。 (Python)

CSS 列,从上到下然后从左到右

python 用于2D阵列的从左到右从上到下遍历的指数生成器

关于小班从上到下,从左到右自动编号问题

从上到下按层打印二叉树,每层打印顺序从左到右