flutter-add-widget-test

Implement a component-level test using `WidgetTester` to verify UI rendering and user interactions (tapping, scrolling, entering text). Use when validating…

INSTALLATION
npx skills add https://github.com/flutter/skills --skill flutter-add-widget-test
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$27

  • **WidgetTester**: The primary interface for building and interacting with widgets in the test environment. Provided automatically by the testWidgets() function.
  • **Finder**: Locates widgets in the test environment (e.g., find.text('Submit'), find.byType(TextField), find.byKey(Key('submit_btn'))).
  • **Matcher**: Verifies the presence or state of widgets located by a Finder (e.g., findsOneWidget, findsNothing, findsNWidgets(2), matchesGoldenFile).

Workflow: Implementing a Widget Test

Copy the following checklist to track progress when implementing a new widget test.

Task Progress

  • Step 1: Define the test. Use testWidgets('description', (WidgetTester tester) async { ... }).
  • Step 2: Build the widget. Call await tester.pumpWidget(MyWidget()) to render the UI. Wrap the widget in a MaterialApp or Directionality widget if it requires inherited directional or theme data.
  • Step 3: Locate elements. Instantiate Finder objects for the target widgets.
  • Step 4: Verify initial state. Use expect(finder, matcher) to validate the initial render.
  • Step 5: Simulate interactions. Execute gestures or inputs (e.g., await tester.tap(buttonFinder)).
  • Step 6: Rebuild the tree. Call await tester.pump() or await tester.pumpAndSettle() to process state changes.
  • Step 7: Verify updated state. Use expect() to validate the UI after the interaction.
  • Step 8: Run and validate. Execute flutter test test/your_test_file_test.dart.
  • Step 9: Feedback Loop. Review test output -> identify failing matchers -> adjust widget logic or test assertions -> re-run until passing.

Interaction & State Management

Apply the following conditional logic based on the type of interaction or state change being tested:

  • If testing static rendering: Call await tester.pumpWidget() once, then immediately run expect() assertions.
  • If testing standard state changes (e.g., button taps):
  • Call await tester.tap(finder).
  • Call await tester.pump() to trigger a single frame rebuild.
  • If testing animations, transitions, or asynchronous UI updates:
  • Trigger the action (e.g., await tester.drag(finder, Offset(500, 0))).
  • Call await tester.pumpAndSettle() to repeatedly pump frames until no more frames are scheduled (animation completes).
  • If testing text input: Call await tester.enterText(textFieldFinder, 'Input string').
  • If testing items in a dynamic or long list: Call await tester.scrollUntilVisible(itemFinder, 500.0, scrollable: listFinder) to ensure the target widget is rendered before interacting with it.

Examples

High-Fidelity Widget Test Implementation

**Target Widget (lib/todo_list.dart):**

import 'package:flutter/material.dart';

class TodoList extends StatefulWidget {

  const TodoList({super.key});

  @override

  State<TodoList> createState() => _TodoListState();

}

class _TodoListState extends State<TodoList> {

  final todos = <String>[];

  final controller = TextEditingController();

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      home: Scaffold(

        body: Column(

          children: [

            TextField(controller: controller),

            Expanded(

              child: ListView.builder(

                itemCount: todos.length,

                itemBuilder: (context, index) {

                  final todo = todos[index];

                  return Dismissible(

                    key: Key('$todo$index'),

                    onDismissed: (_) => setState(() => todos.removeAt(index)),

                    child: ListTile(title: Text(todo)),

                  );

                },

              ),

            ),

          ],

        ),

        floatingActionButton: FloatingActionButton(

          onPressed: () {

            setState(() {

              todos.add(controller.text);

              controller.clear();

            });

          },

          child: const Icon(Icons.add),

        ),

      ),

    );

  }

}

**Test Implementation (test/todo_list_test.dart):**

import 'package:flutter/material.dart';

import 'package:flutter_test/flutter_test.dart';

import 'package:my_app/todo_list.dart';

void main() {

  testWidgets('Add and remove a todo item', (WidgetTester tester) async {

    // 1. Build the widget

    await tester.pumpWidget(const TodoList());

    // 2. Verify initial state

    expect(find.byType(ListTile), findsNothing);

    // 3. Enter text into the TextField

    await tester.enterText(find.byType(TextField), 'Buy groceries');

    // 4. Tap the add button

    await tester.tap(find.byType(FloatingActionButton));

    // 5. Rebuild the widget to reflect the new state

    await tester.pump();

    // 6. Verify the item was added

    expect(find.text('Buy groceries'), findsOneWidget);

    // 7. Swipe the item to dismiss it

    await tester.drag(find.byType(Dismissible), const Offset(500, 0));

    // 8. Build the widget until the dismiss animation ends

    await tester.pumpAndSettle();

    // 9. Verify the item was removed

    expect(find.text('Buy groceries'), findsNothing);

  });

}
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card