在android中,我们使用 app:behavior_overlapTop="64dp"
来实现这一目标
我想让内容重叠,就像上面的GIF一样
我的代码
class DetailsPage extends StatefulWidget {
@override
_DetailsPage createState() => _DetailsPage();
}
class _DetailsPage extends State<DetailsPage> {
@override
void initState() {
super.initState();
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
_scrollController = ScrollController();
_scrollController.addListener(_scrollListener);
}
ScrollController _scrollController;
bool lastStatus = true;
_scrollListener() {
if (isShrink != lastStatus) {
setState(() {
lastStatus = isShrink;
});
}
}
bool get isShrink {
return _scrollController.hasClients &&
_scrollController.offset > (250 - kToolbarHeight);
}
@override
void dispose() {
_scrollController.removeListener(_scrollListener);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
// backgroundColor: Colors.transparent,
body: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: 250.0,
floating: false,
brightness: Brightness.light,
pinned: true,
// elevation: 0.0,
// backgroundColor: AppColors.colorCreateTripOrange,
backgroundColor: Colors.white,
actions: <Widget>[
GestureDetector(
onTap: () {
Navigator.of(context).pop();
},
child: Padding(
padding: const EdgeInsets.only(right: 20.0),
child: Image.asset(
'assets/images/close.png',
width: 25.0,
height: 25.0,
),
),
)
],
leading: Padding(
padding: const EdgeInsets.only(left: 20.0, top: 0),
child: IconButton(
iconSize: 25,
icon: Image.asset(
'assets/images/back.png',
width: 25,
height: 25,
),
color: Colors.black,
onPressed: () {
Navigator.of(context).pop();
},
)),
flexibleSpace: FlexibleSpaceBar(
centerTitle: false,
collapseMode: CollapseMode.parallax,
title: Text(isShrink ? "Rome" : "",
style: TextStyle(
color: isShrink ? Colors.black : Colors.white,
fontFamily: 'bin_bold',
fontSize: 18.0,
)),
background: Image.network(
"https://media.istockphoto.com/photos/great-colosseum-rome-italy-picture-id692334500",
fit: BoxFit.cover,
)),
),
];
},
body: Container(
// padding: const EdgeInsets.only(left: 20.0, right: 20.0, top: 40.0),
decoration: BoxDecoration(
color: AppColors.colorWhite,
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Column(
children: <Widget>[
Expanded(
child: Container(
padding:
const EdgeInsets.only(left: 20.0, right: 20.0, top: 40.0),
decoration: BoxDecoration(
color: AppColors.colorWhite,
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: ListView(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 15.0),
child: Text(
"Rome",
style: TextStyle(
color: Colors.black,
fontFamily: 'bin_bold',
fontSize: 25.0),
),
),
Padding(
padding: EdgeInsets.only(top: 20, left: 15, right: 15),
child: Row(
children: <Widget>[
Image.asset(
'assets/images/calender.png',
width: 25.0,
height: 25.0,
),
Padding(
padding: const EdgeInsets.only(left: 10.0),
child: Text(
"March 6-12, 2020",
style: TextStyle(
fontFamily: 'bin',
fontSize: 18,
color: AppColors.colorActivityGray),
),
)
],
),
),
Container(
width: double.infinity,
margin: const EdgeInsets.only(
top: 20.0, left: 20.0, right: 30.0),
// padding: const EdgeInsets.only(left: 20.0, right: 20.0),
decoration: BoxDecoration(
color: AppColors.colorTripsGray,
borderRadius: BorderRadius.all(Radius.circular(20)),
border: Border.all(color: AppColors.colorDivider),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
top: 15.0,
bottom: 0.0,
left: 20.0,
right: 20.0),
child: Text(
Constants.region,
style: TextStyle(
color: AppColors.colorLightBorderOrange,
fontFamily: 'din',
fontSize: 20),
),
),
Padding(
padding: const EdgeInsets.only(
top: 10.0,
bottom: 0.0,
left: 20.0,
right: 20.0),
child: Text(
Constants.firstName,
style: TextStyle(
color: AppColors.colorCreateGreyTrans,
fontFamily: 'din',
fontSize: 20),
),
),
Padding(
padding: const EdgeInsets.only(
top: 10.0, bottom: 0.0, left: 0.0, right: 00.0),
child: Divider(),
),
Padding(
padding: const EdgeInsets.only(
top: 10.0,
bottom: 0.0,
left: 20.0,
right: 20.0),
child: Text(
Constants.activities,
style: TextStyle(
color: AppColors.colorLightBorderOrange,
fontFamily: 'din',
fontSize: 20),
),
),
Padding(
padding: const EdgeInsets.only(
top: 10.0,
bottom: 0.0,
left: 20.0,
right: 20.0),
child: Text(
"Food & Bar, Must See Attractions",
style: TextStyle(
color: AppColors.colorCreateGreyTrans,
fontFamily: 'din',
fontSize: 20),
),
),
Padding(
padding: const EdgeInsets.only(
top: 10.0, bottom: 0.0, left: 0.0, right: 00.0),
child: Divider(),
),
Padding(
padding: const EdgeInsets.only(
top: 10.0,
bottom: 0.0,
left: 20.0,
right: 20.0),
child: Text(
Constants.noOfTravellers,
style: TextStyle(
color: AppColors.colorLightBorderOrange,
fontFamily: 'din',
fontSize: 20),
),
),
Padding(
padding: const EdgeInsets.only(
top: 10.0,
bottom: 0.0,
left: 20.0,
right: 20.0),
child: Text(
"2 Adults, 1 kid",
style: TextStyle(
color: AppColors.colorCreateGreyTrans,
fontFamily: 'din',
fontSize: 20),
),
),
Padding(
padding: const EdgeInsets.only(
top: 10.0, bottom: 0.0, left: 0.0, right: 00.0),
child: Divider(),
),
Padding(
padding: const EdgeInsets.only(
top: 10.0,
bottom: 0.0,
left: 20.0,
right: 20.0),
child: Text(
Constants.email,
style: TextStyle(
color: AppColors.colorLightBorderOrange,
fontFamily: 'din',
fontSize: 20),
),
),
Padding(
padding: const EdgeInsets.only(
top: 10.0,
bottom: 10.0,
left: 20.0,
right: 20.0),
child: Text(
"kim.john12@gmail.com",
style: TextStyle(
color: AppColors.colorCreateGreyTrans,
fontFamily: 'din',
fontSize: 20),
),
),
],
),
),
],
)
],
),
),
),
Container(
width: double.infinity,
margin: const EdgeInsets.only(top: 20.0),
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: AppColors.colorWhite,
borderRadius: BorderRadius.all(Radius.circular(40)),
border: Border.all(color: AppColors.colorDivider, width: 2.0),
),
child: Center(
child: Wrap(
children: <Widget>[
MaterialButton(
padding: const EdgeInsets.symmetric(
horizontal: 40, vertical: 20),
textColor: Colors.black,
color: AppColors.colorWhite,
child: Text(
Constants.messageTripDesigner,
style: TextStyle(
fontFamily: 'din_bold',
fontSize: Constants.regionFontSize),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0),
side: BorderSide(
color: AppColors.colorLightBorderOrange,
width: 2),
),
onPressed: () {
Navigator.of(context).pop();
},
)
],
),
),
)
],
),
),
),
);
}
}
class MySliverAppBar extends SliverPersistentHeaderDelegate {
final double expandedHeight;
MySliverAppBar({@required this.expandedHeight});
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Stack(
fit: StackFit.expand,
overflow: Overflow.visible,
children: [
Image.network(
"https://images.pexels.com/photos/396547/pexels-photo-396547.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
fit: BoxFit.cover,
),
Center(
child: Opacity(
opacity: shrinkOffset / expandedHeight,
child: Text(
"MySliverAppBar",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 23,
),
),
),
),
],
);
}
@override
double get maxExtent => expandedHeight;
@override
double get minExtent => kToolbarHeight;
@override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}
下面是我到目前为止尝试过的一些帖子
如果需要更多信息,请告诉我。提前致谢。您的努力将不胜感激。
答案 0 :(得分:3)
根据使用情况,可以使用以下任意组合来使应用栏滚动显示具有重叠内容(如灵活空间)的滚动条。
ListView
(带有滚动通知)SliverList
(带有滚动通知)DraggableScrollableSheet
和DraggableScrollableNotification 好吧,这里我将介绍最简单的DraggableScrollableSheet
,它可以同时滚动和拖动以创建所需的效果。
DraggableScrollableSheet
和AppBar
堆叠在
脚手架DraggableScrollableNotification
更新标题高度和
AppBar
阴影。你在这里
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(debugShowCheckedModeBanner: false, home: HomePage()));
}
class HomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
final ValueNotifier<double> headerNegativeOffset = ValueNotifier<double>(0);
final ValueNotifier<bool> appbarShadow = ValueNotifier<bool>(false);
final double maxHeaderHeight = 250.0;
final double minHeaderHeight = 56.0;
final double bodyContentRatioMin = .8;
final double bodyContentRatioMax = 1.0;
///must be between min and max values of body content ratio.
final double bodyContentRatioParallax = .9;
@override
void dispose() {
headerNegativeOffset.dispose();
appbarShadow.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
//just for status bar color
appBar: PreferredSize(
preferredSize: Size.fromHeight(0.0),
child: AppBar(
backgroundColor: Colors.pink,
elevation: 0.0,
),
),
body: Stack(
children: <Widget>[
Stack(children: [
Container(
child: ValueListenableBuilder<double>(
valueListenable: headerNegativeOffset,
builder: (context, offset, child) {
return Transform.translate(
offset: Offset(0, offset * -1),
child: SizedBox(
height: maxHeaderHeight,
child: Container(
color: Colors.pink,
),
),
);
})),
NotificationListener<DraggableScrollableNotification>(
onNotification: (notification) {
if (notification.extent == bodyContentRatioMin) {
appbarShadow.value = false;
headerNegativeOffset.value = 0;
} else if (notification.extent == bodyContentRatioMax) {
appbarShadow.value = true;
headerNegativeOffset.value =
maxHeaderHeight - minHeaderHeight;
} else {
double newValue = (maxHeaderHeight - minHeaderHeight) -
((maxHeaderHeight - minHeaderHeight) *
((bodyContentRatioParallax - (notification.extent)) /
(bodyContentRatioMax -
bodyContentRatioParallax)));
appbarShadow.value = false;
if (newValue >= maxHeaderHeight - minHeaderHeight) {
appbarShadow.value = true;
newValue = maxHeaderHeight - minHeaderHeight;
} else if (newValue < 0) {
appbarShadow.value = false;
newValue = 0;
}
headerNegativeOffset.value = newValue;
}
return true;
},
child: Stack(
children: <Widget>[
DraggableScrollableSheet(
initialChildSize: bodyContentRatioMin,
minChildSize: bodyContentRatioMin,
maxChildSize: bodyContentRatioMax,
builder: (BuildContext context,
ScrollController scrollController) {
return Stack(
children: <Widget>[
Container(
alignment: AlignmentDirectional.center,
padding: EdgeInsets.only(
left: 16.0, right: 16.0, top: 16.0),
child: Material(
type: MaterialType.canvas,
color: Colors.white,
elevation: 2.0,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24.0),
topRight: Radius.circular(24.0),
),
child: ListView.builder(
controller: scrollController,
itemCount: 200,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
),
),
),
],
);
},
),
],
),
)
]),
Positioned(
left: 0.0,
right: 0.0,
top: 0.0,
child: ValueListenableBuilder<bool>(
valueListenable: appbarShadow,
builder: (context, value, child) {
///default height of appbar is 56.0. You can also
///use a custom widget with custom height if you want.
return AppBar(
backgroundColor: Colors.pink,
title: Text("Notes"),
elevation: value ? 2.0 : 0.0,
);
}),
),
],
),
drawer: Drawer(),
);
}
}
观看现场演示here。