美文网首页Flutter
Flutter - 个人主页下拉放大背景图

Flutter - 个人主页下拉放大背景图

作者: One1丨光 | 来源:发表于2020-06-11 13:33 被阅读0次

    下拉放大背景在个人主页的设计中比较常见,但是我没在Flutter的文章中找到实现方式。
    结合了几个文章的方案后,我尝试修改实现方式,最终结合CustomScrollView 和Listener的方法实现了以下的方案

    CustomScrollView

    可以说列表视图想要自定义滚动效果,绕不过CustomScrollView
    详细的定义可以参见其他博客https://juejin.im/post/5bceb534e51d457aa4596f9a
    这里不详细赘述

    SliverPersistentHeader 可高度自定制的头部伸缩视图

    很多文章里推荐大家用SliverAppBar来实现头部,这种方案并不是很灵活
    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是一个抽象类,所以需要我们自己去继承实现一个子类

    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;
      }
    }
    

    完成头部视图的定制后,我们需要关心的几个问题

    1. 放大后,解除触摸,如何让他自动重置到正常状态?
    2. 从顶部快速滑下后,如何制造弹性放大回置的动画?

    我通过引入Listener 和状态监听来完成了这两部份的实现
    完整代码如下

    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;
      }
    }
    

    相关文章

      网友评论

        本文标题:Flutter - 个人主页下拉放大背景图

        本文链接:https://www.haomeiwen.com/subject/zskbtktx.html