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多平台公布