日益努力,而后风生水起|

Flutter使用SliverAppBar开发更多样式AppBar

代码参考于【无聊的编码】Flutter Sliver全家桶初体验,一起炫酷一下,有优化

只是用官方的SliverAppBar只能做到如下样式:

image

但是我们想要的结果是下面这样的:

image

为此,我封装了一个组件SliverCustomAppBar用来实现。

在我封装的这个组件中有以下几个参数:

  • double height:封面图片和信息显示区域的总高度
  • String cover:封面图片的地址(这里我使用的是加载资源图片的方法,实际应用中应该更改)
  • double coverHeight:封面图片所占有的高度
  • double radius:信息显示区域上方两个角的圆角值
  • String avatar:头像图片(这里我使用的是加载资源图片的方法,实际应用中应该更改)
  • double avatarTop:头像离顶部的距离
  • double avatarLeft:头像离左边的距离
  • double avatarSize:头像的尺寸
  • Widget child:信息区域显示的内容
  • List<Widget> sliversSliverAppBar下方显示的Sliver组件

下面是使用该组件的代码和显示效果:

class CustomAppBarPage extends StatelessWidget {
  const CustomAppBarPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SliverCustomAppBar(
        height: 372.h,
        cover: 'assets/images/cover.jpg',
        coverHeight: 210.h,
        radius: 12.h,
        avatar: 'assets/images/avatar.jpg',
        avatarTop: 174.h,
        avatarLeft: 15.w,
        avatarSize: 80.h,
        child: Column(children: []),
        slivers: [BuildList()],
      ),
    );
  }
}

image

SliverCustomAppBar的详细代码,在实际使用前请详细查看TODO注释处的内容:

class SliverCustomAppBar extends StatefulWidget {
  const SliverCustomAppBar({
    Key? key,
    required this.height,
    required this.cover,
    required this.coverHeight,
    required this.radius,
    required this.avatar,
    required this.avatarTop,
    required this.avatarLeft,
    required this.avatarSize,
    required this.child,
    required this.slivers,
  }) : super(key: key);

  final double height;
  final String cover;
  final double coverHeight;
  final double radius;
  final String avatar;
  final double avatarTop;
  final double avatarLeft;
  final double avatarSize;
  final Widget child;
  final List<Widget> slivers;

  @override
  State<SliverCustomAppBar> createState() => _SliverCustomAppBarState();
}

class _SliverCustomAppBarState extends State<SliverCustomAppBar>
    with SingleTickerProviderStateMixin {
  late AnimationController animationController;
  late Animation<double> animation;

  double startDy = 0;
  double expendHeight = 0;

  @override
  void initState() {
    super.initState();
    animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 300),
    );
    animation = Tween(begin: 0.0, end: 0.0).animate(animationController);
  }

  @override
  void dispose() {
    animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Listener(
        onPointerMove: (e) {
          // 记录初始位置的信息
          if (startDy == 0) startDy = e.position.dy;
          // 滑动的距离 = 滑动到的位置 - 初始位置(防止滑动太长所以除以3)
          expendHeight = (e.position.dy - startDy) / 3;
          setState(() {});
        },
        onPointerUp: (e) {
          startDy = 0;
          animation =
              Tween(begin: expendHeight, end: 0.0).animate(animationController)
                ..addListener(() {
                  expendHeight = animation.value;
                  setState(() {});
                });
          animationController.forward(from: 0.0);
        },
        child: ScrollConfiguration(
          // 防止下拉时顶部出现水波纹效果
          behavior: NoShadowScrollBehavior(),
          // TODO:实际应用中大部分应该改为NestedScrollView
          child: CustomScrollView(
            slivers: [
              BuildSliverAppBar(
                expendHeight: expendHeight,
                height: widget.height,
                cover: widget.cover,
                coverHeight: widget.coverHeight,
                radius: widget.radius,
                avatarTop: widget.avatarTop,
                avatarLeft: widget.avatarLeft,
                avatarSize: widget.avatarSize,
                avatar: widget.avatar,
                child: widget.child,
              ),
              ...widget.slivers,
            ],
          ),
        ),
      ),
    );
  }
}
class BuildSliverAppBar extends StatelessWidget {
  BuildSliverAppBar({
    super.key,
    required this.expendHeight,
    required this.height,
    required this.cover,
    required this.coverHeight,
    required this.radius,
    required this.avatarTop,
    required this.avatarLeft,
    required this.avatarSize,
    required this.child,
    required this.avatar,
  });

  final double height;
  final double expendHeight;
  final String cover;
  final double coverHeight;
  final double radius;
  final String avatar;
  final double avatarTop;
  final double avatarLeft;
  final double avatarSize;
  final Widget child;

  final double _statusBarHeight = MediaQueryData.fromWindow(window).padding.top;
  final _screenWidth = MediaQueryData.fromWindow(window).size.width;

  @override
  Widget build(BuildContext context) {
    return SliverAppBar(
      pinned: true,
      expandedHeight: height - _statusBarHeight + expendHeight,
      flexibleSpace: FlexibleSpaceBar(
        collapseMode: CollapseMode.pin,
        background: Stack(
          children: [
            SizedBox(
              height: coverHeight + expendHeight,
              width: _screenWidth,
              // TODO:更改以下代码
              child: Image.asset(cover, fit: BoxFit.cover),
            ),
            Align(
              alignment: Alignment.bottomCenter,
              child: Container(
                width: _screenWidth,
                height: height - coverHeight + radius,
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.vertical(
                    top: Radius.circular(radius),
                  ),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black.withOpacity(.1),
                      offset: const Offset(0, -2),
                      blurRadius: 2.h,
                    ),
                  ],
                ),
                child: child,
              ),
            ),
            Positioned(
              top: avatarTop + expendHeight,
              left: avatarLeft,
              child: Container(
                height: avatarSize,
                width: avatarSize,
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.all(Radius.circular(avatarSize)),
                  border: Border.all(color: Colors.white, width: 2.h),
                  image: DecorationImage(
                    // TODO:更改以下代码
                    image: AssetImage(avatar),
                    fit: BoxFit.cover,
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

NoShadowScrollBehavior详细代码:

import 'package:flutter/material.dart';

/// 隐藏水波纹配置
class NoShadowScrollBehavior extends ScrollBehavior {
  @override
  Widget buildOverscrollIndicator(
      BuildContext context, Widget child, ScrollableDetails details) {
    // TODO: implement buildOverscrollIndicator
    // return super.buildOverscrollIndicator(context, child, details);
    return child;
  }
}

使用示例:

class CustomAppBarPage extends StatelessWidget {
  const CustomAppBarPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SliverCustomAppBar(
        height: 372.h,
        cover: 'assets/images/cover.jpg',
        coverHeight: 210.h,
        radius: 12.h,
        avatar: 'assets/images/avatar.jpg',
        avatarTop: 174.h,
        avatarLeft: 15.w,
        avatarSize: 80.h,
        child: Column(children: []),
        slivers: [BuildList()],
      ),
    );
  }
}

本文作者:菠萝橙子丶

本文链接:https://www.cnblogs.com/ilgnefz/p/17108015.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   菠萝橙子丶  阅读(728)  评论(3编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 Shining For One Thing 赵贝尔
Shining For One Thing - 赵贝尔
00:00 / 00:00
An audio error has occurred.

Shining For One Thing (《一闪一闪亮星星》影视剧歌曲) - 赵贝尔

词:萨吉

曲:金大洲

编曲:金大洲D-Jin

制作人:金大洲D-Jin

吉他/Bass:D-Jin

合声编写/合声:赵贝尔

人声录音/编辑:张德龙@D-Jin Music Studio

混音/母带处理:George Dum

宣传推广:杨慧颖/杨佩

封面设计:HOO

OP/音乐制作出品:D-Jin Music(北京翊辰文化传媒有限公司)

(未经著作权人许可,不得翻唱、翻录或使用)

夏夜的花火

夏夜的花火

因为你在身边而深刻

因为你在身边而深刻

幸运的是我

在宇宙之间听见承诺

在宇宙之间听见承诺

嗨 是我

这一次别再错过

这一次别再错过

喜欢你该由我主动了

喜欢你该由我主动了

星星那么多

星星那么多

有数不尽的浪漫闪烁

注定这一颗

会让你刻在手臂左侧

属于我

星形心率的贴合

幸有你总在守护我

幸有你总在守护我

I fall in love

I fall in love

I see your love

遇见你才发现我在

等你到来

等你到来

Fallen star

The wonder of you

我会永远在你天空

我会永远在你天空

为你闪烁 my love

为你闪烁 my love

Shining for one thing

Shining for one thing

Shining for one thing

Shining for one thing

It's you

It's you

星星那么多

星星那么多

有数不尽的浪漫闪烁

注定这一颗

会让你刻在手臂左侧

属于我

星形心率的贴合

幸有你总在守护我

幸有你总在守护我

I fall in love

I fall in love

I see your love

遇见你才发现我在

等你到来

等你到来

Fallen star

The wonder of you

我会永远在你天空

我会永远在你天空

为你闪烁 my love

为你闪烁 my love

Shining for one thing

Shining for one thing

Shining for one thing

Shining for one thing

It's you

It's you