Reputation: 158
I am trying to create an AnimatedList
using bloc
pattern, but ran into some problems.
When I am setting initialItemsCount of AnimatedList
to state.itemList.length
, it does not build. Although when I am printing out state.itemList
(that comes from ListBloc
) in BlocConsumer
's listener it prints out the itemList
.
So, the question is why is this not working?
I tried to do the same with ListView.builder
and it works fine. Am I missing something or the AnimatedList
is not even supposed to work using bloc?
Here is some sample code, made it super simple for this case:
MyApp class:
class _MyAppState extends State<MyApp> {
final key = GlobalKey<AnimatedListState>();
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => ListBloc()..add(LoadList()),
child: MaterialApp(
home: SafeArea(
child: BlocConsumer<ListBloc, ListState>(
listener: (_, state) {
print(state.itemList);
},
builder: (context, state) => Scaffold(
body: Column(
children: [
Expanded(
child: AnimatedList(
key: key,
initialItemCount: state.itemList.length,
itemBuilder: (context, index, animation) {
return Item(
animation: animation,
index: index,
text: state.itemList[index],
onTap: () => removeItem(index, state.itemList));
},
),
),
],
),
),
),
),
),
);
}
void removeItem(int index, List<String> items) {
final item = items.removeAt(index);
key.currentState?.removeItem(
index,
(context, animation) => Item(
animation: animation,
index: index,
text: items[index],
onTap: () => removeItem(index, items)));
}
}
Item class:
class Item extends StatelessWidget {
final Animation<double> animation;
final int index;
final String text;
final VoidCallback onTap;
const Item(
{required this.animation,
required this.index,
required this.text,
required this.onTap,
Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
return ScaleTransition(
scale: animation,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: onTap,
child: Container(
color: Colors.blue,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(text),
),
)),
),
);
}
}
Bloc:
class ListEvent extends Equatable {
const ListEvent();
@override
List<Object> get props => [];
}
class LoadList extends ListEvent {}
class ListState extends Equatable {
final List<String> itemList;
const ListState({required this.itemList});
@override
List<Object> get props => [itemList];
}
class ListBloc extends Bloc<ListEvent, ListState> {
ListBloc() : super(ListState(itemList: []));
@override
Stream<ListState> mapEventToState(ListEvent event) async* {
if (event is LoadList) {
var items = ['1', '2', '3', '4', '5', '6'];
yield ListState(itemList: items);
}
}
}
Thanks!
Upvotes: 4
Views: 1394
Reputation: 274
I've experienced similar scenarios regarding AnimatedList in how the itemBuilder is called and the list is rebuilt.
If you set a break-point above the AnimatedList widget (i.e your parent Expanded widget) and one within the itemBuilder call, you'll probably notice that the itemBuilder is being executed in some cases without the first break-point being hit.
This looks to be because the animated nature of AnimatedList causing the itemBuilder to be directly called as part of framework page refresh (outside of the encompassing widgets, such as your BlocConsumer, so your state is not yet set as you'd expect).
Upvotes: 2