我在SliverPersistentHeader
中使用CustomScrollView
来拥有一个持久的标头,该标头在用户滚动时会缩小和增长,但是当达到最大大小时,它会变得有点僵硬,因为它不会t“过度增长”。
这是我想要的行为(来自Spotify应用程序)和我的行为的视频:
。
答案 0 :(得分:3)
现在您可以创建自己的SliverPersistentHeaderDelegate
并覆盖此参数”
@override
OverScrollHeaderStretchConfiguration get stretchConfiguration =>
OverScrollHeaderStretchConfiguration();
默认情况下,如果为null,则一旦添加,便可以拉伸视图。
这是我使用的课程:
class CustomSliverDelegate extends SliverPersistentHeaderDelegate {
final Widget child;
final Widget title;
final Widget background;
final double topSafeArea;
final double maxExtent;
CustomSliverDelegate({
this.title,
this.child,
this.maxExtent = 350,
this.background,
this.topSafeArea = 0,
});
@override
Widget build(BuildContext context, double shrinkOffset,
bool overlapsContent) {
final appBarSize = maxExtent - shrinkOffset;
final proportion = 2 - (maxExtent / appBarSize);
final percent = proportion < 0 || proportion > 1 ? 0.0 : proportion;
return Theme(
data: ThemeData.dark(),
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: maxExtent),
child: Stack(
children: [
Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
top: 0,
child: background,
),
Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
child: Opacity(opacity: percent, child: child),
),
Positioned(
top: 0.0,
left: 0.0,
right: 0.0,
child: AppBar(
title: Opacity(opacity: 1 - percent, child: title),
backgroundColor: Colors.transparent,
elevation: 0,
),
),
],
),
),
);
}
@override
OverScrollHeaderStretchConfiguration get stretchConfiguration =>
OverScrollHeaderStretchConfiguration();
@override
double get minExtent => kToolbarHeight + topSafeArea;
@override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
return true;
}
}
答案 1 :(得分:1)
作为一种选择,您只需复制粘贴此代码即可查看其工作原理
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Page(),
),
);
}
}
class Page extends StatefulWidget {
@override
_PageState createState() => _PageState();
}
class _PageState extends State<Page> {
static final double _initialToolbarHeight = 300;
static final double _maxSizeFactor = 1.3; // image max size will 130%
static final double _transformSpeed = 0.001; // 0.1 very fast, 0.001 slow
ScrollController _controller;
double _factor = 1;
double _expandedToolbarHeight = _initialToolbarHeight;
@override
void initState() {
_controller = ScrollController();
_controller.addListener(_scrollListener);
super.initState();
}
_scrollListener() {
if (_controller.offset < 0) {
_factor = 1 + _controller.offset.abs() * _transformSpeed;
_factor = _factor.clamp(1, _maxSizeFactor);
_expandedToolbarHeight = _initialToolbarHeight + _controller.offset.abs(); //
} else {
_factor = 1;
_expandedToolbarHeight = _initialToolbarHeight; //
}
setState(() {});
}
@override
Widget build(BuildContext context) {
return CustomScrollView(
physics: const BouncingScrollPhysics(),
controller: _controller,
slivers: [
SliverPersistentHeader(
floating: false,
pinned: true,
delegate: _SliverAppBarDelegate(
minHeight: 300,
maxHeight: 300,
child: Transform.scale(
scale: _factor,
child: Image.network('https://picsum.photos/id/1025/990/660', fit: BoxFit.cover),
),
),
),
SliverList(
delegate: SliverChildListDelegate(getItems()),
)
],
);
}
getItems() {
return List.generate(50, (pos) {
return Container(
height: 64,
child: Text('item $pos'),
);
});
}
}
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_SliverAppBarDelegate({
@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 => maxHeight;
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return new SizedBox.expand(child: child);
}
@override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
return maxHeight != oldDelegate.maxHeight || minHeight != oldDelegate.minHeight || child != oldDelegate.child;
}
}
。
答案 2 :(得分:1)
在寻找解决方案时,我遇到了三种不同的解决方法:
Stack
和标题小部件(覆盖在滚动视图顶部)的CustomScrollView
,向ScrollController
提供一个CustomScrollView
并传递控制器到标题小部件以调整其大小ScrollController
,将其传递到CustomScrollView
,然后使用控制器的值来调整maxExtent
的{{1}}(这就是{{3} })。我在解决方案1和2上遇到了问题
SliverPersistentHeader
。这就是为什么我选择解决方案3的原因。我确定实现它的方式并不是最好的,但是它完全可以按照我的意愿工作:
CustomScrollView
答案 3 :(得分:1)
我通过简单地创建自定义SliverPersistentHeaderDelegate
解决了这个问题。
只需覆盖StretchConfiguration的吸气剂即可。如果有用的话,这是我的代码。
class LargeCustomHeader extends SliverPersistentHeaderDelegate {
LargeCustomHeader(
{this.children,
this.title = '',
this.childrenHeight = 0,
this.backgroundImage,
this.titleHeight = 44,
this.titleMaxLines = 1,
this.titleTextStyle = const TextStyle(
fontSize: 30,
letterSpacing: 0.5,
fontWeight: FontWeight.bold,
height: 1.2,
color: ColorConfig.primaryContrastColor)}) {}
final List<Widget> children;
final String title;
final double childrenHeight;
final String backgroundImage;
final int _fadeDuration = 250;
final double titleHeight;
final int titleMaxLines;
final double _navBarHeight = 56;
final TextStyle titleTextStyle;
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
constraints: BoxConstraints.expand(),
decoration: BoxDecoration(
// borderRadius: BorderRadius.vertical(bottom: Radius.circular(35.0)),
color: Colors.black,
),
child: Stack(
fit: StackFit.loose,
children: <Widget>[
if (this.backgroundImage != null) ...[
Positioned(
top: 0,
left: 0,
right: 0,
bottom: 0,
child: FadeInImage.assetNetwork(
placeholder: "assets/images/image-placeholder.png",
image: backgroundImage,
placeholderScale: 1,
fit: BoxFit.cover,
alignment: Alignment.center,
imageScale: 0.1,
fadeInDuration: const Duration(milliseconds: 500),
fadeOutDuration: const Duration(milliseconds: 200),
),
),
Positioned(
top: 0,
left: 0,
right: 0,
bottom: 0,
child: Container(
color: Color.fromRGBO(0, 0, 0, 0.6),
),
),
],
Positioned(
bottom: 0,
left: 0,
right: 0,
top: _navBarHeight + titleHeight,
child: AnimatedOpacity(
opacity: (shrinkOffset >= childrenHeight / 3) ? 0 : 1,
duration: Duration(milliseconds: _fadeDuration),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[if (children != null) ...children],
))),
Positioned(
top: _navBarHeight,
left: 0,
right: 0,
height: titleHeight,
child: Padding(
padding: const EdgeInsets.only(
right: 30, bottom: 0, left: 30, top: 5),
child: AnimatedOpacity(
opacity: (shrinkOffset >= childrenHeight + (titleHeight / 3))
? 0
: 1,
duration: Duration(milliseconds: _fadeDuration),
child: Text(
title,
style: titleTextStyle,
maxLines: titleMaxLines,
overflow: TextOverflow.ellipsis,
),
),
),
),
Container(
color: Colors.transparent,
height: _navBarHeight,
child: AppBar(
elevation: 0.0,
backgroundColor: Colors.transparent,
title: AnimatedOpacity(
opacity:
(shrinkOffset >= childrenHeight + (titleHeight / 3))
? 1
: 0,
duration: Duration(milliseconds: _fadeDuration),
child: Text(
title,
),
)),
)
],
));
}
@override
double get maxExtent => _navBarHeight + titleHeight + childrenHeight;
@override
double get minExtent => _navBarHeight;
// @override
// FloatingHeaderSnapConfiguration get snapConfiguration => FloatingHeaderSnapConfiguration() ;
@override
OverScrollHeaderStretchConfiguration get stretchConfiguration =>
OverScrollHeaderStretchConfiguration(
stretchTriggerOffset: maxExtent,
onStretchTrigger: () {},
);
double get maxShrinkOffset => maxExtent - minExtent;
@override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
//TODO: implement specific rebuild checks
return true;
}
}
答案 4 :(得分:0)
您可以尝试将SliverAppBar
与stretch:true
一起使用,并将要显示在应用栏中的小部件作为flexibleSpace
传递。
这是一个例子
CustomScrollView(
physics: BouncingScrollPhysics(),
slivers: <Widget>[
SliverAppBar(
stretch: true,
floating: true,
backgroundColor: Colors.black,
expandedHeight: 300,
centerTitle: true,
title: Text("My Custom Bar"),
leading: IconButton(
onPressed: () {},
icon: Icon(Icons.menu),
),
actions: <Widget>[
IconButton(
onPressed: () {},
icon: Icon(Icons.search),
)
],
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
stretchModes:
[
StretchMode.zoomBackground,
StretchMode.blurBackground
],
background: YourCustomWidget(),
),
),
SliverList(
delegate: SliverChildListDelegate(
[
Container(color: Colors.red, height: 300.0),
Container(color: Colors.blue, height: 300.0),
],
),
),
],
);