提问人:Ramji 提问时间:8/20/2023 更新时间:8/21/2023 访问量:47
ListWheelScrollView 的进入和退出子项的淡入淡出效果
Fade effect on the entering and the exiting child of ListWheelScrollView
问:
从下面的GIF中可以看出,每当孩子退出父母时,孩子就会消失而没有动画,这给用户留下了不好的印象。如何为进入和退出的孩子添加平滑过渡出口?Container
Chat GPT 给出了一个似乎具有正确逻辑的答案,但它给出了同样突然的效果。
聊天GPT代码 : -
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('Smooth Transition in ListWheelScrollView')),
body: TransitionListWheel(),
),
);
}
}
class TransitionListWheel extends StatefulWidget {
@override
_TransitionListWheelState createState() => _TransitionListWheelState();
}
class _TransitionListWheelState extends State<TransitionListWheel> {
FixedExtentScrollController _scrollController;
double itemHeight = 100.0; // Height of each item in the list
int itemCount = 20;
@override
void initState() {
super.initState();
_scrollController = FixedExtentScrollController();
_scrollController.addListener(_handleScroll);
}
@override
void dispose() {
_scrollController.removeListener(_handleScroll);
_scrollController.dispose();
super.dispose();
}
void _handleScroll() {
setState(() {
// Trigger a rebuild to update the transition animations
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
height: itemHeight * 5, // Visible height of the list
child: ListWheelScrollView.useDelegate(
controller: _scrollController,
itemExtent: itemHeight,
physics: FixedExtentScrollPhysics(),
childDelegate: ListWheelChildBuilderDelegate(
builder: (context, index) {
final double scrollOffset = _scrollController.offset;
final double itemScrollOffset = scrollOffset % itemHeight;
// Calculate transition values for entering and exiting animations
final double enteringScale = 1.0 - (itemScrollOffset / itemHeight);
final double exitingScale = itemScrollOffset / itemHeight;
return AnimatedBuilder(
animation: _scrollController,
builder: (context, child) {
return Transform.scale(
scale: (enteringScale + exitingScale).clamp(0.6, 1.0), // Adjust the range as needed
child: child,
);
},
child: Center(
child: Container(
width: 200,
height: 80,
color: Colors.blue,
child: Center(
child: Text(
'Item $index',
style: TextStyle(color: Colors.white),
),
),
),
),
);
},
childCount: itemCount,
),
),
),
);
}
}
我使用的代码: -
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'List',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: const List(),
);
}
}
class List extends StatefulWidget {
const List({Key? key}) : super(key: key);
@override
_ListState createState() => _ListState();
}
class _ListState extends State<List> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
height: 500,
child: ListWheelScrollView(
itemExtent: 100,
physics: const FixedExtentScrollPhysics(),
onSelectedItemChanged: (value) {
},
children: [
for (int i = 0; i < 5; i++) ...[
Container(
color: Colors.green,
height: 50,
width: 50,
)
]
]),
),
));
}
}
答:
我认为这个包将解决您的问题。https://pub.dev/packages/clickable_list_wheel_view
例:
import 'package:clickable_list_wheel_view/clickable_list_wheel_widget.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Material(
child: MyHomePage(),
),
);
}
}
class MyHomePage extends StatelessWidget {
final _scrollController = FixedExtentScrollController();
static const double _itemHeight = 60;
static const int _itemCount = 100;
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: ClickableListWheelScrollView(
scrollController: _scrollController,
itemHeight: _itemHeight,
itemCount: _itemCount,
onItemTapCallback: (index) {
print("onItemTapCallback index: $index");
},
child: ListWheelScrollView.useDelegate(
controller: _scrollController,
itemExtent: _itemHeight,
physics: FixedExtentScrollPhysics(),
overAndUnderCenterOpacity: 0.5,
perspective: 0.002,
onSelectedItemChanged: (index) {
print("onSelectedItemChanged index: $index");
},
childDelegate: ListWheelChildBuilderDelegate(
builder: (context, index) => _child(index),
childCount: _itemCount,
),
),
),
),
);
}
Widget _child(int index) {
return SizedBox(
height: _itemHeight,
child: ListTile(
leading: Icon(
IconData(int.parse("0xe${index + 200}"),
fontFamily: 'MaterialIcons'),
size: 50),
title: Text('Heart Shaker'),
subtitle: Text('Description here'),
),
);
}
}
我不能在这里发布它,因为 gif 大小很高。您可以从我分享的链接中看到此代码的输出: https://raw.githubusercontent.com/cilestal/clickable_list_wheel_view/master/example/example.gif
我希望我有所帮助。享受你的工作。
评论
我扩展了 ChatGPT 代码,试试看:
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('Smooth Transition in ListWheelScrollView')),
body: TransitionListWheel(),
),
);
}
}
class TransitionListWheel extends StatefulWidget {
@override
_TransitionListWheelState createState() => _TransitionListWheelState();
}
class _TransitionListWheelState extends State<TransitionListWheel> {
late final FixedExtentScrollController _scrollController;
double itemHeight = 100.0; // Height of each item in the list
int itemCount = 20;
int viewCount = 5;
@override
void initState() {
super.initState();
_scrollController = FixedExtentScrollController();
_scrollController.addListener(_handleScroll);
}
@override
void dispose() {
_scrollController.removeListener(_handleScroll);
_scrollController.dispose();
super.dispose();
}
void _handleScroll() {
setState(() {
// Trigger a rebuild to update the transition animations
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
height: itemHeight * viewCount, // Visible height of the list
child: ListWheelScrollView.useDelegate(
controller: _scrollController,
itemExtent: itemHeight,
physics: FixedExtentScrollPhysics(),
childDelegate: ListWheelChildBuilderDelegate(
builder: (context, index) {
final double scrollOffset = _scrollController.offset;
final double itemScrollOffset = scrollOffset % itemHeight;
// Calculate transition values for entering and exiting animations
final double enteringScale =
1.0 - (itemScrollOffset / itemHeight);
final double exitingScale = itemScrollOffset / itemHeight;
final centerOffset = index * itemHeight - scrollOffset;
final itemOpacity = (double x) {
if (x < viewCount ~/ 2 * itemHeight) return 1.0;
if (x > (viewCount ~/ 2 + 1) * itemHeight) return 0.0;
return 1 - (x - viewCount ~/ 2 * itemHeight) / itemHeight;
}(centerOffset.abs() + 25); // adjust as needed
return AnimatedBuilder(
animation: _scrollController,
builder: (context, child) {
// return Text('offset: $centerOffset, opacity: $itemOpacity');
return Transform.scale(
scale: (enteringScale + exitingScale)
.clamp(0.6, 1.0), // Adjust the range as needed
child: Opacity(opacity: itemOpacity, child: child),
);
},
child: Center(
child: Container(
width: 200,
height: 80,
color: Colors.blue,
child: Center(
child: Text(
'Item $index',
style: TextStyle(color: Colors.white),
),
),
),
),
);
},
childCount: itemCount,
),
),
),
);
}
}
编辑:这就是它的工作原理
此线计算每个索引与中心的偏移量。这意味着如果索引在中心,则偏移量为 0,如果索引在顶部,则为负值,如果索引在底部,则为正值。有点像 (-y) 轴。
final centerOffset = index * itemHeight - scrollOffset;
我们可以用绝对值来判断一个项目离中心有多远
假设我们有 5 个可见的项目,这意味着高度将为 500,因此两个边缘项目将具有价值(-200 和 200)
我们可以做一个像这样的切换案例逻辑:Container
centerOffset
如果小于 200(项目靠近中心),则不透明度将为 1centerOffset
如果大于 200 +(不在可见区域),则不透明度将为 0centerOffset
itemHeight
else(项目接近边缘,介于 200 和 300 之间)
- 首先,我们减去 200 以获得介于 0 和 100 之间的变量
(x - viewCount ~/ 2 * itemHeight)
- 我们将该变量除以 100(对不起,我在之前的答案中硬编码了 100,应该是)等于 0 和 1
itemHeight
itemHeight
- 我们不需要介于 0 和 之间的值,我们需要介于 1 和 0,所以我们将 1 - x 应用于值
之前的所有数字都与高度 500 () 相关联,这就是我使用viewCount
viewCount ~/ 2
取消在构建器中注释以可视化我刚刚解释的内容Text
注意:如果你不喜欢线性不透明度,也可以转换它
final transformedOpacity = Curves.fastEaseInToSlowEaseOut.transform(itemOpacity);
评论