Flutter中的ListView/ScrollablePositionedList.builder动画项-如何设置动画



我试着制作这个动画(https://www.instagram.com/p/CQ3XpZaD-yM/)但我做不到。

首先,我试图用Listview.builder和ScrollController在屏幕上找到项目,然后在谷歌上搜索我发现了这种依赖关系(https://pub.dev/packages/scrollable_positioned_list),但是我无法产生相同的效果,代码变得肮脏。

为了缩放项目,我使用了Transform.scale。所以我在项目之间没有平稳的转换,因为我有一个布尔决策。。。以及其他奇怪的问题,当你快速滚动时,行为不是预期的,也不能进行缩放,当你滚动时工作正常;"慢";。

import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData.dark(),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final ItemScrollController itemScrollController = ItemScrollController();
final ItemPositionsListener itemPositionsListener =
ItemPositionsListener.create();
double scaleOne = 1.0;
double scaleTwo = 1.0;
int index = 0;
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return SafeArea(
child: Scaffold(
body: Column(
children: [
SizedBox(
height: size.height * 0.35,
width: double.infinity,
child: ScrollablePositionedList.builder(
physics: const BouncingScrollPhysics(),
itemPositionsListener: itemPositionsListener,
itemScrollController: itemScrollController,
scrollDirection: Axis.horizontal,
itemCount: 10,
itemBuilder: (context, i) {
return ValueListenableBuilder(
valueListenable: itemPositionsListener.itemPositions,
builder: (context, value, Widget? child) {
if (itemPositionsListener
.itemPositions.value.isNotEmpty) {
var scroll = itemPositionsListener
.itemPositions.value
.toList();
print(scroll);
index = scroll[0].index;
scaleOne =
(scroll[0].itemTrailingEdge * 1.5).clamp(0, 1);
}
return _ContainerAndText(
size: size,
///How I can make a smooth move?
scale: index == i ? scaleOne : 1.0,
i: i);
});
}),
),
],
),
),
);
}
}
class _ContainerAndText extends StatelessWidget {
const _ContainerAndText({
Key? key,
required this.size,
required this.scale,
required this.i,
}) : super(key: key);
final Size size;
final double scale;
final int i;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Container(
alignment: Alignment.bottomLeft,
width: size.width * 0.61,
child: Stack(
alignment: Alignment.bottomLeft,
children: [
Container(
width: size.width * 0.5,
decoration: BoxDecoration(
color: Colors.primaries[i],
borderRadius: BorderRadius.circular(45),
),
),
Container(
alignment: Alignment.center,
margin: EdgeInsets.only(left: size.width * 0.3, bottom: 15),
height: size.height * 0.30,
width: double.infinity,
child: Transform(
alignment: Alignment.bottomLeft,
transform: Matrix4.identity()..scale(scale, scale),
child: Text(
'${i}',
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white, fontSize: 260),
),
),
),
],
),
),
);
}
}

使用PageView.builder可以帮助。。。

import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData.dark(),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final ItemScrollController itemScrollController = ItemScrollController();
final ItemPositionsListener itemPositionsListener =
ItemPositionsListener.create();
final _pageController = PageController(viewportFraction: 0.83);
double currentPage = 0;
@override
void initState() {
_pageController.addListener(_listenerPage);
super.initState();
}
@override
void dispose() {
_pageController.removeListener(_listenerPage);
_pageController.dispose();
}
void _listenerPage() {
setState(() {
currentPage = _pageController.page!;
});
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return SafeArea(
child: Scaffold(
body: Center(
child: SizedBox(
height: size.height * 0.35,
width: size.width,
child: PageView.builder(
controller: _pageController,
itemCount: 10,
scrollDirection: Axis.horizontal,
itemBuilder: (context, i) {
final result = currentPage - i;
double value = (-1 * result * result + 1);
return _ContainerAndText(
i: i,
size: size,
scale: result == i ? 1 - value : 0.8 * value);
})),
),
),
);
}
}
class _ContainerAndText extends StatelessWidget {
const _ContainerAndText({
Key? key,
required this.size,
required this.scale,
required this.i,
}) : super(key: key);
final Size size;
final double scale;
final int i;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Container(
alignment: Alignment.bottomLeft,
//color: Colors.red.withOpacity(0.5),
width: size.width * 0.61,
child: Stack(
alignment: Alignment.bottomLeft,
children: [
Container(
width: size.width * 0.5,
decoration: BoxDecoration(
color: Colors.primaries[i],
borderRadius: BorderRadius.circular(45),
),
),
Container(
alignment: Alignment.center,
margin: EdgeInsets.only(left: size.width * 0.3, bottom: 15),
height: size.height * 0.30,
width: double.infinity,
child: Transform(
alignment: Alignment.bottomLeft,
transform: Matrix4.identity()..scale(scale, scale),
child: Text(
'${i}',
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white, fontSize: 260),
),
),
),
],
),
),
);
}
}

您现在可以使用具有相同键的Hero小部件来实现类似的转换。

https://docs.flutter.dev/development/ui/animations/hero-animations

最新更新