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