乐趣区

关于程序员:Flutter-实现背景-Parallax-动画

Flutter 实现背景 Parallax 动画

原文 https://arkapp.medium.com/bac…

前言

咱们将创立咱们的 Flutter 我的项目惊人的 Parallax 动画。

在本文中,咱们将实现一个简略的实用工具 widget,它将在任何 widget 之上增加 Parallax 成果。

注释

创立 Base Widget

让咱们创立咱们的根底 widget,咱们将增加 Parallax 动画。在 BaseWidget 中,咱们将从 Asset 目录增加一个图像。稍后,咱们将增加 Parallax 成果到这个图像。

import 'package:flutter/material.dart';

class BaseWidget extends StatelessWidget {const BaseWidget({super.key});

  @override
  Widget build(BuildContext context) {

    ///We will add parallax effect to this image
    return Image.asset(
      'assets/moon.webp',
      width: MediaQuery.of(context).size.width,
      height: MediaQuery.of(context).size.height,
    );
  }
}

创立 Parallax widget

当初咱们将创立一个实用工具 widget,它将为下面的 BaseWidget 增加 Parallax 成果。这将是采纳子窗口 widget 作为结构函数参数的状态窗口 widget。

import 'package:flutter/material.dart';

class ParallaxAnimationWidget extends StatefulWidget {
  final Widget child;

  const ParallaxAnimationWidget({required this.child, super.key});

  @override
  State<ParallaxAnimationWidget> createState() => _WidgetState();
}

class _WidgetState extends State<ParallaxAnimationWidget> {


  @override
  Widget build(BuildContext context) {return Container();
  }
}

当初咱们将减少子窗口 widget 的宽度。为此,咱们将首先确定子 widget 的宽度。咱们将把 GlobalKeyto 增加到子 widget 中,应用该键将获取 widget 的宽度。

import 'package:flutter/material.dart';

class ParallaxAnimationWidget extends StatefulWidget {
  final Widget child;

  const ParallaxAnimationWidget({required this.child, super.key});

  @override
  State<ParallaxAnimationWidget> createState() => _WidgetState();
}

class _WidgetState extends State<ParallaxAnimationWidget> {final childKey = GlobalKey();

  late Widget childWithKey;
  double? childBaseWidth;

  @override
  void initState() {super.initState();

    childWithKey = SizedBox(key: childKey, child: widget.child);
    fetchChildWidth();}

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [childWithKey,],
    );
  }

  fetchChildWidth() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
        final RenderBox renderBoxRed =
            childKey.currentContext?.findRenderObject() as RenderBox;
        final childSize = renderBoxRed.size;
        childBaseWidth = childSize.width;
      },
    );
  }

}

这里咱们增加了 StackWidget,而后在其中增加了子 Widget。当初,咱们将减少咱们的子窗口 widget 的宽度,以实现横跨该宽度的 Parallax 成果。

import 'package:flutter/material.dart';

class ParallaxAnimationWidget extends StatefulWidget {
  final Widget child;

  const ParallaxAnimationWidget({required this.child, super.key});

  @override
  State<ParallaxAnimationWidget> createState() => _WidgetState();
}

class _WidgetState extends State<ParallaxAnimationWidget> {

  ///We are increasing the widget width by 20% you can change according
  ///to your needs.
  final parallaxWidthPercent = 20;
  final childKey = GlobalKey();

  late Widget childWithKey;
  double? childBaseWidth;
  double? totalAdditionalParallaxWidth;
  double? rightPosition;
  double maxEndPosition = 0;
  double maxStartPosition = 0;

  @override
  void initState() {super.initState();

    childWithKey = SizedBox(key: childKey, child: widget.child);
    fetchChildWidth();}

  @override
  Widget build(BuildContext context) {

    return Stack(
      children: [
        ///Validating the width and setting new increased width.
        SizedBox(
            width:
                (childBaseWidth != null && totalAdditionalParallaxWidth != null)
                    ? (childBaseWidth! + totalAdditionalParallaxWidth!)
                    : null,
            child: childWithKey,
          ),
      ],
    );
  }


  fetchChildWidth() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
        final RenderBox renderBoxRed =
            childKey.currentContext?.findRenderObject() as RenderBox;
        final childSize = renderBoxRed.size;
        childBaseWidth = childSize.width;
        initChildPosition();},
    );
  }

  initChildPosition() {
    totalAdditionalParallaxWidth = childBaseWidth! * parallaxWidthPercent / 100;
    rightPosition = -totalAdditionalParallaxWidth! / 2;
    maxEndPosition = -childBaseWidth! + totalAdditionalParallaxWidth!;
    maxStartPosition = totalAdditionalParallaxWidth!;
    setState(() {});
  }
}

咱们曾经减少了 20% 的宽度部件,您能够依据您的须要扭转。咱们还应用新的宽度计算了动画地位参数 (right position、maxEndposition、maxStartposition)。这个新参数将在下一步中用于增加动画。

增加动画

咱们将应用 Animatedposition 来创立漂亮的 Parallax 动画。咱们将创立一个定时器,它将一直扭转咱们的子窗口 widget 的地位,以创立 Parallax 成果。

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

class ParallaxAnimationWidget extends StatefulWidget {
  final Widget child;

  const ParallaxAnimationWidget({required this.child, super.key});

  @override
  State<ParallaxAnimationWidget> createState() => _WidgetState();
}

class _WidgetState extends State<ParallaxAnimationWidget> {

  ///You can change the duration accoridng to your needs
  Duration animationDuration = const Duration(seconds: 15);
  final initialDelay = Future.delayed(const Duration(seconds: 1));
  Timer? animationTimer;

  final parallaxWidthPercent = 20;
  final childKey = GlobalKey();

  late Widget childWithKey;
  double? childBaseWidth;
  double? totalAdditionalParallaxWidth;
  double? rightPosition;
  double maxEndPosition = 0;
  double maxStartPosition = 0;

  @override
  void initState() {super.initState();
    childWithKey = SizedBox(key: childKey, child: widget.child);
    fetchChildWidth();
    initTimer();}

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [

        ///This will animate our widget between edge positions.
        AnimatedPositioned(
          right: rightPosition,
          duration: animationDuration,
          child: SizedBox(
            width:
                (childBaseWidth != null && totalAdditionalParallaxWidth != null)
                    ? (childBaseWidth! + totalAdditionalParallaxWidth!)
                    : null,
            child: childWithKey,
          ),
        ),
      ],
    );
  }

  initTimer() async {animationTimer?.cancel();
    await initialDelay;
    updateChildPosition();
    animationTimer = Timer.periodic(
      animationDuration,
      (_) => updateChildPosition(),);
  }

  ///This method will animate our widget horizontally
  updateChildPosition() async {if (rightPosition == 0) {rightPosition = maxEndPosition;} else if (rightPosition == maxEndPosition) {rightPosition = maxStartPosition;} else if (rightPosition == maxStartPosition) {rightPosition = maxEndPosition;} else {rightPosition = maxEndPosition;}
    setState(() {});
  }

  fetchChildWidth() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
        final RenderBox renderBoxRed =
            childKey.currentContext?.findRenderObject() as RenderBox;
        final childSize = renderBoxRed.size;
        childBaseWidth = childSize.width;
        initChildPosition();},
    );
  }

  initChildPosition() {
    totalAdditionalParallaxWidth = childBaseWidth! * parallaxWidthPercent / 100;
    rightPosition = -totalAdditionalParallaxWidth! / 2;
    maxEndPosition = -childBaseWidth! + totalAdditionalParallaxWidth!;
    maxStartPosition = totalAdditionalParallaxWidth!;
    setState(() {});
  }

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

    ///Closing timer on widget dispose.
    animationTimer?.cancel();}
}

应用 Parallax 动画

咱们曾经实现了 ParallaxAnimationWidget,当初咱们只须要将它增加到 BaseWidget 中就能够看到它的神奇之处了。

import 'package:parallax/base_widget.dart';
import 'package:parallax/parallax_animation_widget.dart';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {

    return const MaterialApp(

      ///Addding animation to our base widget
      home: ParallaxAnimationWidget(child: BaseWidget(),
      ),
    );
  }
}

就是这样!您曾经胜利地增加 Parallax 动画到您的 Flutter 我的项目。您能够在我的项目中的任何中央应用此 widget 来创立令人惊叹的 UI。

结束语

如果本文对你有帮忙,请转发让更多的敌人浏览。

兴许这个操作只有你 3 秒钟,对我来说是一个激励,感激。

祝你有一个美妙的一天~


© 猫哥

  • 微信 ducafecat
  • https://wiki.ducafecat.tech
  • https://video.ducafecat.tech

本文由 mdnice 多平台公布

退出移动版