Flutter声明式动画列表插件declarative_animated_list的使用
Flutter声明式动画列表插件 declarative_animated_list
的使用
declarative_animated_list
是一个实现自动更新的动画列表小部件。它基于 Android 的 DiffUtil
进行了轻微的修改,以支持 Flutter 的声明式 UI。
基本用法
创建动画列表项
首先,你需要创建一个包含动画效果的列表项。这里我们使用 FadeTransition
和 SizeTransition
来实现淡入淡出和尺寸变化的效果。
Widget _buildAnimatedTile(Animation<double> animation, PresentationModel model) {
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
child: SomeWidget(model),
),
);
}
创建移除动画列表项
同样地,你可以为移除的列表项创建动画效果。
Widget _buildRemovingTile(final Animation<double> animation, final PresentationModel model) {
// 可以使用与插入相同的动画效果,或者自定义其他效果
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
child: SomeWidget(model),
),
);
}
使用 DeclarativeList
最后,将上述方法集成到 DeclarativeList
中。
final declarativeList = DeclarativeList<PresentationModel>(
items: presentationModels,
itemBuilder: (ctx, model, index, animation) => _buildAnimatedTile(animation, model),
removeBuilder: (ctx, model, index, animation) => _buildRemovingTile(animation, model),
);
完整示例
以下是一个完整的示例,展示了如何在 Flutter 应用中使用 declarative_animated_list
插件来管理待办事项列表。
主文件 main.dart
import 'package:declarative_animated_list/declarative_animated_list.dart';
import 'package:example/bloc.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
final ToDosBloc bloc = ToDosBloc();
return MaterialApp(
title: 'Declarative Animated List Demo',
theme: ThemeData.dark(),
home: ToDosPage(
bloc: bloc,
),
);
}
}
class ToDosPage extends StatelessWidget {
final ToDosBloc bloc;
const ToDosPage({required this.bloc, super.key});
@override
Widget build(final BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: StreamBuilder<ToDosState>(
stream: bloc.toDosState,
builder: (_, final AsyncSnapshot<ToDosState> snapshot) {
return _buildBody(context, snapshot);
},
),
floatingActionButton: _buildFab(context),
);
}
Widget _buildBody(
BuildContext context,
AsyncSnapshot<ToDosState> snapshot,
) {
if (snapshot.hasData) {
return _buildBasedOnState(context, snapshot.data!);
} else if (snapshot.hasError) {
return Center(
child: Text(
'Error occurred: ${snapshot.error}',
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.black),
),
);
} else {
return const Center(child: CircularProgressIndicator());
}
}
Widget _buildBasedOnState(
BuildContext context,
ToDosState state,
) {
final List<ToDoPresentationModel> toDos = state.toDos.toList()
..sort(
(left, right) {
if (left.isCompleted == right.isCompleted) {
return left.description.compareTo(right.description);
} else {
return left.isCompleted ? 1 : -1;
}
},
);
if (toDos.isEmpty) {
return Center(
child: Icon(
Icons.delete_outline,
size: MediaQuery.of(context).size.height * 0.4,
color: Theme.of(context).colorScheme.secondary,
),
);
} else {
return DeclarativeList<ToDoPresentationModel>(
items: toDos,
insertDuration: const Duration(milliseconds: 500),
removeDuration: const Duration(milliseconds: 500),
itemBuilder: (_, model, __, anim) => _buildFadeAndSizeTransitioningTile(anim, model),
removeBuilder: (_, model, __, anim) => _buildFadeAndSizeTransitioningTile(anim, model),
);
}
}
Widget _buildFadeAndSizeTransitioningTile(
Animation<double> animation,
ToDoPresentationModel model,
) {
return FadeTransition(
opacity: animation,
child: SizeTransition(sizeFactor: animation, child: _buildTile(model)),
);
}
Widget _buildTile(ToDoPresentationModel toDo) {
return ListTile(
onLongPress: () => bloc.removeToDo.add(RemoveToDoEvent(toDo)),
title: Text(toDo.description),
leading: IconButton(
icon: Icon(toDo.isCompleted ? Icons.check : Icons.sync),
onPressed: () {
bloc.changeToDoStatus.add(
ChangeCompletionStatusEvent(
toDo,
shouldBeCompleted: !toDo.isCompleted,
),
);
},
),
);
}
Widget _buildFab(BuildContext context) {
return FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () => showDialog<void>(
context: context,
builder: (ctx) {
final controller = TextEditingController();
final size = MediaQuery.sizeOf(ctx);
return Container(
height: size.height * 0.5,
width: size.width * 0.5,
padding: const EdgeInsets.all(16.0),
child: Material(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(controller: controller),
),
IconButton(
icon: const Icon(Icons.check),
onPressed: () {
bloc.addToDo.add(AddToDoEvent(controller.text));
Navigator.of(ctx).pop();
},
)
],
),
),
);
},
),
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty('bloc', bloc));
}
}
BLoC 文件 bloc.dart
import 'dart:async';
class ToDosBloc {
final _toDosController = StreamController<ToDosState>.broadcast();
Stream<ToDosState> get toDosState => _toDosController.stream;
final _addToDo = StreamController<AddToDoEvent>.broadcast();
Sink<AddToDoEvent> get addToDo => _addToDo.sink;
final _removeToDo = StreamController<RemoveToDoEvent>.broadcast();
Sink<RemoveToDoEvent> get removeToDo => _removeToDo.sink;
final _changeToDoStatus = StreamController<ChangeCompletionStatusEvent>.broadcast();
Sink<ChangeCompletionStatusEvent> get changeToDoStatus => _changeToDoStatus.sink;
ToDosBloc() {
_init();
}
void _init() {
_addToDo.stream.listen(_handleAddToDo);
_removeToDo.stream.listen(_handleRemoveToDo);
_changeToDoStatus.stream.listen(_handleChangeToDoStatus);
_toDosController.sink.add(ToDosState([]));
}
void _handleAddToDo(AddToDoEvent event) {
final currentState = _toDosController.valueOrNull ?? ToDosState([]);
final newToDos = List<ToDoPresentationModel>.from(currentState.toDos)
..add(ToDoPresentationModel(event.description, false));
_toDosController.sink.add(ToDosState(newToDos));
}
void _handleRemoveToDo(RemoveToDoEvent event) {
final currentState = _toDosController.valueOrNull ?? ToDosState([]);
final newToDos = List<ToDoPresentationModel>.from(currentState.toDos)
..remove(event.toDo);
_toDosController.sink.add(ToDosState(newToDos));
}
void _handleChangeToDoStatus(ChangeCompletionStatusEvent event) {
final currentState = _toDosController.valueOrNull ?? ToDosState([]);
final newToDos = List<ToDoPresentationModel>.from(currentState.toDos)
..[currentState.toDos.indexOf(event.toDo)] = event.toDo.copyWith(isCompleted: event.shouldBeCompleted);
_toDosController.sink.add(ToDosState(newToDos));
}
void dispose() {
_toDosController.close();
_addToDo.close();
_removeToDo.close();
_changeToDoStatus.close();
}
}
class AddToDoEvent {
final String description;
AddToDoEvent(this.description);
}
class RemoveToDoEvent {
final ToDoPresentationModel toDo;
RemoveToDoEvent(this.toDo);
}
class ChangeCompletionStatusEvent {
final ToDoPresentationModel toDo;
final bool shouldBeCompleted;
ChangeCompletionStatusEvent(this.toDo, {required this.shouldBeCompleted});
}
class ToDosState {
final List<ToDoPresentationModel> toDos;
ToDosState(this.toDos);
}
class ToDoPresentationModel {
final String description;
final bool isCompleted;
ToDoPresentationModel(this.description, this.isCompleted);
ToDoPresentationModel copyWith({String? description, bool? isCompleted}) {
return ToDoPresentationModel(description ?? this.description, isCompleted ?? this.isCompleted);
}
}
通过以上代码,你可以在 Flutter 应用中使用 declarative_animated_list
插件来创建带有动画效果的待办事项列表。希望这个示例对你有所帮助!
更多关于Flutter声明式动画列表插件declarative_animated_list的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter声明式动画列表插件declarative_animated_list的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,declarative_animated_list
是一个用于在 Flutter 中实现声明式动画列表的插件。这个插件允许你以一种简洁且声明式的方式创建带有动画效果的列表。以下是一个简单的代码示例,展示如何使用 declarative_animated_list
插件来创建一个带有动画效果的列表。
首先,确保你的 Flutter 项目中已经添加了 declarative_animated_list
插件。在你的 pubspec.yaml
文件中添加以下依赖:
dependencies:
flutter:
sdk: flutter
declarative_animated_list: ^x.y.z # 替换为最新版本号
然后,运行 flutter pub get
来获取依赖。
接下来,在你的 Dart 文件中,你可以按照以下方式使用 DeclarativeAnimatedList
:
import 'package:flutter/material.dart';
import 'package:declarative_animated_list/declarative_animated_list.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Declarative Animated List Example'),
),
body: AnimatedListExample(),
),
);
}
}
class AnimatedListExample extends StatefulWidget {
@override
_AnimatedListExampleState createState() => _AnimatedListExampleState();
}
class _AnimatedListExampleState extends State<AnimatedListExample> {
final List<String> items = List.generate(20, (i) => "Item ${i + 1}");
final GlobalKey<DeclarativeAnimatedListState> listKey = GlobalKey<DeclarativeAnimatedListState>();
void _addItem() {
setState(() {
items.add("Item ${items.length + 1}");
});
}
void _removeItem(int index) {
setState(() {
items.removeAt(index);
});
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
ElevatedButton(
onPressed: _addItem,
child: Text('Add Item'),
),
Expanded(
child: DeclarativeAnimatedList<String>(
key: listKey,
items: items,
itemBuilder: (context, index, item) {
return ListTile(
title: Text(item),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => _removeItem(index),
),
);
},
itemAnimationBuilder: (context, index, animation) {
return SlideTransition(
position: Tween<Offset>(
begin: Offset(1.0, 0.0),
end: Offset(0.0, 0.0),
).animate(animation),
child: FadeTransition(
opacity: Tween<double>(
begin: 0.0,
end: 1.0,
).animate(animation),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 16.0),
child: Divider(
height: 0,
),
),
),
);
},
),
),
],
),
);
}
}
代码解释
-
依赖导入:
import 'package:flutter/material.dart';
导入 Flutter 的基础组件。import 'package:declarative_animated_list/declarative_animated_list.dart';
导入declarative_animated_list
插件。
-
主应用:
MyApp
类是应用的主入口,包含一个Scaffold
,其主体是AnimatedListExample
组件。
-
动画列表:
AnimatedListExample
是一个StatefulWidget
,包含一个列表items
和一个GlobalKey
用于控制列表的动画状态。_addItem
方法用于向列表中添加新项。_removeItem
方法用于从列表中移除指定索引的项。
-
列表构建:
DeclarativeAnimatedList
组件用于创建带有动画效果的列表。items
属性指定列表的数据源。itemBuilder
属性用于构建列表项。itemAnimationBuilder
属性用于定义列表项的动画效果。在这个例子中,我们使用了SlideTransition
和FadeTransition
来实现滑动和淡入淡出的动画效果。
这个示例展示了如何使用 declarative_animated_list
插件来创建一个带有基本动画效果的列表。你可以根据需求进一步自定义动画效果和列表项的外观。