rdev
rdev

Reputation: 158

Why is AnimatedList not building using list from bloc state in flutter?

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

Answers (1)

Pip
Pip

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

Related Questions