gpt4 book ai didi

flutter - 模仿 iOS 联系表单 AppBar

转载 作者:行者123 更新时间:2023-12-03 02:42:14 25 4
gpt4 key购买 nike

我正在尝试模仿 iOS 联系表单应用栏。

展开

enter image description here

折叠

enter image description here

这是我到目前为止的地方

enter image description here

主屏幕

class CompanyScreen extends StatefulWidget {
@override
_CompanyScreenState createState() => _CompanyScreenState();
}

class _CompanyScreenState extends State<CompanyScreen> {
@override
Widget build(BuildContext context) {

return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverPersistentHeader(
pinned: true,
floating: true,
delegate: SafeAreaPersistentHeaderDelegate(
expandedHeight: 200,
flexibleSpace:
SafeArea(child: Image.asset('assets/images/user.png'))),
),
SliverList(
delegate: SliverChildListDelegate([
TextField(),
]),
)
],
),
);
}
}

银头
class SafeAreaPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
final Widget title;

final Widget flexibleSpace;

final double expandedHeight;

SafeAreaPersistentHeaderDelegate(
{this.title, this.flexibleSpace, this.expandedHeight});

@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
final Widget appBar = FlexibleSpaceBar.createSettings(
minExtent: minExtent,
maxExtent: maxExtent,
currentExtent: max(minExtent, maxExtent - shrinkOffset),
toolbarOpacity: 1,
child: AppBar(
actions: <Widget>[
Container(
height: 60,
child: FlatButton(
child: Text('Done'),
),
)
],
backgroundColor: Colors.blue,
automaticallyImplyLeading: false,
title: title,
flexibleSpace: (title == null && flexibleSpace != null)
? Semantics(child: flexibleSpace, header: true)
: flexibleSpace,
centerTitle: true,
toolbarOpacity: 1,
bottomOpacity: 1.0),
);
return appBar;
}

@override
double get maxExtent => expandedHeight;

@override
double get minExtent => 80;

@override
bool shouldRebuild(SafeAreaPersistentHeaderDelegate old) {
if (old.flexibleSpace != flexibleSpace) {
return true;
}
return false;
}
}

更新:一切正常,但我在添加图像下的文本(添加照片)时遇到问题,并使该文本在折叠时消失。使用此解决方案,如果我将图像包装成一列,则图像会扩展溢出并且不会缩放。

要求:
  • AppBar 和 flex 区域必须在安全区域
  • 带图片的小部件底部必须有文字,可以动态更改(添加图片或更改图片),并且必须是可点击的
  • 当 flex 区域折叠并带有一些过渡时,图像区域下的文本必须消失
  • 能够在应用栏中添加标题并与操作按钮对齐
  • 当在应用栏中提供标题时,弹性区域应在标题下方缩放,否则弹性区域应缩放至标题区域,如上图

  • 非常感谢您对此的任何帮助

    最佳答案

    我试了一下..我不是条子专家,所以这个解决方案可能并不完美。我已将您的代码作为起点。该列似乎停用了所有缩放,因此我手动缩放了所有缩放。
    这是你的应用栏
    更新我对它进行了一些调整,所以感觉更像是 iOS 应用栏,而且我添加了额外的功能

    import 'dart:math';

    import 'package:flutter/material.dart';

    double _defaultTextHeight = 14;
    double _defaultTextPadding = 5;
    double _defaultAppBarHeight = 60;
    double _defaultMinAppBarHeight = 40;
    double _unknownTextValue = 1;

    class AppBarSliverHeader extends SliverPersistentHeaderDelegate {
    final String title;
    final double expandedHeight;
    final double safeAreaPadding;
    final Widget flexibleImage;
    final double flexibleSize;
    final String flexibleTitle;
    final double flexiblePadding;
    final bool flexToTop;
    final Function onTap;
    final Widget rightButton;
    final Widget leftButton;

    AppBarSliverHeader(
    {this.title,
    this.onTap,
    this.flexibleImage,
    @required this.expandedHeight,
    @required this.safeAreaPadding,
    this.flexibleTitle = '',
    this.flexToTop = false,
    this.leftButton,
    this.rightButton,
    this.flexibleSize = 30,
    this.flexiblePadding = 4});

    double _textPadding(double shrinkOffset) {
    return _defaultTextPadding * _scaleFactor(shrinkOffset);
    }

    double _widgetPadding(double shrinkOffset) {
    double offset;
    if (title == null) {
    offset = _defaultMinAppBarHeight * _scaleFactor(shrinkOffset);
    } else {
    if (flexToTop) {
    offset = _defaultAppBarHeight * _scaleFactor(shrinkOffset);
    } else {
    offset = (_defaultAppBarHeight - _defaultMinAppBarHeight) *
    _scaleFactor(shrinkOffset) +
    _defaultMinAppBarHeight;
    }
    }
    return offset;
    }

    double _topOffset(double shrinkOffset) {
    double offset;
    if (title == null) {
    offset = safeAreaPadding +
    (_defaultMinAppBarHeight * _scaleFactor(shrinkOffset));
    } else {
    if (flexToTop) {
    offset = safeAreaPadding +
    (_defaultAppBarHeight * _scaleFactor(shrinkOffset));
    } else {
    offset = safeAreaPadding +
    ((_defaultAppBarHeight - _defaultMinAppBarHeight) *
    _scaleFactor(shrinkOffset)) +
    _defaultMinAppBarHeight;
    }
    }

    return offset;
    }

    double _calculateWidgetHeight(double shrinkOffset) {
    double actualTextHeight = _scaleFactor(shrinkOffset) * _defaultTextHeight +
    _textPadding(shrinkOffset) +
    _unknownTextValue;

    final padding = title == null
    ? (2 * flexiblePadding)
    : flexToTop ? (2 * flexiblePadding) : flexiblePadding;

    final trueMinExtent = minExtent - _topOffset(shrinkOffset);

    final trueMaxExtent = maxExtent - _topOffset(shrinkOffset);

    double minWidgetSize =
    trueMinExtent - padding;

    double widgetHeight =
    ((trueMaxExtent - actualTextHeight) - shrinkOffset) - padding;

    return widgetHeight >= minWidgetSize ? widgetHeight : minWidgetSize;
    }

    double _scaleFactor(double shrinkOffset) {
    final ratio = (maxExtent - minExtent) / 100;
    double percentageHeight = shrinkOffset / ratio;
    double limitedPercentageHeight =
    percentageHeight >= 100 ? 100 : percentageHeight;
    return 1 - (limitedPercentageHeight / 100);
    }

    Widget _builtContent(BuildContext context, double shrinkOffset) {
    _topOffset(shrinkOffset);
    return SafeArea(
    bottom: false,
    child: Semantics(
    child: Padding(
    padding: title == null
    ? EdgeInsets.symmetric(vertical: flexiblePadding)
    : flexToTop
    ? EdgeInsets.symmetric(vertical: flexiblePadding)
    : EdgeInsets.only(bottom: flexiblePadding),
    child: GestureDetector(
    onTap: onTap,
    child: Column(
    mainAxisSize: MainAxisSize.min,
    children: <Widget>[
    LimitedBox(
    maxWidth: _calculateWidgetHeight(shrinkOffset),
    maxHeight: _calculateWidgetHeight(shrinkOffset),
    child: Container(
    decoration: BoxDecoration(
    borderRadius: BorderRadius.all(Radius.circular(
    _calculateWidgetHeight(shrinkOffset))),
    color: Colors.white),
    child: ClipRRect(
    borderRadius: BorderRadius.circular(
    _calculateWidgetHeight(shrinkOffset)),
    child: flexibleImage,
    ),
    )),
    Padding(
    padding: EdgeInsets.only(top: _textPadding(shrinkOffset)),
    child: Text(
    flexibleTitle,
    textScaleFactor: _scaleFactor(shrinkOffset),
    style: TextStyle(
    fontSize: _defaultTextHeight,
    color: Colors.white
    .withOpacity(_scaleFactor(shrinkOffset)), height: 1),
    ),
    )
    ],
    ),
    ),
    ),
    button: true,
    ),
    );
    }

    @override
    Widget build(
    BuildContext context, double shrinkOffset, bool overlapsContent) {
    final Widget appBar = FlexibleSpaceBar.createSettings(
    minExtent: minExtent,
    maxExtent: maxExtent,
    currentExtent: max(minExtent, maxExtent - shrinkOffset),
    toolbarOpacity: 1,
    child: AppBar(
    actions: <Widget>[rightButton == null ? Container() : rightButton],
    leading: leftButton == null ? Container() : leftButton,
    backgroundColor: Colors.blue,
    automaticallyImplyLeading: false,
    title: title != null
    ? Text(
    title,
    style: TextStyle(
    color: flexToTop
    ? Colors.white.withOpacity(_scaleFactor(shrinkOffset))
    : Colors.white),
    )
    : null,
    flexibleSpace: Padding(
    padding: EdgeInsets.only(top: _widgetPadding(shrinkOffset)),
    child: _builtContent(context, shrinkOffset),
    ),
    centerTitle: true,
    toolbarOpacity: 1,
    bottomOpacity: 1.0),
    );
    return appBar;
    }

    @override
    double get maxExtent => expandedHeight + safeAreaPadding;

    @override
    double get minExtent => title == null
    ? _defaultAppBarHeight + safeAreaPadding
    : flexToTop
    ? _defaultAppBarHeight + safeAreaPadding
    : _defaultAppBarHeight + safeAreaPadding + flexibleSize;

    @override
    bool shouldRebuild(AppBarSliverHeader old) {
    if (old.flexibleImage != flexibleImage) {
    return true;
    }
    return false;
    }
    }
    这是用法
    Scaffold(
    body: CustomScrollView(
    slivers: <Widget>[
    SliverPersistentHeader(
    pinned: true,
    floating: true,
    delegate: AppBarSliverHeader(
    expandedHeight: 250,
    safeAreaPadding: MediaQuery.of(context).padding.top,
    title: 'New Contact',
    flexibleImage: Image.asset('assets/images/avatar.png'),
    flexibleTitle: 'Add Image',
    flexiblePadding: 6,
    flexibleSize: 50,
    flexToTop: true,
    onTap: () {
    print('hello');
    },
    leftButton: IconButton(
    icon: Text('Cancel'),
    iconSize: 60,
    padding: EdgeInsets.zero,
    onPressed: () {},
    ),
    rightButton: IconButton(
    icon: Text('Done'),
    iconSize: 60,
    padding: EdgeInsets.zero,
    onPressed: () {},
    )),
    ),
    SliverList(
    delegate: SliverChildListDelegate([
    TextField(),
    ]),
    )
    ],
    ),
    );
    还有一些让我感到意外的事情。首先是文字大小。似乎文字大小不是实际的文字大小,所以我添加了 _unknownTextValue那里赔偿。此外,即使文本大小设置为 0,文本小部件仍然具有 1px 大小,因此我已在注释代码中对此进行了补偿。另一件事是我想使用 CircularAvatar对于图像,但显然是 CircularAvatar当更改干扰应用栏动画的大小时,小部件已内置动画,因此我构建了自定义头像。
    更新:为了使实际文本高度与字体大小相同,我在 TextStyle 中添加了高度属性 1。它似乎工作,但是仍然偶尔会在高达 1px 的文本字段上溢出,所以我将 _unknownTextValue 保持在 1px
    正如我所说,我不是白银专家,因此可能有更好的解决方案,因此我建议您等待其他答案
    注意:我只在 2 个 iOS 设备上测试过,所以你应该进一步测试以使用它
    有标题
    enter image description here
    无标题
    enter image description here
    激活 Title 和 flexToTop
    enter image description here

    关于flutter - 模仿 iOS 联系表单 AppBar,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62445009/

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