在初始化时为 ListView 中的元素设置动画
Animate elements in ListView on initialization
我想实现如下所示(动画风格无关紧要,我正在寻找实现方式)
但是,所有资源和问题仅说明如何创建
项目添加或删除动画。
我当前的代码(我使用 BLoC 模式)
class _MembersPageState extends State<MembersPage> {
@override
Widget build(BuildContext context) {
return BlocProvider<MembersPageBloc>(
create: (context) =>
MembersPageBloc(userRepository: UserRepository.instance)..add(MembersPageShowed()),
child: BlocBuilder<MembersPageBloc, MembersPageState>(
builder: (context, state) {
if (state is MembersPageSuccess) {
return ListView.builder(
itemCount: state.users.length,
itemBuilder: (context, index) {
User user = state.users[index];
return ListTile(
isThreeLine: true,
leading: Icon(Icons.person, size: 36),
title: Text(user.name),
subtitle: Text(user.username),
onTap: () => null,
);
},
);
} else
return Text("I don't care");
},
),
);
}
}
AnimatedOpacity
和 AnimatedPositioned
等动画小部件可以做到。您可以通过这种方式使用不同的动画小部件、曲线、开始值和结束值来制作不同的动画。
但是,Listview 中子控件的生命周期有点复杂。它们根据滚动位置被销毁和重新创建。如果子部件有一个在初始化时开始的动画,那么只要子部件对 UI.
可见,它就会重新激活。
这是我的 hacky 解决方案。我使用静态布尔值来指示它是第一次还是重新创建状态,并简单地忽略重新创建。你可以在 Dartpad 中快速尝试这个。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: Colors.black54),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: ListView(
children: List.generate(
25, (i) => AnimatedListItem(i, key: ValueKey<int>(i))),
)),
);
}
}
class AnimatedListItem extends StatefulWidget {
final int index;
AnimatedListItem(this.index, {Key key}) : super(key: key);
@override
_AnimatedListItemState createState() => _AnimatedListItemState();
}
class _AnimatedListItemState extends State<AnimatedListItem> {
bool _animate = false;
static bool _isStart = true;
@override
void initState() {
super.initState();
_isStart
? Future.delayed(Duration(milliseconds: widget.index * 100), () {
setState(() {
_animate = true;
_isStart = false;
});
})
: _animate = true;
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedOpacity(
duration: Duration(milliseconds: 1000),
opacity: _animate ? 1 : 0,
curve: Curves.easeInOutQuart,
child: AnimatedPadding(
duration: Duration(milliseconds: 1000),
padding: _animate
? const EdgeInsets.all(4.0)
: const EdgeInsets.only(top: 10),
child: Container(
constraints: BoxConstraints.expand(height: 100),
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
widget.index.toString(),
style: TextStyle(fontSize: 24),
),
),
),
),
),
);
}
}
我在 dartpad 上创建了一个 gist 展示了如何为 ListView
添加初始动画
重点是为每个列表项创建一个AnimationController
,缓存它,并在动画完成时清理。
我想实现如下所示(动画风格无关紧要,我正在寻找实现方式)
但是,所有资源和问题仅说明如何创建 项目添加或删除动画。
我当前的代码(我使用 BLoC 模式)
class _MembersPageState extends State<MembersPage> {
@override
Widget build(BuildContext context) {
return BlocProvider<MembersPageBloc>(
create: (context) =>
MembersPageBloc(userRepository: UserRepository.instance)..add(MembersPageShowed()),
child: BlocBuilder<MembersPageBloc, MembersPageState>(
builder: (context, state) {
if (state is MembersPageSuccess) {
return ListView.builder(
itemCount: state.users.length,
itemBuilder: (context, index) {
User user = state.users[index];
return ListTile(
isThreeLine: true,
leading: Icon(Icons.person, size: 36),
title: Text(user.name),
subtitle: Text(user.username),
onTap: () => null,
);
},
);
} else
return Text("I don't care");
},
),
);
}
}
AnimatedOpacity
和 AnimatedPositioned
等动画小部件可以做到。您可以通过这种方式使用不同的动画小部件、曲线、开始值和结束值来制作不同的动画。
但是,Listview 中子控件的生命周期有点复杂。它们根据滚动位置被销毁和重新创建。如果子部件有一个在初始化时开始的动画,那么只要子部件对 UI.
可见,它就会重新激活。这是我的 hacky 解决方案。我使用静态布尔值来指示它是第一次还是重新创建状态,并简单地忽略重新创建。你可以在 Dartpad 中快速尝试这个。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: Colors.black54),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: ListView(
children: List.generate(
25, (i) => AnimatedListItem(i, key: ValueKey<int>(i))),
)),
);
}
}
class AnimatedListItem extends StatefulWidget {
final int index;
AnimatedListItem(this.index, {Key key}) : super(key: key);
@override
_AnimatedListItemState createState() => _AnimatedListItemState();
}
class _AnimatedListItemState extends State<AnimatedListItem> {
bool _animate = false;
static bool _isStart = true;
@override
void initState() {
super.initState();
_isStart
? Future.delayed(Duration(milliseconds: widget.index * 100), () {
setState(() {
_animate = true;
_isStart = false;
});
})
: _animate = true;
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedOpacity(
duration: Duration(milliseconds: 1000),
opacity: _animate ? 1 : 0,
curve: Curves.easeInOutQuart,
child: AnimatedPadding(
duration: Duration(milliseconds: 1000),
padding: _animate
? const EdgeInsets.all(4.0)
: const EdgeInsets.only(top: 10),
child: Container(
constraints: BoxConstraints.expand(height: 100),
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
widget.index.toString(),
style: TextStyle(fontSize: 24),
),
),
),
),
),
);
}
}
我在 dartpad 上创建了一个 gist 展示了如何为 ListView
重点是为每个列表项创建一个AnimationController
,缓存它,并在动画完成时清理。