Flutter로 앱에 애니메이션 적용하기

게시 됨: 2022-03-10
빠른 요약 ↬ Flutter는 플랫폼 간 앱에 대한 뛰어난 애니메이션 지원을 제공합니다. 이 기사에서는 앱에 애니메이션을 추가하는 새롭고 독창적이고 더 쉬운 방법을 살펴봅니다. 이 새로운 "애니메이션 및 모션" 위젯은 정확히 무엇이며 간단하고 복잡한 애니메이션을 추가하는 데 어떻게 사용할 수 있습니까?

모든 플랫폼용 앱은 직관적이고 보기 좋으며 사용자 상호 작용에 대한 즐거운 피드백을 제공할 때 칭찬을 받습니다. 애니메이션은 바로 그 방법 중 하나입니다.

크로스 플랫폼 프레임워크인 Flutter는 지난 2년 동안 웹 및 데스크톱 지원을 포함하도록 발전했습니다. 그것으로 개발된 앱이 매끄럽고 보기 좋다는 평판을 얻었습니다. 풍부한 애니메이션 지원, 선언적 UI 작성 방식, "Hot Reload" 및 기타 기능을 통해 이제 완전한 크로스 플랫폼 프레임워크입니다.

Flutter를 처음 시작하고 애니메이션을 추가하는 비 전통적인 방법을 배우고 싶다면 올바른 위치에 있습니다. 애니메이션을 추가하는 암시적 방법인 애니메이션 및 모션 위젯의 영역을 탐색할 것입니다.

Flutter는 위젯의 개념을 기반으로 합니다. 앱의 각 시각적 구성 요소는 위젯입니다. Android의 보기라고 생각하면 됩니다. Flutter는 Animation 클래스, 관리를 위한 "AnimationController" 개체 및 데이터 범위를 보간하기 위한 "Tween"을 사용하여 애니메이션 지원을 제공합니다. 이 세 가지 구성 요소가 함께 작동하여 부드러운 애니메이션을 제공합니다. 이를 위해서는 애니메이션을 수동으로 생성하고 관리해야 하므로 명시적 애니메이션 방식이라고 합니다.

이제 애니메이션 및 모션 위젯을 소개하겠습니다. Flutter는 본질적으로 애니메이션을 지원하는 수많은 위젯을 제공합니다. 모든 애니메이션이 이 범주의 위젯에서 처리되므로 애니메이션 개체나 컨트롤러를 만들 필요가 없습니다. 필요한 애니메이션에 적절한 위젯을 선택하고 위젯의 속성 값을 전달하여 애니메이션을 적용하면 됩니다. 이 기술은 암시적인 애니메이션 방법입니다.

Flutter의 애니메이션 계층. (큰 미리보기)

위의 차트는 Flutter의 애니메이션 계층 구조, 명시적 및 암시적 애니메이션이 모두 지원되는 방식을 대략적으로 설명합니다.

이 기사에서 다루는 애니메이션 위젯은 다음과 같습니다.

  • AnimatedOpacity
  • AnimatedCrossFade
  • AnimatedAlign
  • AnimatedPadding
  • AnimatedSize
  • AnimatedPositioned .

Flutter는 미리 정의된 애니메이션 위젯뿐만 아니라 암시적으로 애니메이션된 사용자 지정 위젯을 만드는 데 사용할 수 있는 AnimatedWidget 이라는 일반 위젯도 제공합니다. 이름에서 알 수 있듯이 이러한 위젯은 애니메이션 및 모션 위젯 범주에 속하므로 애니메이션을 훨씬 부드럽고 보기 좋게 만드는 몇 가지 공통 속성이 있습니다.

나중에 모든 예제에서 사용할 이러한 공통 속성을 지금 설명하겠습니다.

  • duration
    매개변수에 애니메이션을 적용할 기간입니다.
  • reverseDuration
    역방향 애니메이션의 지속 시간입니다.
  • curve
    매개변수를 애니메이션할 때 적용할 곡선입니다. 보간된 값은 선형 분포에서 가져오거나 지정된 경우 곡선에서 가져올 수 있습니다.

"Quoted"라고 하는 간단한 앱을 만들어 여정을 시작해 보겠습니다. 앱이 시작될 때마다 임의의 인용문이 표시됩니다. 주의해야 할 두 가지 사항: 첫째, 이러한 모든 인용문은 애플리케이션에서 하드코딩됩니다. 둘째, 사용자 데이터가 저장되지 않습니다.

참고 : 이 예제의 모든 파일은 GitHub에서 찾을 수 있습니다.

점프 후 더! 아래에서 계속 읽기 ↓

시작하기

Flutter를 설치해야 하며 계속 진행하기 전에 기본 흐름에 익숙해져야 합니다. 시작하기에 좋은 곳은 "진정한 플랫폼 간 모바일 개발을 위해 Google의 Flutter 사용"입니다.

Android Studio에서 새 Flutter 프로젝트를 만듭니다.

Android Studio의 새로운 flutter 프로젝트 메뉴. (큰 미리보기)

그러면 프로젝트 기본 사항을 구성할 수 있는 새 프로젝트 마법사가 열립니다.

Flutter 프로젝트 유형 선택 화면입니다. (큰 미리보기)

프로젝트 유형 선택 화면에는 각각 특정 시나리오에 맞는 다양한 유형의 Flutter 프로젝트가 있습니다. 이 튜토리얼에서는 Flutter 애플리케이션 을 선택하고 다음 을 누릅니다.

이제 프로젝트 이름 및 경로, 회사 도메인 등 일부 프로젝트 관련 정보를 입력해야 합니다. 아래 이미지를 살펴보십시오.

Flutter 애플리케이션 구성 화면입니다. (큰 미리보기)

프로젝트 이름, Flutter SDK 경로, 프로젝트 위치 및 선택적 프로젝트 설명을 추가합니다. 다음 을 누릅니다.

Flutter 애플리케이션 패키지 이름 화면. (큰 미리보기)

각 애플리케이션(Android 또는 iOS)에는 고유한 패키지 이름이 필요합니다. 일반적으로 웹사이트 도메인의 반대 방향을 사용합니다. 예: com.google 또는 com.yahoo. Finish 를 눌러 작동하는 Flutter 애플리케이션을 생성합니다.

생성된 샘플 프로젝트입니다. (큰 미리보기)

프로젝트가 생성되면 위와 같은 화면이 표시되어야 합니다. main.dart 파일을 엽니다(스크린샷에서 강조 표시됨). 이것은 주요 응용 프로그램 파일입니다. 샘플 프로젝트는 그 자체로 완전하며 수정 없이 에뮬레이터 또는 물리적 장치에서 직접 실행할 수 있습니다.

main.dart 파일의 내용을 다음 코드 조각으로 바꿉니다.

 import 'package:animated_widgets/FirstPage.dart'; import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Animated Widgets', debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, accentColor: Colors.redAccent, ), home: FirstPage(), ); } }

이 코드는 새 앱 생성과 관련된 간단한 정보를 추가하여 main.dart 파일을 정리합니다. MyApp 클래스는 Material Design을 준수하는 앱을 만들기 위한 기본 구조를 제공하는 MaterialApp 위젯이라는 개체를 반환합니다. 코드를 보다 구조화하려면 lib 폴더 안에 FirstPage.dartQuotes.dart 라는 두 개의 새 dart 파일을 만듭니다.

FirstPage.dart 파일. (큰 미리보기)

FirstPage.dart 에는 Quoted 앱에 필요한 모든 시각적 요소(위젯)를 담당하는 모든 코드가 포함됩니다. 모든 애니메이션은 이 파일에서 처리됩니다.

참고 : 기사 뒷부분에서 각 애니메이션 위젯에 대한 모든 코드 조각이 Scaffold 위젯의 자식으로 이 파일에 추가됩니다. 자세한 내용은 GitHub의 이 예제가 유용할 수 있습니다.

FirstPage.dart 에 다음 코드를 추가하여 시작하십시오. 이것은 나중에 다른 것들이 추가될 부분 코드입니다.

 import 'dart:math'; import 'package:animated_widgets/Quotes.dart'; import 'package:flutter/material.dart'; class FirstPage extends StatefulWidget { @override State createState() { return FirstPageState(); } } class FirstPageState extends State with TickerProviderStateMixin { bool showNextButton = false; bool showNameLabel = false; bool alignTop = false; bool increaseLeftPadding = false; bool showGreetings = false; bool showQuoteCard = false; String name = ''; double screenWidth; double screenHeight; String quote; @override void initState() { super.initState(); Random random = new Random(); int quoteIndex = random.nextInt(Quotes.quotesArray.length); quote = Quotes.quotesArray[quoteIndex]; } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; return Scaffold( appBar: _getAppBar(), body: Stack( children: [ // All other children will be added here. // In this article, all the children widgets are contained // in their own separate methods. // Just method calls should be added here for the respective child. ], ), ); } } import 'dart:math'; import 'package:animated_widgets/Quotes.dart'; import 'package:flutter/material.dart'; class FirstPage extends StatefulWidget { @override State createState() { return FirstPageState(); } } class FirstPageState extends State with TickerProviderStateMixin { bool showNextButton = false; bool showNameLabel = false; bool alignTop = false; bool increaseLeftPadding = false; bool showGreetings = false; bool showQuoteCard = false; String name = ''; double screenWidth; double screenHeight; String quote; @override void initState() { super.initState(); Random random = new Random(); int quoteIndex = random.nextInt(Quotes.quotesArray.length); quote = Quotes.quotesArray[quoteIndex]; } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; return Scaffold( appBar: _getAppBar(), body: Stack( children: [ // All other children will be added here. // In this article, all the children widgets are contained // in their own separate methods. // Just method calls should be added here for the respective child. ], ), ); } } import 'dart:math'; import 'package:animated_widgets/Quotes.dart'; import 'package:flutter/material.dart'; class FirstPage extends StatefulWidget { @override State createState() { return FirstPageState(); } } class FirstPageState extends State with TickerProviderStateMixin { bool showNextButton = false; bool showNameLabel = false; bool alignTop = false; bool increaseLeftPadding = false; bool showGreetings = false; bool showQuoteCard = false; String name = ''; double screenWidth; double screenHeight; String quote; @override void initState() { super.initState(); Random random = new Random(); int quoteIndex = random.nextInt(Quotes.quotesArray.length); quote = Quotes.quotesArray[quoteIndex]; } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; return Scaffold( appBar: _getAppBar(), body: Stack( children: [ // All other children will be added here. // In this article, all the children widgets are contained // in their own separate methods. // Just method calls should be added here for the respective child. ], ), ); } } import 'dart:math'; import 'package:animated_widgets/Quotes.dart'; import 'package:flutter/material.dart'; class FirstPage extends StatefulWidget { @override State createState() { return FirstPageState(); } } class FirstPageState extends State with TickerProviderStateMixin { bool showNextButton = false; bool showNameLabel = false; bool alignTop = false; bool increaseLeftPadding = false; bool showGreetings = false; bool showQuoteCard = false; String name = ''; double screenWidth; double screenHeight; String quote; @override void initState() { super.initState(); Random random = new Random(); int quoteIndex = random.nextInt(Quotes.quotesArray.length); quote = Quotes.quotesArray[quoteIndex]; } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; return Scaffold( appBar: _getAppBar(), body: Stack( children: [ // All other children will be added here. // In this article, all the children widgets are contained // in their own separate methods. // Just method calls should be added here for the respective child. ], ), ); } }
Quotes.dart 파일. (큰 미리보기)

Quotes.dart 파일에는 모든 하드코딩된 견적 목록이 포함되어 있습니다. 여기서 주의할 점은 목록이 정적 개체라는 것입니다. 이것은 Quote 클래스의 새로운 객체를 생성하지 않고도 다른 곳에서 사용할 수 있음을 의미합니다. 위의 목록은 단순히 유틸리티 역할을 하기 때문에 이것은 의도적으로 선택되었습니다.

이 파일에 다음 코드를 추가합니다.

 class Quotes { static const quotesArray = [ "Good, better, best. Never let it rest. 'Til your good is better and your better is best", "It does not matter how slowly you go as long as you do not stop.", "Only I can change my life. No one can do it for me." ]; }

이제 프로젝트 골격이 준비되었으므로 Quoted를 좀 더 구체화해 보겠습니다.

AnimatedOpacity

앱에 개인적인 터치를 부여하려면 사용자의 이름을 아는 것이 좋을 것이므로 요청하고 다음 버튼을 표시해 보겠습니다. 사용자가 이름을 입력할 때까지 이 버튼은 숨겨져 있으며 이름이 지정되면 정상적으로 표시됩니다. 버튼에 대한 일종의 가시성 애니메이션이 필요하지만 이를 위한 위젯이 있습니까? 예, 있습니다.

AnimatedOpacity 를 입력합니다. 이 위젯은 암시적 애니메이션 지원을 추가하여 불투명도 위젯을 기반으로 합니다. 우리는 그것을 어떻게 사용합니까? 우리의 시나리오를 기억하십시오. 애니메이션 가시성으로 다음 버튼을 표시해야 합니다. AnimatedOpacity 위젯 안에 버튼 위젯을 래핑하고 적절한 값을 입력하고 애니메이션을 트리거하는 조건을 추가하면 Flutter가 나머지를 처리할 수 있습니다.

 _getAnimatedOpacityButton() { return AnimatedOpacity( duration: Duration(seconds: 1), reverseDuration: Duration(seconds: 1), curve: Curves.easeInOut, opacity: showNextButton ? 1 : 0, child: _getButton(), ); }
다음 버튼의 불투명도 애니메이션. (큰 미리보기)

AnimatedOpacity 위젯에는 두 가지 필수 속성이 있습니다.

  • opacity
    값 1은 완전히 표시됨을 의미합니다. 0(영)은 숨김을 의미합니다. 애니메이션을 만드는 동안 Flutter는 이 두 극단 사이의 값을 보간합니다. 가시성을 변경하여 애니메이션을 트리거하는 조건이 어떻게 배치되는지 확인할 수 있습니다.
  • child
    가시성을 애니메이션화할 자식 위젯입니다.

이제 암시적 위젯으로 가시성 애니메이션을 추가하는 것이 얼마나 간단한지 이해해야 합니다. 그리고 그러한 모든 위젯은 동일한 지침을 따르며 사용하기 쉽습니다. 다음으로 넘어갑시다.

AnimatedCrossFade

사용자 이름이 있지만 위젯은 여전히 ​​입력을 기다리고 있습니다. 이전 단계에서 사용자가 이름을 입력하면 다음 버튼이 표시됩니다. 이제 사용자가 버튼을 누를 때 입력 수락을 중지하고 입력된 이름을 표시하고 싶습니다. 물론 이를 수행하는 방법은 여러 가지가 있지만 아마도 입력 위젯을 숨기고 편집할 수 없는 텍스트 위젯을 표시할 수 있습니다. AnimatedCrossFade 위젯을 사용하여 시도해 보겠습니다.

이 위젯은 위젯이 어떤 조건에 따라 그들 사이에서 크로스페이드되기 때문에 두 개의 자식이 필요합니다. 이 위젯을 사용하는 동안 염두에 두어야 할 한 가지 중요한 사항은 두 자식의 너비가 같아야 한다는 것입니다. 높이가 다르면 키가 큰 위젯이 아래쪽에서 잘립니다. 이 시나리오에서는 두 개의 위젯(입력 및 레이블)이 자식으로 사용됩니다.

 _getAnimatedCrossfade() { return AnimatedCrossFade( duration: Duration(seconds: 1), alignment: Alignment.center, reverseDuration: Duration(seconds: 1), firstChild: _getNameInputWidget(), firstCurve: Curves.easeInOut, secondChild: _getNameLabelWidget(), secondCurve: Curves.easeInOut, crossFadeState: showNameLabel ? CrossFadeState.showSecond : CrossFadeState.showFirst, ); }
입력 위젯과 이름 위젯 간의 크로스 페이딩. (큰 미리보기)

이 위젯에는 다른 필수 매개변수 세트가 필요합니다.

  • crossFadeState
    이 상태는 어떤 자식을 보여줄지 결정합니다.
  • firstChild
    이 위젯의 ​​첫 번째 자식을 지정합니다.
  • secondChild
    두 번째 자식을 지정합니다.

AnimatedAlign

이때 이름 레이블은 화면 중앙에 위치합니다. 따옴표를 표시하려면 화면 중앙이 필요하기 때문에 상단에서 훨씬 더 잘 보일 것입니다. 간단히 말해서 이름 레이블 위젯의 정렬은 가운데에서 위로 변경되어야 합니다. 그리고 이 정렬 변경을 이전 크로스 페이드 애니메이션과 함께 애니메이션으로 만드는 것이 좋지 않을까요? 해보자

항상 그렇듯이 이를 달성하기 위해 여러 기술을 사용할 수 있습니다. 이름 레이블 위젯은 이미 가운데 정렬되어 있으므로 정렬에 애니메이션을 적용하는 것은 위젯의 상단 및 왼쪽 값을 조작하는 것보다 훨씬 간단합니다. AnimatedAlign 위젯은 이 작업에 적합합니다.

이 애니메이션을 시작하려면 트리거가 필요합니다. 이 위젯의 ​​유일한 목적은 정렬 변경을 애니메이션으로 만드는 것이므로 하위 속성 추가, 정렬 설정, 정렬 변경 트리거 등 몇 가지 속성만 있습니다.

 _getAnimatedAlignWidget() { return AnimatedAlign( duration: Duration(seconds: 1), curve: Curves.easeInOut, alignment: alignTop ? Alignment.topLeft : Alignment.center, child: _getAnimatedCrossfade(), ); }
이름 위젯의 정렬 애니메이션입니다. (큰 미리보기)

두 가지 필수 속성만 있습니다.

  • 어린이:
    정렬이 수정될 자식입니다.
  • 조정:
    필수 정렬 값입니다.

이 위젯은 정말 간단하지만 결과는 우아합니다. 또한 두 개의 서로 다른 애니메이션 위젯을 사용하여 더 복잡한 애니메이션을 만드는 방법을 쉽게 보았습니다. 이것이 애니메이션 위젯의 아름다움입니다.

AnimatedPadding

이제 다양한 종류의 애니메이션 위젯을 사용하여 많은 노력 없이 부드럽게 애니메이션된 사용자 이름이 맨 위에 표시됩니다. 이름 앞에 "안녕하세요"라는 인사말을 추가해 보겠습니다. 상단에 값이 "Hi"인 텍스트 위젯을 추가하면 아래 이미지와 같이 인사말 텍스트 위젯과 겹치게 됩니다.

인사말 및 이름 위젯이 겹칩니다. (큰 미리보기)

이름 텍스트 위젯의 왼쪽에 패딩이 있으면 어떻게 될까요? 왼쪽 패딩을 늘리면 확실히 효과가 있지만 잠깐만요. 애니메이션으로 패딩을 늘릴 수 있습니까? 예, 이것이 AnimatedPadding 이 하는 일입니다. 이 모든 것을 훨씬 더 보기 좋게 만들기 위해 인사말 텍스트 위젯이 페이드 인되고 동시에 이름 텍스트 위젯의 패딩이 증가하도록 합시다.

 _getAnimatedPaddingWidget() { return AnimatedPadding( duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn, padding: increaseLeftPadding ? EdgeInsets.only(left: 28.0) : EdgeInsets.only(left: 0), child: _getAnimatedCrossfade(), ); }

위의 애니메이션은 이전 애니메이션 정렬이 완료된 후에만 발생해야 하므로 이 애니메이션 트리거를 지연해야 합니다. 주제에서 잠시 벗어나면 지연을 추가하는 인기 있는 메커니즘에 대해 이야기할 수 있는 좋은 시간입니다. Flutter는 이러한 기술을 몇 가지 제공하지만 Future.delayed 생성자는 더 간단하고 명확하며 읽기 쉬운 접근 방식 중 하나입니다. 예를 들어, 1초 후에 코드를 실행하려면:

 Future.delayed(Duration(seconds: 1), (){ sum = a + b; // This sum will be calculated after 1 second. print(sum); });

지연 시간이 이미 알려져 있으므로(이전 애니메이션 지속 시간에서 계산됨) 이 간격 후에 애니메이션이 트리거될 수 있습니다.

 // Showing “Hi” after 1 second - greetings visibility trigger. _showGreetings() { Future.delayed(Duration(seconds: 1), () { setState(() { showGreetings = true; }); }); } // Increasing the padding for name label widget after 1 second - increase padding trigger. _increaseLeftPadding() { Future.delayed(Duration(seconds: 1), () { setState(() { increaseLeftPadding = true; }); }); }
이름 위젯의 패딩 애니메이션입니다. (큰 미리보기)

이 위젯에는 두 가지 필수 속성만 있습니다.

  • child
    패딩이 적용될 이 위젯 내부의 자식입니다.
  • padding
    추가할 공간의 양입니다.

AnimatedSize

오늘날 어떤 종류의 애니메이션이 있는 모든 앱에는 사용자의 관심을 끌기 위해 시각적 구성 요소를 확대하거나 축소하는 기능이 포함됩니다(일반적으로 애니메이션 크기 조정이라고 함). 여기에서 동일한 기술을 사용하지 않는 이유는 무엇입니까? 화면 중앙에서 확대되는 동기 부여 인용문을 사용자에게 보여줄 수 있습니다. 확대 및 축소 효과를 활성화하고 자식 크기를 변경하여 제어하는 AnimatedSize 위젯을 소개하겠습니다.

이 위젯은 필수 매개변수와 관련하여 다른 위젯과 약간 다릅니다. Flutter가 "Ticker"라고 부르는 것이 필요합니다. Flutter에는 새 프레임 이벤트가 트리거될 때마다 객체에 알리는 방법이 있습니다. “지금 하세요! … 지금 그것을 할! … 지금 그것을 할! …”

AnimatedSize 위젯에는 티커 제공자를 허용하는 속성( vsync )이 필요합니다. 시세 제공자를 얻는 가장 쉬운 방법은 클래스에 Mixin 을 추가하는 것입니다. 두 가지 기본 티커 제공자 구현이 있습니다. 단일 티커를 제공하는 SingleTickerProviderStateMixin ; 몇 가지를 제공하는 TickerProviderStateMixin .

Ticker의 기본 구현은 애니메이션의 프레임을 표시하는 데 사용됩니다. 이 경우 후자가 사용됩니다. 믹스인에 대한 추가 정보.

 // Helper method to create quotes card widget. _getQuoteCardWidget() { return Card( color: Colors.green, elevation: 8.0, child: _getAnimatedSizeWidget(), ); } // Helper method to create animated size widget and set its properties. _getAnimatedSizeWidget() { return AnimatedSize( duration: Duration(seconds: 1), curve: Curves.easeInOut, vsync: this, child: _getQuoteContainer(), ); } // Helper method to create the quotes container widget with different sizes. _getQuoteContainer() { return Container( height: showQuoteCard ? 100 : 0, width: showQuoteCard ? screenWidth - 32 : 0, child: Center( child: Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: Text(quote, style: TextStyle(color: Colors.white, fontWeight: FontWeight.w400, fontSize: 14),), ), ), ); } // Trigger used to show the quote card widget. _showQuote() { Future.delayed(Duration(seconds: 2), () { setState(() { showQuoteCard = true; }); }); }
따옴표 위젯의 크기 조정 애니메이션. (큰 미리보기)

이 위젯의 ​​필수 속성:

  • vsync
    애니메이션 및 프레임 변경을 조정하는 데 필요한 티커 제공자.<
  • child
    크기가 변경되는 자식은 애니메이션됩니다.

확대 및 축소 애니메이션이 이제 쉽게 길들여집니다.

AnimatedPositioned

엄청난! 인용 부호는 사용자의 주의를 끌기 위해 중앙에서 확대됩니다. 확대하는 동안 아래에서 위로 미끄러지면 어떻게 될까요? 해 보자. 이 동작에는 인용 위젯의 위치를 ​​가지고 놀고 위치 속성의 변경 사항을 애니메이션으로 만드는 작업이 포함됩니다. AnimatedPositioned 는 완벽한 후보입니다.

이 위젯은 지정된 위치가 변경될 때마다 지정된 기간 동안 자녀의 위치를 ​​자동으로 전환합니다. 한 가지 주의할 점: 상위 위젯이 "스택"인 경우에만 작동합니다. 이 위젯은 매우 간단하고 사용하기 쉽습니다. 봅시다.

 // Helper method to create the animated positioned widget. // With position changes based on “showQuoteCard” flag. _getAnimatedPositionWidget() { return AnimatedPositioned( duration: Duration(seconds: 1), curve: Curves.easeInOut, child: _getQuoteCardWidget(), top: showQuoteCard ? screenHeight/2 - 100 : screenHeight, left: !showQuoteCard ? screenWidth/2 : 12, ); }
따옴표의 크기 조정 애니메이션을 사용하여 위치를 지정합니다. (큰 미리보기)

이 위젯에는 필수 속성이 하나만 있습니다.

  • child
    위치가 변경될 위젯입니다.

자식의 크기가 위치와 함께 변경될 것으로 예상되지 않는 경우 이 위젯에 대한 보다 성능적인 대안은 SlideTransition 입니다.

다음은 전체 애니메이션입니다.

모든 애니메이션 위젯이 함께 제공됩니다. (큰 미리보기)

결론

애니메이션은 사용자 경험의 필수적인 부분입니다. 정적인 앱이나 버벅거리는 애니메이션이 있는 앱은 사용자 유지율을 낮출 뿐만 아니라 결과를 제공하는 개발자의 평판도 떨어뜨립니다.

오늘날 가장 인기 있는 앱에는 사용자를 즐겁게 하는 일종의 미묘한 애니메이션이 있습니다. 사용자 요청에 대한 애니메이션 피드백은 사용자가 더 많은 것을 탐색하도록 유도할 수도 있습니다. Flutter는 부드럽고 반응이 빠른 애니메이션을 위한 풍부한 지원을 포함하여 플랫폼 간 개발을 위한 많은 기능을 제공합니다.

Flutter는 다른 개발자의 애니메이션을 사용할 수 있는 훌륭한 플러그인 지원을 제공합니다. 이제 커뮤니티의 많은 사랑을 받아 버전 1.9로 성숙했기 때문에 Flutter는 앞으로 더 좋아질 것입니다. 지금이 Flutter를 배우기에 좋은 시기입니다!

추가 리소스

  • "애니메이션 및 모션 위젯", Flutter Docs
  • "애니메이션 소개", Flutter Docs
  • Flutter Codelabs

편집자 주 : 이 기사를 검토하는 데 도움을 주신 Ahmad Awais에게 큰 감사를 드립니다.