gpt4 book ai didi

flutter - 如何在 SliverAppBar 中为项目的位置设置动画以在关闭时围绕标题移动它们

转载 作者:行者123 更新时间:2023-12-03 13:30:08 34 4
gpt4 key购买 nike

我对 Appbar 有这些要求,但我找不到解决它们的方法。

  • 拉伸(stretch) , AppBar 必须显示 两个图像一个在另一个之上并且标题必须隐藏。
  • 关闭 , AppBar 必须显示标题和 滚动时两个图像必须按比例缩小并移动到标题的两侧 .滚动时标题变为可见。

  • 我创建了几个模型来帮助获得所需的结果。
    这是拉伸(stretch)时的 Appbar:
    enter image description here
    这是关闭时的 Appbar:
    enter image description here

    最佳答案

    您可以创建自己的SliverAppBar通过扩展 SliverPersistentHeaderDelegate .
    平移、缩放和不透明度更改将在 build(...) 中完成。方法,因为这将在范围更改期间调用(通过滚动),minExtent <-> maxExtent .
    这是一个示例代码。

    import 'dart:math';

    import 'package:flutter/material.dart';

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

    class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return MaterialApp(
    theme: ThemeData(
    primaryColor: Colors.blue,
    ),
    home: HomePage(),
    );
    }
    }

    class HomePage extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return Scaffold(
    body: CustomScrollView(
    slivers: <Widget>[
    SliverPersistentHeader(
    delegate: MySliverAppBar(
    title: 'Sample',
    minWidth: 50,
    minHeight: 25,
    leftMaxWidth: 200,
    leftMaxHeight: 100,
    rightMaxWidth: 100,
    rightMaxHeight: 50,
    shrinkedTopPos: 10,
    ),
    pinned: true,
    ),
    SliverList(
    delegate: SliverChildBuilderDelegate(
    (_, int i) => Container(
    height: 50,
    color: Color.fromARGB(
    255,
    Random().nextInt(255),
    Random().nextInt(255),
    Random().nextInt(255),
    ),
    ),
    childCount: 50,
    ),
    ),
    ],
    ),
    );
    }
    }

    class MySliverAppBar extends SliverPersistentHeaderDelegate {
    MySliverAppBar({
    required this.title,
    required this.minWidth,
    required this.minHeight,
    required this.leftMaxWidth,
    required this.leftMaxHeight,
    required this.rightMaxWidth,
    required this.rightMaxHeight,
    this.titleStyle = const TextStyle(fontSize: 26),
    this.shrinkedTopPos = 0,
    });

    final String title;
    final TextStyle titleStyle;
    final double minWidth;
    final double minHeight;
    final double leftMaxWidth;
    final double leftMaxHeight;
    final double rightMaxWidth;
    final double rightMaxHeight;

    final double shrinkedTopPos;

    final GlobalKey _titleKey = GlobalKey();

    double? _topPadding;
    double? _centerX;
    Size? _titleSize;

    double get _shrinkedTopPos => _topPadding! + shrinkedTopPos;

    @override
    Widget build(
    BuildContext context,
    double shrinkOffset,
    bool overlapsContent,
    ) {
    if (_topPadding == null) {
    _topPadding = MediaQuery.of(context).padding.top;
    }
    if (_centerX == null) {
    _centerX = MediaQuery.of(context).size.width / 2;
    }
    if (_titleSize == null) {
    _titleSize = _calculateTitleSize(title, titleStyle);
    }

    double percent = shrinkOffset / (maxExtent - minExtent);
    percent = percent > 1 ? 1 : percent;

    return Container(
    color: Colors.red,
    child: Stack(
    children: <Widget>[
    _buildTitle(shrinkOffset),
    _buildLeftImage(percent),
    _buildRightImage(percent),
    ],
    ),
    );
    }

    Size _calculateTitleSize(String text, TextStyle style) {
    final TextPainter textPainter = TextPainter(
    text: TextSpan(text: text, style: style),
    maxLines: 1,
    textDirection: TextDirection.ltr)
    ..layout(minWidth: 0, maxWidth: double.infinity);
    return textPainter.size;
    }

    Widget _buildTitle(double shrinkOffset) => Align(
    alignment: Alignment.topCenter,
    child: Padding(
    padding: EdgeInsets.only(top: _topPadding!),
    child: Opacity(
    opacity: shrinkOffset / maxExtent,
    child: Text(title, key: _titleKey, style: titleStyle),
    ),
    ),
    );

    double getScaledWidth(double width, double percent) =>
    width - ((width - minWidth) * percent);

    double getScaledHeight(double height, double percent) =>
    height - ((height - minHeight) * percent);

    /// 20 is the padding between the image and the title
    double get shrinkedHorizontalPos =>
    (_centerX! - (_titleSize!.width / 2)) - minWidth - 20;

    Widget _buildLeftImage(double percent) {
    final double topMargin = minExtent;
    final double rangeLeft =
    (_centerX! - (leftMaxWidth / 2)) - shrinkedHorizontalPos;
    final double rangeTop = topMargin - _shrinkedTopPos;

    final double top = topMargin - (rangeTop * percent);
    final double left =
    (_centerX! - (leftMaxWidth / 2)) - (rangeLeft * percent);

    return Positioned(
    left: left,
    top: top,
    child: Container(
    width: getScaledWidth(leftMaxWidth, percent),
    height: getScaledHeight(leftMaxHeight, percent),
    color: Colors.black,
    ),
    );
    }

    Widget _buildRightImage(double percent) {
    final double topMargin = minExtent + (rightMaxHeight / 2);
    final double rangeRight =
    (_centerX! - (rightMaxWidth / 2)) - shrinkedHorizontalPos;
    final double rangeTop = topMargin - _shrinkedTopPos;

    final double top = topMargin - (rangeTop * percent);
    final double right =
    (_centerX! - (rightMaxWidth / 2)) - (rangeRight * percent);

    return Positioned(
    right: right,
    top: top,
    child: Container(
    width: getScaledWidth(rightMaxWidth, percent),
    height: getScaledHeight(rightMaxHeight, percent),
    color: Colors.white,
    ),
    );
    }

    @override
    double get maxExtent => 300;

    @override
    double get minExtent => _topPadding! + 50;

    @override
    bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) =>
    false;
    }

    关于flutter - 如何在 SliverAppBar 中为项目的位置设置动画以在关闭时围绕标题移动它们,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66825376/

    34 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com