flutterwidgetwidget-test-flutter

How to test an AnimatedIcon in Flutter


I want to do a test that allows me to verify if my widget is showing the AnimatedIcon that I provide, as well as to know if the animation is working (changing the icon in an animated way), the problem is that I can't find the way to do it.

  1. Is there a method similar to finder.byIcon(), but that accepts AnimatedIconData instead of IconData?
  2. How to verify that the animation is happening?

Solution

  • 1. Finder for AnimatedIconData

    To get a finder for AnimatedIconData, use the find.byWidgetPredicate method like below (assuming the AnimatedIconData being searched for is AnimatedIcons.menu_arrow):

    final animatedIconFinder = find.byWidgetPredicate((widget) =>
            widget is AnimatedIcon && widget.icon == AnimatedIcons.menu_arrow); 
    

    2. Verification that the animation is happening

    To verify that the animation is happening, check the value of the AnimatedIcon widget's progress property.

    Assuming the animation happens after a floating action button on the screen is tapped, the code sample below shows how to verify that the AnimatedIcon animates after the button is tapped.

    // Gets the [AnimatedIcon] widget
    final animatedIconWidget = tester.widget(animatedIconFinder) as AnimatedIcon;
    
    final animatedIconProgress = animatedIconWidget.progress.value;
    
    // Verifies that [AnimatedIcon] is not animating
    expect(animatedIconProgress, 0);
    
    // Finds [FloatingActionButton]
    final floatingActionButtonFinder = 
        find.widgetWithIcon(FloatingActionButton, Icons.change_circle);
    
    // Taps [FloatingActionButton]
    await tester.tap(floatingActionButtonFinder);
    
    await tester.pumpAndSettle();
    
    final updatedAnimatedIconProgress = animatedIconWidget.progress.value;
    
    // Verifies that the [AnimatedIcon] has completed its animation
    expect(updatedAnimatedIconProgress, 1);
    

    Using a Custom Finder

    To make your finder more concise and similar to find.byIcon, do the following:

    1. Create a custom Finder by extending MatchFinder and add the matching logic in the matches method (This is a modification of the find.byIcon implementation).

      class _WidgetAnimatedIconFinder extends MatchFinder {
        _WidgetAnimatedIconFinder(this.icon, {super.skipOffstage});
      
        final AnimatedIconData icon;
      
        @override
        String get description => 'icon "$icon"';
      
        @override
        bool matches(Element candidate) {
          final Widget widget = candidate.widget;
          return widget is AnimatedIcon && widget.icon == icon;
        }
      }
      
    2. Create an extension on the CommonFinders class and add a byAnimatedIcon method that uses the custom Finder created in step 1. The CommonFinders class is provided by the flutter_test package and the find constant (as in find.byIcon) is an instance of CommonFinders.

      extension CustomFindersExtension on CommonFinders {
        Finder byAnimatedIcon(AnimatedIconData icon) =>
          _WidgetAnimatedIconFinder(icon);
      }
      
    3. Use find.byAnimatedIcon to find the AnimatedIcon by supplying the AnimatedIconData being searched for.

      Replace the Finder using find.byWidgetPredicate with this below:

      final animatedIconFinder = find.byAnimatedIcon(AnimatedIcons.menu_arrow);