Flutter动画列表插件implicitly_animated_list的使用
Flutter动画列表插件implicitly_animated_list的使用
在Flutter开发中,implicitly_animated_list
插件提供了一种优雅的方式来创建带有过渡动画的列表。它可以在数据变化时自动为插入、删除或更新的项目添加动画效果。下面将详细介绍如何使用这个插件。
基本用法
ImplicitlyAnimatedList
是一个可以接收任意类型数据列表,并根据这些数据构建Widget的组件。当传入的数据发生变化时(例如:添加、移除或修改),它会以动画的形式展示这些变更。最简单的例子如下:
ImplicitlyAnimatedList(
itemData: [1, 2, 3, 4], // 数据源
itemBuilder: (_, number) => ListTile(title: Text('$number')), // 构建每个item的widget
)
这段代码展示了如何基于一个整数列表来创建一个带动画的列表。每当您更改此列表中的元素并进行热重载时,列表将会以动画形式展现变化。
高级用法
结合 Stream 使用
除了静态的数据列表外,implicitly_animated_list
还非常适合与 StreamBuilder
搭配使用,以便实时响应数据流的变化。这里有一个使用用户模型作为示例的片段:
class User {
const User({required this.firstName, required this.lastName});
final String firstName;
final String lastName;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is User &&
runtimeType == other.runtimeType &&
firstName == other.firstName &&
lastName == other.lastName;
@override
int get hashCode => firstName.hashCode ^ lastName.hashCode;
}
...
StreamBuilder<List<User>>(
stream: someSource.usersStream,
builder: (context, snapshot) {
if (!snapshot.hasData) return Center(child: CircularProgressIndicator());
return ImplicitlyAnimatedList(
itemData: snapshot.data!,
itemBuilder: (context, user) {
return ListTile(title: Text('${user.firstName} ${user.lastName}'));
},
);
},
);
在这个例子中,我们定义了一个 User
类,并实现了它的 ==
和 hashCode
方法,这样 ImplicitlyAnimatedList
就能够正确识别哪些项是相同的,从而只对实际发生变化的部分应用动画。
自定义比较逻辑
有时你可能需要自定义判断两个项目是否相同的方式。通过设置 itemEquality
参数,你可以提供一个函数来自定义这项行为。比如上面的例子中,我们可以选择只比较用户的姓氏来决定是否为同一人:
itemEquality: (a, b) => a.lastName == b.lastName,
使用 Sliver 版本
如果你的应用程序中已经有了 CustomScrollView
或者你需要更复杂的滚动控制,那么可以考虑使用 SliverImplicitlyAnimatedList
,它提供了类似的功能但适用于 Sliver
环境:
CustomScrollView(
slivers: [
SliverImplicitlyAnimatedList(
itemData: myListOfItems,
itemBuilder: (context, item) => ListTile(title: Text('$item')),
),
],
),
完整示例
为了更好地理解如何使用 implicitly_animated_list
,下面给出一个完整的示例应用程序代码,该程序会生成随机数字并显示在一个可动画的列表中:
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:implicitly_animated_list/implicitly_animated_list.dart';
void main() => runApp(MyApp());
class MyItem {
const MyItem({required this.value, required this.version});
final int value;
final int version;
@override
bool operator ==(Object other) =>
other is MyItem && this.value == other.value && this.version == other.version;
@override
int get hashCode => Object.hash(value, version);
@override
String toString() => '$value (version: $version)';
}
List<MyItem> updateItems(List<MyItem> old) {
return List.generate(
10,
(i) => MyItem(
value: Random().nextInt(3),
version: i < old.length ? old[i].version + 1 : 0,
),
);
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var _items = updateItems([]);
var _initialAnimation = true;
var _customEquality = true;
var _resetKey = ValueKey(0);
bool _myCustomEquality(MyItem a, MyItem b) => a.value == b.value;
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Implicitly animated list'),
actions: [_buildSettingsButton()],
),
floatingActionButton: FloatingActionButton.extended(
label: Text('Generate numbers'),
icon: Icon(Icons.directions_walk),
onPressed: () => setState(() {
_items = updateItems(_items);
print(_items);
}),
),
body: Row(
children: [
_listWithTitle(
title: 'ImplicitlyAnimatedList',
child: ImplicitlyAnimatedList(
key: _resetKey,
initialAnimation: _initialAnimation,
insertDuration: Duration(milliseconds: 500),
deleteDuration: Duration(milliseconds: 500),
itemData: _items,
itemBuilder: (context, item) => ListTile(title: Text('$item')),
itemEquality: _customEquality ? _myCustomEquality : null,
),
),
VerticalDivider(),
_listWithTitle(
title: 'SliverImplicitlyAnimatedList',
child: CustomScrollView(
slivers: [
SliverImplicitlyAnimatedList(
key: _resetKey,
initialAnimation: _initialAnimation,
insertDuration: Duration(milliseconds: 500),
deleteDuration: Duration(milliseconds: 500),
itemData: _items,
itemBuilder: (context, item) =>
ListTile(title: Text('$item')),
itemEquality: _customEquality ? _myCustomEquality : null,
),
],
),
),
],
),
),
);
}
Widget _buildSettingsButton() {
return PopupMenuButton(
icon: Icon(Icons.settings),
itemBuilder: (context) => [
CheckedPopupMenuItem(
value: _SettingAction.toggleAnimation,
checked: _initialAnimation,
child: Text("Use initial animation?"),
),
CheckedPopupMenuItem(
value: _SettingAction.toggleEquality,
checked: _customEquality,
child: Text("Use custom equality?"),
),
PopupMenuItem(
value: _SettingAction.performReset,
child: Text("Reset View"),
),
],
onSelected: (value) => setState(() {
switch (value) {
case _SettingAction.toggleAnimation:
_initialAnimation = !_initialAnimation;
break;
case _SettingAction.toggleEquality:
_customEquality = !_customEquality;
break;
case _SettingAction.performReset:
_resetKey = ValueKey(_resetKey.value + 1);
_items = updateItems([]);
break;
}
}),
);
}
Widget _listWithTitle({required String title, required Widget child}) {
return Expanded(
child: Column(
children: [
Text(title, textAlign: TextAlign.center),
Expanded(child: child),
],
),
);
}
}
enum _SettingAction { toggleAnimation, toggleEquality, performReset }
以上就是关于 implicitly_animated_list
插件的基本介绍和使用方法。希望这对你的项目有所帮助!
更多关于Flutter动画列表插件implicitly_animated_list的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter动画列表插件implicitly_animated_list的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter中使用implicitly_animated_list
插件来创建动画列表的示例代码。这个插件允许你创建带有平滑动画效果的列表项。
首先,你需要在你的pubspec.yaml
文件中添加implicitly_animated_list
依赖项:
dependencies:
flutter:
sdk: flutter
implicitly_animated_list: ^0.7.0 # 请检查最新版本号
然后运行flutter pub get
来安装依赖项。
接下来,我们编写一个完整的Flutter应用示例,展示如何使用implicitly_animated_list
:
import 'package:flutter/material.dart';
import 'package:implicitly_animated_list/implicitly_animated_list.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Implicitly Animated List Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: AnimatedListDemo(),
);
}
}
class AnimatedListDemo extends StatefulWidget {
@override
_AnimatedListDemoState createState() => _AnimatedListDemoState();
}
class _AnimatedListDemoState extends State<AnimatedListDemo> {
final List<String> _items = List.generate(20, (i) => "Item ${i + 1}");
final List<GlobalKey> _keys = List.generate(20, (i) => GlobalKey());
void _addItem() {
setState(() {
_items.add("Item ${_items.length + 1}");
_keys.add(GlobalKey());
});
}
void _removeItem(int index) {
setState(() {
_items.removeAt(index);
_keys.removeAt(index);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Implicitly Animated List Demo'),
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: _addItem,
),
],
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: ImplicitlyAnimatedList<String>(
items: _items,
itemBuilder: (context, index, item) {
return SlideTransition(
position: Tween<Offset>(
begin: Offset(0, 0.5),
end: Offset.zero,
).animate(context.transition),
child: FadeTransition(
opacity: Tween<double>(begin: 0, end: 1).animate(context.transition),
child: Card(
key: _keys[index],
child: ListTile(
title: Text(item),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => _removeItem(index),
),
),
),
),
);
},
),
),
);
}
}
在这个示例中:
- 我们创建了一个包含20个字符串项的列表
_items
,以及一个对应的全局键列表_keys
,用于确保每个列表项具有唯一的键。 ImplicitlyAnimatedList
接受一个项列表items
和一个项构建器itemBuilder
。itemBuilder
函数为每个项创建了一个带有滑动和淡入动画效果的Card
。SlideTransition
和FadeTransition
用于创建滑动和淡入效果。- 我们通过
Tween
和animate
方法将动画应用到每个列表项上。 - 提供了两个按钮,一个用于添加新项,另一个在每个列表项上用于删除该项。
这个示例展示了如何使用implicitly_animated_list
来创建一个带有平滑动画效果的列表,同时展示了如何动态添加和删除列表项。