Costruire la schermata con AnimatedList
Ora creiamo lo StatefulWidget che ospita la lista. Punti chiave:
- la
GlobalKey<AnimatedListState> va creata una sola volta in initState (o come campo final), mai nel build;
initialItemCount deve coincidere con il numero di elementi iniziali del modello;
- l'
itemBuilder riceve un Animation<double> che va applicato al widget: tipicamente SizeTransition (per far "crescere" lo spazio) combinata con FadeTransition.
Isoliamo la riga in un widget _TaskTile così da riutilizzarlo sia per gli elementi vivi sia per quelli in uscita.
import 'package:flutter/material.dart';
import 'list_model.dart';
class TaskListPage extends StatefulWidget {
const TaskListPage({super.key});
@override
State<TaskListPage> createState() => _TaskListPageState();
}
class _TaskListPageState extends State<TaskListPage> {
final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
late final ListModel<Task> _tasks;
int _nextId = 0;
@override
void initState() {
super.initState();
final initial = List<Task>.generate(
3,
(i) => Task(id: _nextId++, title: 'Task iniziale ${i + 1}'),
);
_tasks = ListModel<Task>(
listKey: _listKey,
initialItems: initial,
removedItemBuilder: _buildRemovedItem,
);
}
Widget _buildRemovedItem(
Task task,
BuildContext context,
Animation<double> animation,
) {
return _TaskTile(task: task, animation: animation);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('AnimatedList')),
body: AnimatedList(
key: _listKey,
initialItemCount: _tasks.length,
padding: const EdgeInsets.symmetric(vertical: 8),
itemBuilder: (context, index, animation) {
return _TaskTile(
task: _tasks[index],
animation: animation,
);
},
),
);
}
}
class _TaskTile extends StatelessWidget {
const _TaskTile({
required this.task,
required this.animation,
this.onDelete,
});
final Task task;
final Animation<double> animation;
final VoidCallback? onDelete;
@override
Widget build(BuildContext context) {
final curved = CurvedAnimation(
parent: animation,
curve: Curves.easeOutCubic,
);
return SizeTransition(
sizeFactor: curved,
child: FadeTransition(
opacity: curved,
child: Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: ListTile(
leading: CircleAvatar(child: Text('${task.id}')),
title: Text(task.title),
trailing: onDelete == null
? null
: IconButton(
icon: const Icon(Icons.delete_outline),
onPressed: onDelete,
),
),
),
),
);
}
}
Risultato atteso
L'app mostra tre card statiche. Non ci sono ancora animazioni visibili perché nessun elemento viene aggiunto o rimosso.