Flutter(Flare) 最有趣使用者互動動畫沒有之一

子木_lsy發表於2020-07-04

2019年12月12日,FlutterFlutter Interact '19 上釋出瞭如何使用 RiveFlutter 製作動態可互動的動畫經驗分享,我看了之後,覺得非常有趣,因此,寫了3個小 demo,把它寫成文章記錄分享給大家。

名詞理解

首先,我們來理解幾個名詞,不然後續文章,可能看著有些暈,如下:

  • Flare:是 Flutter 的動畫外掛名稱,完整名稱是 flare_flutter 我們要在 pubspec.yaml 檔案裡引入
  • Rive:是製作 Flare 動畫的網站,它既是一個網站也是製作工具,在此網站裡有很多使用者分享 Flare 動畫供我們下載使用、Flare API使用文件、製作 Flare 動畫的視訊教程(大家也可以通過學習製作自己喜歡的動畫)等

互動動畫預覽

登入互動動畫

登入互動動畫,包含如下6種動畫:

  • idle:無任何操作時的狀態(熊的身體會上下浮動和眨眼睛)
  • test:當我們在 email 輸入框中輸入時的狀態(熊會看向輸入框,且隨著你輸入的長度旋轉頭部)
  • hands_up:當我們在 password 輸入框中輸入時的狀態 (熊會用手蒙上眼睛)
  • hands_down:當我們在 password 輸入框輸入完成時的狀態 (熊會放下雙手)
  • fail:當我們登入失敗時的狀態(熊會做出難過的表情)
  • success:當我們登入成功時的狀態(熊會做出高興的表情)

以上6種狀態,可以在 Rive 網站檢視具體動畫,點選進入檢視

下面,我們來看看案例裡實現動畫效果

idle:無任何操作時的狀態,如圖:

no-shadow
idle

test:當我們在 email 輸入框中輸入時的狀態,如圖:

no-shadow
test

hands_up:當我們在 password 輸入框中輸入時的狀態,hands_down:當我們在 password 輸入框輸入完成時的狀態,如圖:

no-shadow
hands_up & hands_down

fail:當我們登入失敗時的狀態,如圖:

no-shadow
fail

success:當我們登入成功時的狀態,如圖:

no-shadow
success

Button互動動畫

button 互動動畫,如圖:

no-shadow
button

Menu互動動畫

menu 互動動畫,如圖:

no-shadow
menu

以上所有動畫,也可以 點選觀看視訊

程式碼實現

如何用程式碼實現,分為以下2個步驟:

  • 引入外掛和資源:引入相關外掛 flare_fluttersmart_flare
  • 編寫程式碼:編寫相關程式碼

引入外掛和資源

引入外掛和資源,如下:

dependencies:
  ...
  flare_flutter: ^2.0.4  # flare 外掛
  smart_flare: any  # 對 flare API進行封裝的外掛,使用少量的程式碼即可實現互動動畫
  ...

assets:
  ...
  - assets/Teddy.flr
  - assets/button-animation.flr
  - assets/slideout-menu.flr
  ...
複製程式碼

編寫程式碼

由於,登入互動動畫稍複雜一些,在此就不展示實現的程式碼,如感興趣,可移步GitHub檢視原始碼

Button互動動畫程式碼實現

button 互動動畫程式碼實現如下:

import 'package:flutter/material.dart';
import 'package:smart_flare/actors/smart_flare_actor.dart';
import 'package:smart_flare/models.dart';

class FlareButtonDemo extends StatefulWidget {
  @override
  _FlareButtonDemoState createState() => _FlareButtonDemoState();
}

class _FlareButtonDemoState extends State<FlareButtonDemo> {
  @override
  Widget build(BuildContext context) {
    var animationWidth = 295.0;
    var animationHeight = 251.0;
    var animationWidthThirds = animationWidth / 3;
    var halfAnimationHeight = animationHeight / 2;

    var activeAreas = [

      ActiveArea(
        area: Rect.fromLTWH(0, 0, animationWidthThirds, halfAnimationHeight),
        debugArea: false,
        guardComingFrom: ['deactivate'],
        animationName: 'camera_tapped',
      ),

      ActiveArea(
          area: Rect.fromLTWH(animationWidthThirds, 0, animationWidthThirds, halfAnimationHeight),
          debugArea: false,
          guardComingFrom: ['deactivate'],
          animationName: 'pulse_tapped'),

      ActiveArea(
          area: Rect.fromLTWH(animationWidthThirds * 2, 0, animationWidthThirds, halfAnimationHeight),
          debugArea: false,
          guardComingFrom: ['deactivate'],
          animationName: 'image_tapped'),

      ActiveArea(
          area: Rect.fromLTWH(0, animationHeight / 2, animationWidth, animationHeight / 2),
          debugArea: false,
          animationsToCycle: ['activate', 'deactivate'],
          onAreaTapped: () {
            print('Button tapped!');
          })

    ];

    return Scaffold(
      appBar: AppBar(
        title: Text('Flare Button Demo'),
      ),
      body: Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              colors: [
                Color(0x3fffeb3b),
                Colors.orange,
              ]),
        ),
        child: Align(
          alignment: Alignment.bottomCenter,
          child: SmartFlareActor(
            width: animationWidth,
            height: animationHeight,
            filename: 'assets/button-animation.flr',
            startingAnimation: 'deactivate',
            activeAreas: activeAreas,
          ),
        ),
      ),
    );
  }
}
複製程式碼

Menu互動動畫程式碼實現

menu 互動動畫程式碼實現,如下:

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

class FlareSidebarMenuDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print(MediaQuery.of(context).size.height);
    return Scaffold(
      body: Container(
        child: Align(
          alignment: Alignment.centerRight,
          child: PanFlareActor(
            width: MediaQuery.of(context).size.width / 2.366,
            height: MediaQuery.of(context).size.height,
            filename: 'assets/slideout-menu.flr',
            openAnimation: 'open',
            closeAnimation: 'close',
            direction: ActorAdvancingDirection.RightToLeft,
            threshold: 20.0,
            reverseOnRelease: true,
            completeOnThresholdReached: true,
            activeAreas: [
              RelativePanArea(
                  area: Rect.fromLTWH(0, .7, 1.0, .3), debugArea: false),
            ],
          ),
        ),
      ),
    );
  }
}
複製程式碼

以上3個互動動畫案例的原始碼,放在了我2年前寫的一個 Flutter案例 的專案裡了,此專案現已維護起來,以後會長期更新,感興趣的小夥伴可以收藏,沒事時來看看可能會有新的發現 ?

此篇文章到此結束,下篇文章計劃給大家分享,Flutter 裡的路由,會總結歸納所有的路由使用方法,最後來封裝一個優秀的路由管理類。

最後附上部落格和專案地址,如下:
部落格地址:h.lishaoy.net/flutter-fla…
專案地址:github.com/persilee/fl…

相關文章