将 a 与 SingleChildScrollView 一起使用,并将列作为子列。为了使图片在标题时变小,请使用FittedBox。用SizedBox 包裹FittedBox 以控制内部小部件的大小。使用滚动通知器在滚动时进行更新并跟踪用户滚动的距离。将滚动量除以您想要的最大高度,以便了解需要调整大小的当前小部件。通过找到余数并将其除以最大高度并乘以最小和最大尺寸的差值来调整该小部件的大小,然后添加最小尺寸。这将确保平稳过渡。然后将列中的任何小部件设置为最大尺寸并低于最小尺寸,以确保延迟不会破坏滚动条。
使用AnimatedOpacity 允许标题的描述淡入和淡出,或制作您认为应该如何显示的自定义动画。
以下代码应该可以使用您想要的样式自定义文本小部件。输入要在列表中的自定义TitleWithImage(包含小部件和两个字符串)项目,将 maxHeight 和 minHeight 输入自定义小部件。虽然我修复了一些,但它可能没有完全优化并且可能有很多错误:
import 'package:flutter/material.dart';
class CoolListView extends StatefulWidget {
final List<TitleWithImage> items;
final double minHeight;
final double maxHeight;
const CoolListView({Key key, this.items, this.minHeight, this.maxHeight}) : super(key: key);
@override
_CoolListViewState createState() => _CoolListViewState();
}
class _CoolListViewState extends State<CoolListView> {
List<Widget> widgets=[];
ScrollController _scrollController = new ScrollController();
@override
Widget build(BuildContext context) {
if(widgets.length == 0){
for(int i = 0; i<widget.items.length; i++){
if(i==0){
widgets.add(ListItem(height: widget.maxHeight, item: widget.items[0],descriptionTransparent: false));
}
else{
widgets.add(
ListItem(height: widget.minHeight, item: widget.items[i], descriptionTransparent: true,)
);
}
}
}
return new NotificationListener<ScrollUpdateNotification>(
child: SingleChildScrollView(
controller: _scrollController,
child: Column(
children: widgets,
)
),
onNotification: (t) {
if (t!= null && t is ScrollUpdateNotification) {
int currentWidget = (_scrollController.position.pixels/widget.maxHeight).ceil();
currentWidget = currentWidget==-1?0:currentWidget;
setState(() {
if(currentWidget != widgets.length-1){//makes higher index min
for(int i = currentWidget+1; i<=widgets.length-1; i++){
print(i);
widgets[i] = ListItem(height: widget.minHeight, item: widget.items[i],descriptionTransparent: true,);
}
}
if(currentWidget!=0){
widgets[currentWidget] = ListItem(
height: _scrollController.position.pixels%widget.maxHeight/widget.maxHeight*(widget.maxHeight-widget.minHeight)+widget.minHeight,
item: widget.items[currentWidget],
descriptionTransparent: true,
);
for(int i = currentWidget-1; i>=0; i--){
widgets[i] = ListItem(height: widget.maxHeight,
item: widget.items[i],
descriptionTransparent: false,
);
}
}
else{
widgets[0] = ListItem(
height: widget.maxHeight,
item: widget.items[0],
descriptionTransparent: false
);
}
});
}
},
);
}
}
class TitleWithImage
{
final Widget image;
final String title;
final String description;
TitleWithImage(this.image, this.title, this.description);
}
class ListItem extends StatelessWidget {
final double height;
final TitleWithImage item;
final bool descriptionTransparent;
const ListItem({Key key, this.height, this.item, this.descriptionTransparent}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
child:Stack(
children: [
SizedBox(
height: height,
width: MediaQuery.of(context).size.width,
child: FittedBox(
fit: BoxFit.none,
child:Align(
alignment: Alignment.center,
child: item.image
)
),
),
SizedBox(
height: height,
width: MediaQuery.of(context).size.width,
child: Column(
children: [
Spacer(),
Text(item.title,),
AnimatedOpacity(
child: Text(
item.description,
style: TextStyle(
color: Colors.black
),
),
opacity: descriptionTransparent? 0.0 : 1.0,
duration: Duration(milliseconds: 500),
),
],
),
),
],
),
);
}
}
编辑这是我的 main.dart:
import 'package:cool_list_view/CoolListView.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Collapsing List Demo')),
body: CoolListView(
items: [
new TitleWithImage(
Container(
height: 1000,
width:1000,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end:
Alignment(0.8, 0.0), // 10% of the width, so there are ten blinds.
colors: [
const Color(0xffee0000),
const Color(0xffeeee00)
], // red to yellow
tileMode: TileMode.repeated, // repeats the gradient over the canvas
),
),
),
'title',
'description',
),
new TitleWithImage(
Container(
height: 1000,
width:1000,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end:
Alignment(0.8, 0.0), // 10% of the width, so there are ten blinds.
colors: [
Colors.orange,
Colors.blue,
], // red to yellow
tileMode: TileMode.repeated, // repeats the gradient over the canvas
),
),
),
'title',
'description',
),
new TitleWithImage(
Container(
height: 1000,
width:1000,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end:
Alignment(0.8, 0.0), // 10% of the width, so there are ten blinds.
colors: [
const Color(0xffee0000),
const Color(0xffeeee00)
], // red to yellow
tileMode: TileMode.repeated, // repeats the gradient over the canvas
),
),
),
'title',
'description',
),
new TitleWithImage(
Container(
height: 1000,
width:1000,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end:
Alignment(0.8, 0.0), // 10% of the width, so there are ten blinds.
colors: [
const Color(0xffee0000),
const Color(0xffeeee00)
], // red to yellow
tileMode: TileMode.repeated, // repeats the gradient over the canvas
),
),
),
'title',
'description',
),
new TitleWithImage(
Container(
height: 1000,
width:1000,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end:
Alignment(0.8, 0.0), // 10% of the width, so there are ten blinds.
colors: [
const Color(0xffee0000),
const Color(0xffeeee00)
], // red to yellow
tileMode: TileMode.repeated, // repeats the gradient over the canvas
),
),
),
'title',
'description',
),
new TitleWithImage(
Container(
height: 1000,
width:1000,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end:
Alignment(0.8, 0.0), // 10% of the width, so there are ten blinds.
colors: [
const Color(0xffee0000),
const Color(0xffeeee00)
], // red to yellow
tileMode: TileMode.repeated, // repeats the gradient over the canvas
),
),
),
'title',
'description',
),
new TitleWithImage(
Container(
height: 1000,
width:1000,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end:
Alignment(0.8, 0.0), // 10% of the width, so there are ten blinds.
colors: [
const Color(0xffee0000),
const Color(0xffeeee00)
], // red to yellow
tileMode: TileMode.repeated, // repeats the gradient over the canvas
),
),
),
'title',
'description',
),
new TitleWithImage(Container(height: 1000,width:1000,color: Colors.blue), 'title', 'description'),
new TitleWithImage(Container(height: 1000,width:1000, color: Colors.orange), 'title', 'description'),
],
minHeight: 50,
maxHeight: 300,
),
),
);
}
}