// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/widgets.dart'; void main() { testWidgets('AnimatedList initialItemCount', (WidgetTester tester) async { final Map<int, Animation<double>> animations = <int, Animation<double>>{}; await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: AnimatedList( initialItemCount: 2, itemBuilder: (BuildContext context, int index, Animation<double> animation) { animations[index] = animation; return SizedBox( height: 100.0, child: Center( child: Text('item $index'), ), ); }, ), ), ); expect(find.text('item 0'), findsOneWidget); expect(find.text('item 1'), findsOneWidget); expect(animations.containsKey(0), true); expect(animations.containsKey(1), true); expect(animations[0].value, 1.0); expect(animations[1].value, 1.0); }); testWidgets('AnimatedList insert', (WidgetTester tester) async { final GlobalKey<AnimatedListState> listKey = GlobalKey<AnimatedListState>(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: AnimatedList( key: listKey, itemBuilder: (BuildContext context, int index, Animation<double> animation) { return SizeTransition( key: ValueKey<int>(index), axis: Axis.vertical, sizeFactor: animation, child: SizedBox( height: 100.0, child: Center( child: Text('item $index'), ), ), ); }, ), ), ); double itemHeight(int index) => tester.getSize(find.byKey(ValueKey<int>(index), skipOffstage: false)).height; double itemTop(int index) => tester.getTopLeft(find.byKey(ValueKey<int>(index), skipOffstage: false)).dy; double itemBottom(int index) => tester.getBottomLeft(find.byKey(ValueKey<int>(index), skipOffstage: false)).dy; listKey.currentState.insertItem(0, duration: const Duration(milliseconds: 100)); await tester.pump(); // Newly inserted item 0's height should animate from 0 to 100 expect(itemHeight(0), 0.0); await tester.pump(const Duration(milliseconds: 50)); expect(itemHeight(0), 50.0); await tester.pump(const Duration(milliseconds: 50)); expect(itemHeight(0), 100.0); // The list now contains one fully expanded item at the top: expect(find.text('item 0'), findsOneWidget); expect(itemTop(0), 0.0); expect(itemBottom(0), 100.0); listKey.currentState.insertItem(0, duration: const Duration(milliseconds: 100)); listKey.currentState.insertItem(0, duration: const Duration(milliseconds: 100)); await tester.pump(); // The height of the newly inserted items at index 0 and 1 should animate from 0 to 100. // The height of the original item, now at index 2, should remain 100. expect(itemHeight(0), 0.0); expect(itemHeight(1), 0.0); expect(itemHeight(2), 100.0); await tester.pump(const Duration(milliseconds: 50)); expect(itemHeight(0), 50.0); expect(itemHeight(1), 50.0); expect(itemHeight(2), 100.0); await tester.pump(const Duration(milliseconds: 50)); expect(itemHeight(0), 100.0); expect(itemHeight(1), 100.0); expect(itemHeight(2), 100.0); // The newly inserted "item 1" and "item 2" appear above "item 0" expect(find.text('item 0'), findsOneWidget); expect(find.text('item 1'), findsOneWidget); expect(find.text('item 2'), findsOneWidget); expect(itemTop(0), 0.0); expect(itemBottom(0), 100.0); expect(itemTop(1), 100.0); expect(itemBottom(1), 200.0); expect(itemTop(2), 200.0); expect(itemBottom(2), 300.0); }); testWidgets('AnimatedList remove', (WidgetTester tester) async { final GlobalKey<AnimatedListState> listKey = GlobalKey<AnimatedListState>(); final List<int> items = <int>[0, 1, 2]; Widget buildItem(BuildContext context, int item, Animation<double> animation) { return SizeTransition( key: ValueKey<int>(item), axis: Axis.vertical, sizeFactor: animation, child: SizedBox( height: 100.0, child: Center( child: Text('item $item', textDirection: TextDirection.ltr), ), ), ); } await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: AnimatedList( key: listKey, initialItemCount: 3, itemBuilder: (BuildContext context, int index, Animation<double> animation) { return buildItem(context, items[index], animation); }, ), ), ); double itemTop(int index) => tester.getTopLeft(find.byKey(ValueKey<int>(index))).dy; double itemBottom(int index) => tester.getBottomLeft(find.byKey(ValueKey<int>(index))).dy; expect(find.text('item 0'), findsOneWidget); expect(find.text('item 1'), findsOneWidget); expect(find.text('item 2'), findsOneWidget); items.removeAt(0); listKey.currentState.removeItem(0, (BuildContext context, Animation<double> animation) => buildItem(context, 0, animation), duration: const Duration(milliseconds: 100), ); // Items 0, 1, 2 at 0, 100, 200. All heights 100. expect(itemTop(0), 0.0); expect(itemBottom(0), 100.0); expect(itemTop(1), 100.0); expect(itemBottom(1), 200.0); expect(itemTop(2), 200.0); expect(itemBottom(2), 300.0); // Newly removed item 0's height should animate from 100 to 0 over 100ms // Items 0, 1, 2 at 0, 50, 150. Item 0's height is 50. await tester.pump(); await tester.pump(const Duration(milliseconds: 50)); expect(itemTop(0), 0.0); expect(itemBottom(0), 50.0); expect(itemTop(1), 50.0); expect(itemBottom(1), 150.0); expect(itemTop(2), 150.0); expect(itemBottom(2), 250.0); // Items 1, 2 at 0, 100. await tester.pumpAndSettle(); expect(itemTop(1), 0.0); expect(itemBottom(1), 100.0); expect(itemTop(2), 100.0); expect(itemBottom(2), 200.0); }); }