- Install
- Set up an editor
- Test drive
- Write your first Flutter app, part 1
- Learn more
- Flutter for Android developers
- Flutter for iOS developers
- Flutter for React Native developers
- Flutter for web developers
- Flutter for Xamarin.Forms developers
- Introduction to declarative UI
- Cookbook
- Codelabs
- Tutorials
- User interface
- Introduction to widgets
- Layouts in Flutter
- Layout tutorial
- Dealing with box constraints
- Adding interactivity to your Flutter app
- Adding assets and images
- Navigation & routing
- Introduction to animations
- Animations overview
- Animations tutorial
- Hero Animations
- Staggered Animations
- Advanced UI
- Slivers
- Taps, drags, and other gestures
- Widget catalog
- Data & backend
- State management
- State management
- Start thinking declaratively
- Differentiate between ephemeral state and app state
- Simple app state management
- List of state management approaches
- JSON and serialization
- Firebase
- Accessibility & internationalization
- Accessibility
- Internationalizing Flutter apps
- Platform integration
- Writing custom platform-specific code
- Packages & plugins
- Using packages
- Developing packages & plugins
- Background processes
- Tools & techniques
- Android Studio / IntelliJ
- Visual Studio Code
- Upgrading Flutter
- Hot reload
- Code formatting
- Debugging Flutter apps
- Using OEM debuggers
- Flutter's build modes
- Testing Flutter apps
- Performance best practices
- Flutter performance profiling
- Creating flavors for Flutter
- Preparing an Android App for Release
- Preparing an iOS App for Release
- Continuous Delivery using fastlane with Flutter
- Bootstrap into Dart
- Inside Flutter
- Platform specific behaviors and adaptations
- Technical Overview
- Technical videos
- FAQ
- Flutter widget index
- Install
- Windows install
- MacOS install
- Linux install
- Set up an editor
- Write your first Flutter app, part 1
- Learn more
- Cupertino (iOS-style) widgets
- Layout widgets
- Animation and motion widgets
- Retrieve the value of a text field
- Basic widgets
- Material Components widgets
- Animate the properties of a Container
- Fade a Widget in and out
- Add a Drawer to a screen
- Displaying SnackBars
- Exporting fonts from a package
- Updating the UI based on orientation
- Using Themes to share colors and font styles
- Using custom fonts
- Working with Tabs
- Building a form with validation
- Create and style a text field
- Focus on a Text Field
- Handling changes to a text field
- Retrieve the value of a text field
- Adding Material Touch Ripples
- Handling Taps
- Implement Swipe to Dismiss
- Display images from the internet
- Fade in images with a placeholder
- Working with cached images
- Basic List
- Create a horizontal list
- Creating a Grid List
- Creating lists with different types of items
- Place a floating app bar above a list
- Working with long lists
- Report errors to a service
- Animating a Widget across screens
- Navigate to a new screen and back
- Navigate with named routes
- Pass arguments to a named route
- Return data from a screen
- Send data to a new screen
- Fetch data from the internet
- Making authenticated requests
- Parsing JSON in the background
- Working with WebSockets
- Persist data with SQLite
- Reading and Writing Files
- Storing key-value data on disk
- Play and pause a video
- Take a picture using the Camera
- An introduction to integration testing
- Performance profiling
- Scrolling
- An introduction to unit testing
- Mock dependencies using Mockito
- An introduction to widget testing
- Finding widgets
- Tapping, dragging and entering text
- Development
- Introduction to widgets
- Layout tutorial
- Dealing with box constraints
- Adding interactivity to your Flutter app
- Adding assets and images
- Navigation & routing
- Navigate to a new screen and back
- Send data to a new screen
- Return data from a screen
- Navigate with named routes
- Animating a Widget across screens
- AnimatedList
- Sample App Catalog
- Animations overview
- Animations tutorial
- Staggered Animations
- Slivers
- Taps, drags, and other gestures
- Accessibility widgets
- Assets, images, and icon widgets
- Async widgets
- Input widgets
- Interaction model widgets
- Painting and effect widgets
- Scrolling widgets
- Styling widgets
- Text widgets
- State management
- Start thinking declaratively
- Differentiate between ephemeral state and app state
- Simple app state management
- List of state management approaches
- JSON and serialization
- Accessibility
- Internationalizing Flutter apps
- Writing custom platform-specific code
- Using packages
- Fetch data from the internet
- Developing packages & plugins
- Background processes
- Android Studio / IntelliJ
- Set up an editor
- Flutter inspector
- Creating Useful Bug Reports
- Visual Studio Code
- Set up an editor
- Upgrading Flutter
- Hot reload
- Code formatting
Tapping, dragging and entering text
Many of the Widgets we build not only display information, but also respond to user interactionhttps://flutter.axuer.com/docs/cookbook/testing/widget/. This includes buttons that users can tap on, dragging items across the screen, or entering text into a TextField
https://flutter.axuer.com/docs/cookbook/testing/widget/.
In order to test these interactions, we need a way to simulate them in the test environmenthttps://flutter.axuer.com/docs/cookbook/testing/widget/. To do so, we can use the WidgetTester
class provided by the flutter_test
libraryhttps://flutter.axuer.com/docs/cookbook/testing/widget/.
The WidgetTester
provides methods for entering text, tapping, and dragginghttps://flutter.axuer.com/docs/cookbook/testing/widget/.
In many cases, user interactions will update the state of our apphttps://flutter.axuer.com/docs/cookbook/testing/widget/. In the test environment, Flutter will not automatically rebuild widgets when the state changeshttps://flutter.axuer.com/docs/cookbook/testing/widget/. To ensure our Widget tree is rebuilt after we simulate a user interaction, we must call the pump
or pumpAndSettle
methods provided by the WidgetTester
https://flutter.axuer.com/docs/cookbook/testing/widget/.
Directions
- Create a Widget to test
- Enter text in the text field
- Ensure tapping a button adds the todo
- Ensure swipe-to-dismiss removes the todo
1https://flutter.axuer.com/docs/cookbook/testing/widget/. Create a Widget to test
For this example, we’ll create a basic todo apphttps://flutter.axuer.com/docs/cookbook/testing/widget/. It will have three main features that we’ll want to test:
- Enter text into a
TextField
- Tapping a
FloatingActionButton
adds the text to a list of todos - Swipe-to-dismiss removes the item from the list
To keep the focus on testing, this recipe will not provide a detailed guide on how to build the todo apphttps://flutter.axuer.com/docs/cookbook/testing/widget/. To learn more about how this app is built, please see the relevant recipes:
class TodoList extends StatefulWidget {
@override
_TodoListState createState() => _TodoListState();
}
class _TodoListState extends State<TodoList> {
static const _appTitle = 'Todo List';
final todos = <String>[];
final controller = TextEditingController();
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _appTitle,
home: Scaffold(
appBar: AppBar(
title: Text(_appTitle),
),
body: Column(
children: [
TextField(
controller: controller,
),
Expanded(
child: ListViewhttps://flutter.axuer.com/docs/cookbook/testing/widget/.builder(
itemCount: todoshttps://flutter.axuer.com/docs/cookbook/testing/widget/.length,
itemBuilder: (BuildContext context, int index) {
final todo = todos[index];
return Dismissible(
key: Key('$todo$index'),
onDismissed: (direction) => todoshttps://flutter.axuer.com/docs/cookbook/testing/widget/.removeAt(index),
child: ListTile(title: Text(todo)),
background: Container(color: Colorshttps://flutter.axuer.com/docs/cookbook/testing/widget/.red),
);
},
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
todoshttps://flutter.axuer.com/docs/cookbook/testing/widget/.add(controllerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.text);
controllerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.clear();
});
},
child: Icon(Iconshttps://flutter.axuer.com/docs/cookbook/testing/widget/.add),
),
),
);
}
}
2https://flutter.axuer.com/docs/cookbook/testing/widget/. Enter text in the text field
Now that we have a todo app, we can begin writing our test! In this case, we’ll start by entering text into the TextField
https://flutter.axuer.com/docs/cookbook/testing/widget/.
We can accomplish this task by:
- Building the Widget in the Test Environment
- Using the
enterText
method from theWidgetTester
testWidgets('Add and remove a todo', (WidgetTester tester) async {
// Build the Widget
await testerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.pumpWidget(TodoList());
// Enter 'hi' into the TextField
await testerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.enterText(findhttps://flutter.axuer.com/docs/cookbook/testing/widget/.byType(TextField), 'hi');
});
Note: This recipe builds upon previous Widget testing recipeshttps://flutter.axuer.com/docs/cookbook/testing/widget/. To learn the core concepts of Widget testing, see the following recipes:
3https://flutter.axuer.com/docs/cookbook/testing/widget/. Ensure tapping a button adds the todo
After we’ve entered text into the TextField
, we’ll want to ensure that tapping the FloatingActionButton
adds the item to the listhttps://flutter.axuer.com/docs/cookbook/testing/widget/.
This will involve three steps:
- Tap the add button using the
tap
method - Rebuild the Widget after the state has changed using the
pump
method - Ensure the list item appears on screen
testWidgets('Add and remove a todo', (WidgetTester tester) async {
// Enter text codehttps://flutter.axuer.com/docs/cookbook/testing/widget/.https://flutter.axuer.com/docs/cookbook/testing/widget/.https://flutter.axuer.com/docs/cookbook/testing/widget/.
// Tap the add button
await testerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.tap(findhttps://flutter.axuer.com/docs/cookbook/testing/widget/.byType(FloatingActionButton));
// Rebuild the Widget after the state has changed
await testerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.pump();
// Expect to find the item on screen
expect(findhttps://flutter.axuer.com/docs/cookbook/testing/widget/.text('hi'), findsOneWidget);
});
4https://flutter.axuer.com/docs/cookbook/testing/widget/. Ensure swipe-to-dismiss removes the todo
Finally, we can ensure that performing a swipe-to-dismiss action on the todo item will remove it from the listhttps://flutter.axuer.com/docs/cookbook/testing/widget/. This will involve three steps:
- Use the
drag
method to perform a swipe-to-dismiss actionhttps://flutter.axuer.com/docs/cookbook/testing/widget/. - Use the
pumpAndSettle
method to continually rebuild our Widget tree until the dismiss animation is completehttps://flutter.axuer.com/docs/cookbook/testing/widget/. - Ensure the item no longer appears on screenhttps://flutter.axuer.com/docs/cookbook/testing/widget/.
testWidgets('Add and remove a todo', (WidgetTester tester) async {
// Enter text and add the itemhttps://flutter.axuer.com/docs/cookbook/testing/widget/.https://flutter.axuer.com/docs/cookbook/testing/widget/.https://flutter.axuer.com/docs/cookbook/testing/widget/.
// Swipe the item to dismiss it
await testerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.drag(findhttps://flutter.axuer.com/docs/cookbook/testing/widget/.byType(Dismissible), Offset(500https://flutter.axuer.com/docs/cookbook/testing/widget/.0, 0https://flutter.axuer.com/docs/cookbook/testing/widget/.0));
// Build the Widget until the dismiss animation ends
await testerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.pumpAndSettle();
// Ensure the item is no longer on screen
expect(findhttps://flutter.axuer.com/docs/cookbook/testing/widget/.text('hi'), findsNothing);
});
Complete example
Once we’ve completed these steps, we should have a working app with a test to ensure it works correctly!
import 'package:flutter/materialhttps://flutter.axuer.com/docs/cookbook/testing/widget/.dart';
import 'package:flutter_test/flutter_testhttps://flutter.axuer.com/docs/cookbook/testing/widget/.dart';
void main() {
testWidgets('Add and remove a todo', (WidgetTester tester) async {
// Build the Widget
await testerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.pumpWidget(TodoList());
// Enter 'hi' into the TextField
await testerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.enterText(findhttps://flutter.axuer.com/docs/cookbook/testing/widget/.byType(TextField), 'hi');
// Tap the add button
await testerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.tap(findhttps://flutter.axuer.com/docs/cookbook/testing/widget/.byType(FloatingActionButton));
// Rebuild the Widget with the new item
await testerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.pump();
// Expect to find the item on screen
expect(findhttps://flutter.axuer.com/docs/cookbook/testing/widget/.text('hi'), findsOneWidget);
// Swipe the item to dismiss it
await testerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.drag(findhttps://flutter.axuer.com/docs/cookbook/testing/widget/.byType(Dismissible), Offset(500https://flutter.axuer.com/docs/cookbook/testing/widget/.0, 0https://flutter.axuer.com/docs/cookbook/testing/widget/.0));
// Build the Widget until the dismiss animation ends
await testerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.pumpAndSettle();
// Ensure the item is no longer on screen
expect(findhttps://flutter.axuer.com/docs/cookbook/testing/widget/.text('hi'), findsNothing);
});
}
class TodoList extends StatefulWidget {
@override
_TodoListState createState() => _TodoListState();
}
class _TodoListState extends State<TodoList> {
static const _appTitle = 'Todo List';
final todos = <String>[];
final controller = TextEditingController();
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _appTitle,
home: Scaffold(
appBar: AppBar(
title: Text(_appTitle),
),
body: Column(
children: [
TextField(
controller: controller,
),
Expanded(
child: ListViewhttps://flutter.axuer.com/docs/cookbook/testing/widget/.builder(
itemCount: todoshttps://flutter.axuer.com/docs/cookbook/testing/widget/.length,
itemBuilder: (BuildContext context, int index) {
final todo = todos[index];
return Dismissible(
key: Key('$todo$index'),
onDismissed: (direction) => todoshttps://flutter.axuer.com/docs/cookbook/testing/widget/.removeAt(index),
child: ListTile(title: Text(todo)),
background: Container(color: Colorshttps://flutter.axuer.com/docs/cookbook/testing/widget/.red),
);
},
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
todoshttps://flutter.axuer.com/docs/cookbook/testing/widget/.add(controllerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.text);
controllerhttps://flutter.axuer.com/docs/cookbook/testing/widget/.clear();
});
},
child: Icon(Iconshttps://flutter.axuer.com/docs/cookbook/testing/widget/.add),
),
),
);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论