Flutter - 個人主頁下拉放大背景圖

下拉放大背景在個人主頁的設(shè)計中比較常見,但是我沒在Flutter的文章中找到實現(xiàn)方式。
結(jié)合了幾個文章的方案后析珊,我嘗試修改實現(xiàn)方式潮售,最終結(jié)合CustomScrollView 和Listener的方法實現(xiàn)了以下的方案

CustomScrollView

可以說列表視圖想要自定義滾動效果,繞不過CustomScrollView
詳細的定義可以參見其他博客https://juejin.im/post/5bceb534e51d457aa4596f9a
這里不詳細贅述

SliverPersistentHeader 可高度自定制的頭部伸縮視圖

很多文章里推薦大家用SliverAppBar來實現(xiàn)頭部乳绕,這種方案并不是很靈活
SliverPersistentHeader 可以說對下拉放大背景圖的支持更好一些

SliverPersistentHeader(
              delegate: _SVPersonalAppBarDelegate(
                  minHeight: defaultHeight,
                  maxHeight: maxHeight,
                  child: _createImageWidget()),
            )

  _createImageWidget() {
    return Container(
      child: Image.network(
        'https://pic4.zhimg.com/80/v2-b02e601349241df0e3f25fd1ec622155_1440w.jpg',
        fit: BoxFit.cover,
      ),
    );
  }

SliverPersistentHeader 需要我們傳入一個SliverPersistentHeaderDelegate對象俺祠,SliverPersistentHeaderDelegate是一個抽象類公给,所以需要我們自己去繼承實現(xiàn)一個子類

class _SVPersonalAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SVPersonalAppBarDelegate({
    @required this.minHeight,
    @required this.maxHeight,
    @required this.child,
  });

  final double minHeight;
  final double maxHeight;
  final Widget child;

  @override
  double get minExtent => minHeight;

  @override
  double get maxExtent => max(maxHeight, minHeight);

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return new SizedBox.expand(child: child);
  }

  @override
  bool shouldRebuild(_SVPersonalAppBarDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight ||
        minHeight != oldDelegate.minHeight ||
        child != oldDelegate.child;
  }
}

完成頭部視圖的定制后,我們需要關(guān)心的幾個問題

  1. 放大后蜘渣,解除觸摸,如何讓他自動重置到正常狀態(tài)?
  2. 從頂部快速滑下后肺然,如何制造彈性放大回置的動畫蔫缸?

我通過引入Listener 和狀態(tài)監(jiān)聽來完成了這兩部份的實現(xiàn)
完整代碼如下

import 'dart:math';

import 'package:flutter/material.dart';

enum SVDragState {
    SVDragStateIdle,
    SVDragStateBegin,
    SVDrageStateEnd,
}

class SVPersonalInfoPage extends StatefulWidget {
  @override
  _SVPersonalInfoPageState createState() => _SVPersonalInfoPageState();
}

class _SVPersonalInfoPageState extends State<SVPersonalInfoPage> {
  double startOffsetY;
  static double defaultHeight = 250;
  static double maxHeight = 350.0;
  static double offsetY = 0;
  static double distance = maxHeight - defaultHeight;
  SVDragState dragState = SVDragState.SVDragStateIdle;
  final ScrollController controller =
      ScrollController(initialScrollOffset: distance);

  @override
  void initState() {
    controller.addListener(() {
      offsetY = controller.offset;
      if (offsetY <= 0) {
        controller.jumpTo(0);
        _resetWithAnimation(true);
      }
    });

    super.initState();
  }

  _resetWithAnimation(bool delay) {
    if (!delay) {
       if (controller.offset < distance && dragState == SVDragState.SVDrageStateEnd) {
        dragState = SVDragState.SVDragStateIdle;
        controller.animateTo(distance,
            duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
            return;
      }
    }
    Future.delayed(Duration(milliseconds: 200)).then((value) {
      if (controller.offset < distance && dragState == SVDragState.SVDrageStateEnd) {
        dragState = SVDragState.SVDragStateIdle;
        controller.animateTo(distance,
            duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
            return;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Listener(
        onPointerDown: (e) { dragState = SVDragState.SVDragStateBegin; },
        onPointerUp: (e) { 
          dragState = SVDragState.SVDrageStateEnd;
          _resetWithAnimation(false);  
        },
        child: CustomScrollView(
          controller: controller,
          slivers: <Widget>[
            SliverPersistentHeader(
              delegate: _SVPersonalAppBarDelegate(
                  minHeight: defaultHeight,
                  maxHeight: maxHeight,
                  child: _createImageWidget()),
            ),
            SliverList(delegate: SliverChildBuilderDelegate(
              (context, index) {
                return Container(
                  child: Text(
                    "This is item $index",
                    style: TextStyle(fontSize: 20),
                  ),
                  color: Colors.redAccent,
                );
              },
            ))
          ],
        ));
  }

  _createImageWidget() {
    return Container(
      child: Image.network(
        'https://pic4.zhimg.com/80/v2-b02e601349241df0e3f25fd1ec622155_1440w.jpg',
        fit: BoxFit.cover,
      ),
    );
  }
}

class _SVPersonalAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SVPersonalAppBarDelegate({
    @required this.minHeight,
    @required this.maxHeight,
    @required this.child,
  });

  final double minHeight;
  final double maxHeight;
  final Widget child;

  @override
  double get minExtent => minHeight;

  @override
  double get maxExtent => max(maxHeight, minHeight);

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return new SizedBox.expand(child: child);
  }

  @override
  bool shouldRebuild(_SVPersonalAppBarDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight ||
        minHeight != oldDelegate.minHeight ||
        child != oldDelegate.child;
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者际起。
  • 序言:七十年代末拾碌,一起剝皮案震驚了整個濱河市吐葱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌校翔,老刑警劉巖弟跑,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異防症,居然都是意外死亡孟辑,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門蔫敲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饲嗽,“玉大人,你說我怎么就攤上這事奈嘿∶蚕海” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵裙犹,是天一觀的道長尽狠。 經(jīng)常有香客問我,道長叶圃,這世上最難降的妖魔是什么晚唇? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮盗似,結(jié)果婚禮上哩陕,老公的妹妹穿的比我還像新娘。我一直安慰自己赫舒,他們只是感情好悍及,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著接癌,像睡著了一般心赶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缺猛,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天缨叫,我揣著相機與錄音,去河邊找鬼荔燎。 笑死耻姥,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的有咨。 我是一名探鬼主播琐簇,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼座享!你這毒婦竟也來了婉商?” 一聲冷哼從身側(cè)響起似忧,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎丈秩,沒想到半個月后盯捌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡蘑秽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年饺著,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筷狼。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡瓶籽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出埂材,到底是詐尸還是另有隱情塑顺,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布俏险,位于F島的核電站严拒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏竖独。R本人自食惡果不足惜裤唠,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望莹痢。 院中可真熱鬧种蘸,春花似錦、人聲如沸竞膳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坦辟。三九已至刊侯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锉走,已是汗流浹背滨彻。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挪蹭,地道東北人亭饵。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像嚣潜,于是被迫代替她去往敵國和親冬骚。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344