Flutter | 一個超級酷炫的登陸頁是怎樣煉成的

近些日子在 UIMovement 上看到了一個比較酷炫的登陸頁效果,以下:git

以爲很酷炫,就本身實現了一下,效果以下:github

下面就來一步一步的分析是如何作出來的。微信

需求分析

首先仍是老套路,看一下都須要作什麼事情:async

  1. 首先咱們最清晰明瞭的需求就是點擊「註冊」彈出 Dialog
  2. 彈出 Dialog 後延遲一段時間彈出 Dialog 裏的內容
  3. Dialog 內說明文字有兩種顏色
  4. 點擊 「Accepter」按鈕會變色縮小回彈並展現 ok圖標
  5. 點擊「Accepter」按鈕時 Dialog 內其餘文字都被「白色遮罩」
  6. 「Accepter」按鈕 動畫結束後 dismiss 掉當前dialog 並把 logo向上移
  7. 跳轉到第二頁,文字呈波浪形彈出
  8. 文字彈出後顯示對話框並彈出鍵盤

開始實現

需求瞭解了,下面就是一步一步的實現效果。ide

1. 點擊「註冊」彈出 Dialog

在這裏咱們須要注意的有一點:動畫

在咱們使用 showModalBottomSheet 時,默認的背景是白色的,也就是說咱們本身設置的圓角是無論用的,ui

因此要給這個 BottomSheet 一個背景,這個參數在 showModalBottomSheet 方法中就有:this

showModalBottomSheet(
  context: context,
  backgroundColor: Colors.transparent,
  builder: (context) {
    Future.delayed(Duration(milliseconds: 50), () {
      _animationController.forward();
    });
    return AnimatedUserAgreement(
      animation: _animation,
    );
  });
)
複製代碼

設置一個 backgroundColor 就ok了。lua

2. 彈出 Dialog 後延遲一段時間彈出 Dialog 裏的內容

這裏我是寫了一個 「AnimatedWidget」,對 Dialog 裏面的 Widget 同時執行透明度和位置的動畫:spa

return Container(
  height: 270,
  padding: EdgeInsets.all(30),
  decoration: BoxDecoration(
    borderRadius: BorderRadius.circular(30), color: Colors.white),
  child: Opacity(
    opacity: _opacityTween.evaluate(animation),
    child: Stack(
      children: <Widget>[
        SingleChildScrollView(
          child: Container(
            child: UserAgreementDialog(),
            margin: EdgeInsets.only(top: _offsetTween.evaluate(animation)),
          ),
        )
      ],
    ),
  ),
);
複製代碼

可能細心的同窗看出來上面的代碼有一些問題:

爲何要加一個 SingleChildScrollView

由於我這裏改變位置使用的動畫效果是 「動態改變 Container margin 的值」

因此若是不使用 ScrollView 的話,會溢出。

3. Dialog 內說明文字有兩種顏色

有兩種顏色這個需求仍是比較簡單的,使用 「TextSpan」就搞定了。

代碼我就不貼了。

4. 點擊 「Accepter」按鈕會變色縮小回彈並展現 ok圖標

重點來了,這個功能是相對來講比較複雜的,可是隻要咱們瞭解需求,寫起來也是比較簡單。

首先咱們也是把這個功能點拆分一下:

  1. 點擊按鈕的時候會變色
  2. 點擊後會變回原來的顏色並縮小成一個圓形
  3. 變成圓形後動畫效果展現 ok 圖標

也仍是一步一步來。

1. 點擊按鈕的時候會變色

該功能不用考慮太多,既然有點擊手勢,那必然會使用 GestureDetector

而後使用 GestureDetectoronTapDown 參數,該參數是在「點擊按下」時回調:

onTapDown: (d) {
  setState(() {
    btnColor = btnColors[1];
  });
複製代碼

也沒有多餘複雜的東西,就是改變按鈕的顏色。

2. 點擊後會變回原來的顏色並縮小成一個圓形

如何處理點擊後?或者沒有點擊?

GestureDetector 也幫咱們封裝好了:

  • onTapUp:在點擊擡起時回調
  • onTapCancel:在取消點擊時回調

首先咱們處理取消點擊:

onTapCancel: () {
  setState(() {
    btnColor = btnColors[0];
  });
}
複製代碼

把顏色變回去就好了。

而後處理擡起時的邏輯,在擡起時也有兩個邏輯:

  1. 按鈕會縮小成圓形
  2. 縮小成圓形的時候會彈出 ok 圖標

首先說一下第一點:

縮小成圓形的時候是有一個回彈效果的,因此不能使用 AnimatedContainer 這種,必需要使用 Animation 纔有這種效果。

因此我使用了 AnimatedBuilder 來包裝這個 Widget。

而後說一下第二點:

如何在縮小成圓形的時候彈出 ok 圖標?

咱們可使用 IndexStack,在開始縮小動畫的時候切換 index,由於 ok 圖標開始時的縮放狀態是 0,因此頁面上是沒有圖標的,方便咱們後續作動畫。

Widget 代碼以下:

AnimatedBuilder(
  animation: _widthAnimation,
  builder: (BuildContext context, Widget child) {
    return Container(
      width: _widthAnimation.value,
      alignment: Alignment.center,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(40)),
        color: btnColor),
      margin: EdgeInsets.only(top: btnMargin),
      height: btnHeight,
      child: IndexedStack(
        alignment: Alignment.center,
        index: index,
        children: <Widget>[
          Text(
            'Accepteer',
            style: TextStyle(fontSize: 18),
          ),
          ScaleTransition(
            scale: _scaleTween.animate(_animation),
            child: Image.asset(
              'images/ok.png',
              width: 35,
              height: 35,
            ),
          )
        ],
      ),
    );
  },
),
複製代碼

5. 點擊「Accepter」按鈕時 Dialog 內其餘文字都被「白色遮罩」

這個也很簡單,Container 默認就有一個參數是:foregroundDecoration,咱們只須要在這個參數裏設置上咱們想要遮罩的顏色就能夠了。

可是也須要注意一點,若是最開始使用的遮罩顏色爲「透明色」,那麼會總體變黑一下,這個具體的緣由我也沒研究明白,有知道的大佬能夠告知一下。

這樣按鈕點擊後的效果就所有完成,代碼以下:

onTapUp: (d) {
  Future.delayed(Duration(milliseconds: 60), () {
    setState(() {
      foregroundColor = Colors.white70;
      btnColor = btnColors[0];
      index = 1;
    });
    _widthController.forward();
    Future.delayed(Duration(milliseconds: 200), () {
      _controller.forward().then((va) {
        Navigator.pop(context);
      });
    });
  });
}
複製代碼

6. 動畫結束後 dismiss 掉當前dialog 並把 logo向上移

這個相對來講就更簡單了,咱們只須要在 logo 的上方套一個 AnimatedContainer

而後監聽 dialog 是否已經 dismiss,若是已經 dismiss 那麼則調整 margin 的值就行了。

代碼以下:

setState(() {
  logoMargin = 100;
});
複製代碼

這樣正好 dialog 會有一個下移的動畫,而 logo 上移,就達到了咱們想要的效果。

7. 跳轉到第二頁,文字呈波浪形彈出

如何把文字呈波浪形彈出?

咱們最早想到的確定就是動畫,由於也只有動畫纔有這種回彈的效果,

那這麼多文字,每個都要設置動畫?

答案是確定的。

既然知道了,那咱們也只能循序漸進的作了。

能夠看到,每個文字都是由透明轉爲不透明,而且還會更改位置,

那咱們仍是先來封裝一個 AnimatedWidget

代碼以下:

class AnimatedStrWidget extends AnimatedWidget {
  final Tween<double> _opacityTween = Tween(begin: 0, end: 1);
  final Tween<Offset> _offsetTween =
      Tween(begin: Offset(0, 3), end: Offset(0, 0));
  final Widget child;

  AnimatedStrWidget(
      {Key key, @required Animation<double> animation, @required this.child})
      : super(key: key, listenable: animation);

  @override
  Widget build(BuildContext context) {
    final Animation<double> animation = listenable;
    return Opacity(
      opacity: _opacityTween.evaluate(animation) < 0
          ? 0
          : _opacityTween.evaluate(animation) > 1
              ? 1
              : _opacityTween.evaluate(animation),
      child: SlideTransition(
        position: _offsetTween.animate(animation),
        child: child,
      ),
    );
  }
}
複製代碼

這裏也有兩點須要注意:

  1. 透明度不能有負數而且不能大於1,由於咱們這個效果是要有回彈的效果,因此要作判斷。
  2. Tween<Offset> 這裏的值是整個高度的倍數,因此不要覺得是像素值。

封裝好之後咱們就能夠愉快的玩耍了:

void startAnim() async {
  for (int i = 0; i < strs.length; i++) {
    Future.delayed(
      Duration(
        milliseconds: i * 100,
      ), () {
        _strController[i].forward();
      });
  }
}
複製代碼

文字彈出效果時間爲 600ms,這裏設置每隔100ms作一個動畫,

這樣的效果是比較好的,更像波浪形彈出。

8. 文字彈出後顯示對話框並彈出鍵盤

主動彈出鍵盤咱們應該都有所瞭解,使用 FocusNode

這裏咱們也是隻須要判斷最後一個動畫什麼時候作完,而後把隱藏的鍵盤彈出,而且把鍵盤彈出就ok了。

代碼以下:

_strPositionAnimation[strs.length - 1].addStatusListener((status){
  if(status == AnimationStatus.completed){
    setState(() {
      opacity = 1;
      FocusScope.of(context).requestFocus(myFocusNode);
    });
  }
});
複製代碼

總結

實現這個頁面耗費了我一個晚上的時間,不得不說,東西仍是很多的。

想要實現這樣酷炫的登陸頁,仍是比較複雜。

這裏我實現的還不是很完美,看起來對比原圖有些「着急」。

不過無所謂了,就是改變一下動畫持續時間的事。

仍是那句話,梳理好需求,什麼都好作。

代碼已上傳至 GitHub:github.com/wanglu1209/…

另我我的建立了一個「Flutter 交流羣」,能夠添加我我的微信 「17610912320」來入羣。

推薦閱讀:

Flutter | WReorderList 一個能夠指定兩個item互換位置的組件

Flutter | 如何實現一個水波紋擴散效果的 Widget

Flutter | 自定義一個 Stepper 步驟組件

img
相關文章
相關標籤/搜索