Flutter中有意思的滚动效果 - Sliver系列

滚动效果:

 

出处原文链接:https://segmentfault.com/a/1190000019902201

由于原文代码对屏幕短的手机存在适配问题,本人进行修改,新代码如下:

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Welcome to Flutter',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<StatefulWidget> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final double _headerCollapsedHeight = 40;
  final double _headerExpandedHeight = 300;

  @override
  Widget build(BuildContext context) {
    //final double height = MediaQuery.of(context).size.height;
    final MediaQueryData mqd = MediaQuery.of(context);
    return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverPersistentHeader(
            pinned: true,
            delegate: _MySliverHeaderDelegate(
              title: '哪吒之魔童降世',
              collapsedHeight: _headerCollapsedHeight,
              expandedHeight: _headerExpandedHeight,
              paddingTop: mqd.padding.top,
              coverImgUrl:
                  'https://img.zcool.cn/community/01c6615d3ae047a8012187f447cfef.jpg@1280w_1l_2o_100sh.jpg',
              coverImgName: null,
            ),
          ),
          SliverFillRemaining(
            hasScrollBody: false,
            child: FilmContent(
              minHeight:
                  (mqd.size.height - mqd.padding.top - _headerCollapsedHeight),
            ),
          ),
        ],
      ),
    );
  }
}

class _MySliverHeaderDelegate extends SliverPersistentHeaderDelegate {
  _MySliverHeaderDelegate({
    required this.collapsedHeight,
    required this.expandedHeight,
    required this.paddingTop,
    this.coverImgUrl,
    this.coverImgName,
    required this.title,
  });

  final double collapsedHeight;
  final double expandedHeight;
  final double paddingTop;
  final String? coverImgUrl;
  final String? coverImgName;
  final String title;
  String _statusBarMode = 'dark';

  @override
  double get minExtent => collapsedHeight + paddingTop;

  @override
  double get maxExtent => expandedHeight + paddingTop;

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
    return true;
  }

  void updateStatusBarBrightness(shrinkOffset) {
    if (shrinkOffset > 50 && _statusBarMode == 'dark') {
      _statusBarMode = 'light';
      SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
        statusBarBrightness: Brightness.light,
        statusBarIconBrightness: Brightness.light,
      ));
    } else if (shrinkOffset <= 50 && _statusBarMode == 'light') {
      _statusBarMode = 'dark';
      SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
        statusBarBrightness: Brightness.dark,
        statusBarIconBrightness: Brightness.dark,
      ));
    }
  }

  Color makeStickyHeaderBgColor(shrinkOffset) {
    final int alpha =
        (shrinkOffset / (maxExtent - minExtent) * 255).clamp(0, 255).toInt();
    return Color.fromARGB(alpha, 255, 255, 255);
  }

  Color makeStickyHeaderTextColor(shrinkOffset, isIcon) {
    if (shrinkOffset <= 50) {
      return isIcon ? Colors.white : Colors.transparent;
    } else {
      final int alpha =
          (shrinkOffset / (maxExtent - minExtent) * 255).clamp(0, 255).toInt();
      return Color.fromARGB(alpha, 0, 0, 0);
    }
  }

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    updateStatusBarBrightness(shrinkOffset);
    return SizedBox(
      height: maxExtent,
      width: MediaQuery.of(context).size.width,
      child: Stack(
        fit: StackFit.expand,
        children: <Widget>[
          coverImgUrl != null
              ? Image.network(coverImgUrl!, fit: BoxFit.cover)
              : coverImgName != null
                  ? Image.asset(coverImgName!, fit: BoxFit.cover)
                  : Container(
                      color: Colors.orangeAccent,
                    ),
          Positioned(
            left: 0,
            top: maxExtent / 2,
            right: 0,
            bottom: 0,
            child: Container(
              decoration: const BoxDecoration(
                gradient: LinearGradient(
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter,
                  colors: [
                    Color(0x00000000),
                    Color(0x90000000),
                  ],
                ),
              ),
            ),
          ),
          Positioned(
            left: 0,
            right: 0,
            top: 0,
            child: Container(
              color: makeStickyHeaderBgColor(shrinkOffset),
              child: SafeArea(
                bottom: false,
                child: SizedBox(
                  height: collapsedHeight,
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      IconButton(
                        icon: Icon(
                          Icons.arrow_back_ios,
                          color: makeStickyHeaderTextColor(shrinkOffset, true),
                        ),
                        onPressed: () => Navigator.pop(context),
                      ),
                      Text(
                        title,
                        style: TextStyle(
                          fontSize: 20,
                          fontWeight: FontWeight.w500,
                          color: makeStickyHeaderTextColor(shrinkOffset, false),
                        ),
                      ),
                      IconButton(
                        icon: Icon(
                          Icons.share,
                          color: makeStickyHeaderTextColor(shrinkOffset, true),
                        ),
                        onPressed: () {},
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class FilmContent extends StatelessWidget {
  const FilmContent({super.key, required this.minHeight});

  final double minHeight;

  @override
  Widget build(BuildContext context) {
    return Container(
      constraints: BoxConstraints(minHeight: minHeight),
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Container(
                width: 130,
                height: 180,
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(6),
                  // color: Colors.orangeAccent,
                ),
                clipBehavior: Clip.hardEdge,
                child: Image.network(
                  'https://img1.gamersky.com/image2019/07/20190725_ll_red_136_2/gamersky_07small_14_201972510258D0.jpg',
                  width: 130,
                  height: 180,
                  fit: BoxFit.cover,
                ),
              ),
              const SizedBox(width: 10),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    _buildText(
                      '哪吒之魔童降世',
                      fontSize: 25,
                      fontWeight: FontWeight.bold,
                    ),
                    const SizedBox(height: 10),
                    _buildText(
                      '动画/中国大陆/110分钟',
                      color: const Color(0xFF999999),
                    ),
                    const SizedBox(height: 8),
                    _buildText(
                      '2019-07-26 08:00 中国大陆上映',
                      color: const Color(0xFF999999),
                    ),
                    const SizedBox(height: 8),
                    _buildText(
                      '32.1万人想看/大V推荐度95%',
                      color: const Color(0xFF999999),
                    ),
                  ],
                ),
              ),
            ],
          ),
          const Divider(height: 32),
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              _buildText(
                '剧情简介',
                fontSize: 25,
                fontWeight: FontWeight.bold,
              ),
              const SizedBox(height: 10),
              _buildText(
                '天地灵气孕育出一颗能量巨大的混元珠,元始天尊将混元珠提炼成灵珠和魔丸,灵珠投胎为人,助周伐纣时可堪大用;而魔丸则会诞出魔王,为祸人间。元始天尊启动了天劫咒语,3年后天雷将会降临,摧毁魔丸。太乙受命将灵珠托生于陈塘关李靖家的儿子哪吒身上。然而阴差阳错,灵珠和魔丸竟然被掉包。本应是灵珠英雄的哪吒却成了混世大魔王。调皮捣蛋顽劣不堪的哪吒却徒有一颗做英雄的心。然而面对众人对魔丸的误解和即将来临的天雷的降临,哪吒是否命中注定会立地成魔?他将何去何从?',
                color: const Color(0xFF999999),
                textAlign: TextAlign.justify,
              ),
            ],
          ),
        ],
      ),
    );
  }

  _buildText(
    String data, {
    double fontSize = 15,
    FontWeight? fontWeight,
    Color? color = const Color(0xFF333333),
    TextAlign? textAlign,
  }) {
    return Text(
      data,
      style: TextStyle(
        fontSize: fontSize,
        fontWeight: fontWeight,
        color: color,
        height: 1,
      ),
      textAlign: textAlign,
      overflow: TextOverflow.clip,
    );
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 你可以使用 Flutter 的命令行工具清除 pub-cache。 在命令行/终端输入以下命令: ``` flutter pub cache repair ``` 这将清除 pub-cache 的所有内容,并重新下载所需的文件。 如果你想要更详细的控制,你可以手动删除 pub-cache 文件夹,它通常位于以下位置: - Windows:`%APPDATA%\Pub\Cache\bin` - Mac/Linux:`~/.pub-cache/bin` 然后在需要时,你可以在 Flutter 项目运行 `flutter pub get` 来重新下载需要的依赖项。 ### 回答2: 在Flutter,pub-cache是存储Flutter项目所需依赖包的地方。如果需要清除pub-cache的内容,可以按照以下步骤进行操作: 1. 打开命令行终端(Command Prompt)或终端(Terminal)。 2. 进入到Flutter SDK的安装目录,通常是在用户目录下的flutter文件夹。 3. 使用以下命令进入pub缓存目录:cd .pub-cache 4. 确保当前路径是在.pub-cache目录下。 5. 执行以下命令来清除pub-cache的内容:flutter pub cache clean 6. 稍等片刻,Flutter会清除pub-cache的内容。完成后,命令行会显示成功清除的提示信息。 需要注意的是,清除pub-cache的内容意味着清除所有已下载的依赖包。这样一来,在下次运行Flutter项目时,Flutter将会重新下载并构建所需的依赖包。因此,在清除pub-cache之前,请确保你了解清楚操作的后果,并确认需要清除pub-cache的内容。 此外,除了清除pub-cache的内容,还可以使用flutter pub cache list命令来查看当前pub-cache已安装的依赖包列表。这样可以帮助你进一步了解并管理当前项目所需的依赖包。 ### 回答3: 在Flutter,pub-cache是用于存储Flutter项目依赖的第三方库的位置。当我们使用pub命令安装依赖时,它们会被下载并存储在pub-cache目录。如果需要清除pub-cache的内容,可以按照以下步骤进行操作: 1. 首先,打开命令行界面并导航到当前Flutter项目的根目录。 2. 运行pub cache clean命令。这将清除pub-cache目录的所有内容,包括已下载的依赖。 3. 等待命令执行完成,此时pub-cache目录将被完全清除。 需要注意的是,清除pub-cache的内容会导致所有依赖被删除,因此在执行此操作之前,应确保不会影响到当前项目的运行和构建。 另外,如果只想清除特定依赖的缓存,可以使用pub cache remove命令。例如,运行pub cache remove package_name将删除名称为package_name的依赖缓存。这对于解决特定依赖问题时可能很有用。 总结:要清除pub-cache的所有内容,只需运行pub cache clean命令即可。此操作将删除项目所有依赖的缓存。如需清除特定依赖的缓存,可以使用pub cache remove命令,并指定要删除的依赖名称。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值