559 lines
21 KiB
Dart
559 lines
21 KiB
Dart
// Copyright 2016 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.
|
|
|
|
// This file contains a wacky demonstration of creating a custom ScrollPosition
|
|
// setup. It's testing that we don't regress the factoring of the
|
|
// ScrollPosition/ScrollActivity logic into a state where you can no longer
|
|
// implement this, e.g. by oversimplifying it or overfitting it to the features
|
|
// built into the framework itself.
|
|
|
|
import 'dart:collection';
|
|
import 'dart:math' as math;
|
|
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
class LinkedScrollController extends ScrollController {
|
|
LinkedScrollController({ this.before, this.after });
|
|
|
|
LinkedScrollController before;
|
|
LinkedScrollController after;
|
|
|
|
ScrollController _parent;
|
|
|
|
void setParent(ScrollController newParent) {
|
|
if (_parent != null) {
|
|
positions.forEach(_parent.detach);
|
|
}
|
|
_parent = newParent;
|
|
if (_parent != null) {
|
|
positions.forEach(_parent.attach);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void attach(ScrollPosition position) {
|
|
assert(position is LinkedScrollPosition, 'A LinkedScrollController must only be used with LinkedScrollPositions.');
|
|
final LinkedScrollPosition linkedPosition = position;
|
|
assert(linkedPosition.owner == this, 'A LinkedScrollPosition cannot change controllers once created.');
|
|
super.attach(position);
|
|
_parent?.attach(position);
|
|
}
|
|
|
|
@override
|
|
void detach(ScrollPosition position) {
|
|
super.detach(position);
|
|
_parent?.detach(position);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
if (_parent != null) {
|
|
positions.forEach(_parent.detach);
|
|
}
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
LinkedScrollPosition createScrollPosition(ScrollPhysics physics, ScrollContext context, ScrollPosition oldPosition) {
|
|
return new LinkedScrollPosition(
|
|
this,
|
|
physics: physics,
|
|
context: context,
|
|
initialPixels: initialScrollOffset,
|
|
oldPosition: oldPosition,
|
|
);
|
|
}
|
|
|
|
bool get canLinkWithBefore => before != null && before.hasClients;
|
|
|
|
bool get canLinkWithAfter => after != null && after.hasClients;
|
|
|
|
Iterable<LinkedScrollActivity> linkWithBefore(LinkedScrollPosition driver) {
|
|
assert(canLinkWithBefore);
|
|
return before.link(driver);
|
|
}
|
|
|
|
Iterable<LinkedScrollActivity> linkWithAfter(LinkedScrollPosition driver) {
|
|
assert(canLinkWithAfter);
|
|
return after.link(driver);
|
|
}
|
|
|
|
Iterable<LinkedScrollActivity> link(LinkedScrollPosition driver) sync* {
|
|
assert(hasClients);
|
|
for (LinkedScrollPosition position in positions)
|
|
yield position.link(driver);
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
if (before != null && after != null) {
|
|
description.add('links: ⬌');
|
|
} else if (before != null) {
|
|
description.add('links: ⬅');
|
|
} else if (after != null) {
|
|
description.add('links: ➡');
|
|
} else {
|
|
description.add('links: none');
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
class LinkedScrollPosition extends ScrollPositionWithSingleContext {
|
|
LinkedScrollPosition(this.owner, {
|
|
ScrollPhysics physics,
|
|
ScrollContext context,
|
|
double initialPixels,
|
|
ScrollPosition oldPosition,
|
|
}) : assert(owner != null),
|
|
super(
|
|
physics: physics,
|
|
context: context,
|
|
initialPixels: initialPixels,
|
|
oldPosition: oldPosition,
|
|
);
|
|
|
|
final LinkedScrollController owner;
|
|
|
|
Set<LinkedScrollActivity> _beforeActivities;
|
|
Set<LinkedScrollActivity> _afterActivities;
|
|
|
|
@override
|
|
void beginActivity(ScrollActivity newActivity) {
|
|
if (newActivity == null)
|
|
return;
|
|
if (_beforeActivities != null) {
|
|
for (LinkedScrollActivity activity in _beforeActivities)
|
|
activity.unlink(this);
|
|
_beforeActivities.clear();
|
|
}
|
|
if (_afterActivities != null) {
|
|
for (LinkedScrollActivity activity in _afterActivities)
|
|
activity.unlink(this);
|
|
_afterActivities.clear();
|
|
}
|
|
super.beginActivity(newActivity);
|
|
}
|
|
|
|
@override
|
|
void applyUserOffset(double delta) {
|
|
updateUserScrollDirection(delta > 0.0 ? ScrollDirection.forward : ScrollDirection.reverse);
|
|
final double value = pixels - physics.applyPhysicsToUserOffset(this, delta);
|
|
|
|
if (value == pixels)
|
|
return;
|
|
|
|
double beforeOverscroll = 0.0;
|
|
if (owner.canLinkWithBefore && (value < minScrollExtent)) {
|
|
final double delta = value - minScrollExtent;
|
|
_beforeActivities ??= new HashSet<LinkedScrollActivity>();
|
|
_beforeActivities.addAll(owner.linkWithBefore(this));
|
|
for (LinkedScrollActivity activity in _beforeActivities)
|
|
beforeOverscroll = math.min(activity.moveBy(delta), beforeOverscroll);
|
|
assert(beforeOverscroll <= 0.0);
|
|
}
|
|
|
|
double afterOverscroll = 0.0;
|
|
if (owner.canLinkWithAfter && (value > maxScrollExtent)) {
|
|
final double delta = value - maxScrollExtent;
|
|
_afterActivities ??= new HashSet<LinkedScrollActivity>();
|
|
_afterActivities.addAll(owner.linkWithAfter(this));
|
|
for (LinkedScrollActivity activity in _afterActivities)
|
|
afterOverscroll = math.max(activity.moveBy(delta), afterOverscroll);
|
|
assert(afterOverscroll >= 0.0);
|
|
}
|
|
|
|
assert(beforeOverscroll == 0.0 || afterOverscroll == 0.0);
|
|
|
|
final double localOverscroll = setPixels(value.clamp(
|
|
owner.canLinkWithBefore ? minScrollExtent : -double.infinity,
|
|
owner.canLinkWithAfter ? maxScrollExtent : double.infinity,
|
|
));
|
|
|
|
assert(localOverscroll == 0.0 || (beforeOverscroll == 0.0 && afterOverscroll == 0.0));
|
|
}
|
|
|
|
void _userMoved(ScrollDirection direction) {
|
|
updateUserScrollDirection(direction);
|
|
}
|
|
|
|
LinkedScrollActivity link(LinkedScrollPosition driver) {
|
|
if (this.activity is! LinkedScrollActivity)
|
|
beginActivity(new LinkedScrollActivity(this));
|
|
final LinkedScrollActivity activity = this.activity;
|
|
activity.link(driver);
|
|
return activity;
|
|
}
|
|
|
|
void unlink(LinkedScrollActivity activity) {
|
|
if (_beforeActivities != null)
|
|
_beforeActivities.remove(activity);
|
|
if (_afterActivities != null)
|
|
_afterActivities.remove(activity);
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('owner: $owner');
|
|
}
|
|
}
|
|
|
|
class LinkedScrollActivity extends ScrollActivity {
|
|
LinkedScrollActivity(
|
|
LinkedScrollPosition delegate,
|
|
) : super(delegate);
|
|
|
|
@override
|
|
LinkedScrollPosition get delegate => super.delegate;
|
|
|
|
final Set<LinkedScrollPosition> drivers = new HashSet<LinkedScrollPosition>();
|
|
|
|
void link(LinkedScrollPosition driver) {
|
|
drivers.add(driver);
|
|
}
|
|
|
|
void unlink(LinkedScrollPosition driver) {
|
|
drivers.remove(driver);
|
|
if (drivers.isEmpty)
|
|
delegate?.goIdle();
|
|
}
|
|
|
|
@override
|
|
bool get shouldIgnorePointer => true;
|
|
|
|
@override
|
|
bool get isScrolling => true;
|
|
|
|
// LinkedScrollActivity is not self-driven but moved by calls to the [moveBy]
|
|
// method.
|
|
@override
|
|
double get velocity => 0.0;
|
|
|
|
double moveBy(double delta) {
|
|
assert(drivers.isNotEmpty);
|
|
ScrollDirection commonDirection;
|
|
for (LinkedScrollPosition driver in drivers) {
|
|
commonDirection ??= driver.userScrollDirection;
|
|
if (driver.userScrollDirection != commonDirection)
|
|
commonDirection = ScrollDirection.idle;
|
|
}
|
|
delegate._userMoved(commonDirection);
|
|
return delegate.setPixels(delegate.pixels + delta);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
for (LinkedScrollPosition driver in drivers)
|
|
driver.unlink(this);
|
|
super.dispose();
|
|
}
|
|
}
|
|
|
|
class Test extends StatefulWidget {
|
|
@override
|
|
_TestState createState() => new _TestState();
|
|
}
|
|
|
|
class _TestState extends State<Test> {
|
|
LinkedScrollController _beforeController;
|
|
LinkedScrollController _afterController;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_beforeController = new LinkedScrollController();
|
|
_afterController = new LinkedScrollController(before: _beforeController);
|
|
_beforeController.after = _afterController;
|
|
}
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
_beforeController.setParent(PrimaryScrollController.of(context));
|
|
_afterController.setParent(PrimaryScrollController.of(context));
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_beforeController.dispose();
|
|
_afterController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: new Column(
|
|
children: <Widget>[
|
|
new Expanded(
|
|
child: new ListView(
|
|
controller: _beforeController,
|
|
children: <Widget>[
|
|
new Container(
|
|
margin: const EdgeInsets.all(8.0),
|
|
padding: const EdgeInsets.all(8.0),
|
|
height: 250.0,
|
|
color: const Color(0xFF90F090),
|
|
child: const Center(child: Text('Hello A')),
|
|
),
|
|
new Container(
|
|
margin: const EdgeInsets.all(8.0),
|
|
padding: const EdgeInsets.all(8.0),
|
|
height: 250.0,
|
|
color: const Color(0xFF90F090),
|
|
child: const Center(child: Text('Hello B')),
|
|
),
|
|
new Container(
|
|
margin: const EdgeInsets.all(8.0),
|
|
padding: const EdgeInsets.all(8.0),
|
|
height: 250.0,
|
|
color: const Color(0xFF90F090),
|
|
child: const Center(child: Text('Hello C')),
|
|
),
|
|
new Container(
|
|
margin: const EdgeInsets.all(8.0),
|
|
padding: const EdgeInsets.all(8.0),
|
|
height: 250.0,
|
|
color: const Color(0xFF90F090),
|
|
child: const Center(child: Text('Hello D')),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const Divider(),
|
|
new Expanded(
|
|
child: new ListView(
|
|
controller: _afterController,
|
|
children: <Widget>[
|
|
new Container(
|
|
margin: const EdgeInsets.all(8.0),
|
|
padding: const EdgeInsets.all(8.0),
|
|
height: 250.0,
|
|
color: const Color(0xFF9090F0),
|
|
child: const Center(child: Text('Hello 1')),
|
|
),
|
|
new Container(
|
|
margin: const EdgeInsets.all(8.0),
|
|
padding: const EdgeInsets.all(8.0),
|
|
height: 250.0,
|
|
color: const Color(0xFF9090F0),
|
|
child: const Center(child: Text('Hello 2')),
|
|
),
|
|
new Container(
|
|
margin: const EdgeInsets.all(8.0),
|
|
padding: const EdgeInsets.all(8.0),
|
|
height: 250.0,
|
|
color: const Color(0xFF9090F0),
|
|
child: const Center(child: Text('Hello 3')),
|
|
),
|
|
new Container(
|
|
margin: const EdgeInsets.all(8.0),
|
|
padding: const EdgeInsets.all(8.0),
|
|
height: 250.0,
|
|
color: const Color(0xFF9090F0),
|
|
child: const Center(child: Text('Hello 4')),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
testWidgets('LinkedScrollController - 1', (WidgetTester tester) async {
|
|
await tester.pumpWidget(new Test());
|
|
expect(find.text('Hello A'), findsOneWidget);
|
|
expect(find.text('Hello 1'), findsOneWidget);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 4'), findsNothing);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await tester.fling(find.text('Hello A'), const Offset(0.0, -50.0), 10000.0);
|
|
await tester.pumpAndSettle();
|
|
await tester.pump(const Duration(seconds: 2));
|
|
expect(find.text('Hello A'), findsNothing);
|
|
expect(find.text('Hello 1'), findsOneWidget);
|
|
expect(find.text('Hello D'), findsOneWidget);
|
|
expect(find.text('Hello 4'), findsNothing);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await tester.drag(find.text('Hello D'), const Offset(0.0, -10000.0));
|
|
await tester.pump(const Duration(seconds: 2));
|
|
expect(find.text('Hello A'), findsNothing);
|
|
expect(find.text('Hello 1'), findsNothing);
|
|
expect(find.text('Hello D'), findsOneWidget);
|
|
expect(find.text('Hello 4'), findsOneWidget);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await tester.drag(find.text('Hello D'), const Offset(0.0, -10000.0));
|
|
await tester.pump(const Duration(seconds: 2));
|
|
expect(find.text('Hello A'), findsNothing);
|
|
expect(find.text('Hello 1'), findsNothing);
|
|
expect(find.text('Hello D'), findsOneWidget);
|
|
expect(find.text('Hello 4'), findsOneWidget);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await tester.drag(find.text('Hello 4'), const Offset(0.0, -10000.0));
|
|
await tester.pump(const Duration(seconds: 2));
|
|
expect(find.text('Hello A'), findsNothing);
|
|
expect(find.text('Hello 1'), findsNothing);
|
|
expect(find.text('Hello D'), findsOneWidget);
|
|
expect(find.text('Hello 4'), findsOneWidget);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await tester.drag(find.text('Hello D'), const Offset(0.0, 10000.0));
|
|
await tester.pump(const Duration(seconds: 2));
|
|
expect(find.text('Hello A'), findsOneWidget);
|
|
expect(find.text('Hello 1'), findsNothing);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 4'), findsOneWidget);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await tester.drag(find.text('Hello A'), const Offset(0.0, 10000.0));
|
|
await tester.pump(const Duration(seconds: 2));
|
|
expect(find.text('Hello A'), findsOneWidget);
|
|
expect(find.text('Hello 1'), findsNothing);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 4'), findsOneWidget);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await tester.drag(find.text('Hello A'), const Offset(0.0, -10000.0));
|
|
await tester.pump(const Duration(seconds: 2));
|
|
expect(find.text('Hello A'), findsNothing);
|
|
expect(find.text('Hello 1'), findsNothing);
|
|
expect(find.text('Hello D'), findsOneWidget);
|
|
expect(find.text('Hello 4'), findsOneWidget);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await tester.drag(find.text('Hello 4'), const Offset(0.0, 10000.0));
|
|
await tester.pump(const Duration(seconds: 2));
|
|
expect(find.text('Hello A'), findsOneWidget);
|
|
expect(find.text('Hello 1'), findsOneWidget);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 4'), findsNothing);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await tester.drag(find.text('Hello 1'), const Offset(0.0, 10000.0));
|
|
await tester.pump(const Duration(seconds: 2));
|
|
expect(find.text('Hello A'), findsOneWidget);
|
|
expect(find.text('Hello 1'), findsOneWidget);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 4'), findsNothing);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await tester.drag(find.text('Hello 1'), const Offset(0.0, -10000.0));
|
|
await tester.pump(const Duration(seconds: 2));
|
|
expect(find.text('Hello A'), findsOneWidget);
|
|
expect(find.text('Hello 1'), findsNothing);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 4'), findsOneWidget);
|
|
});
|
|
testWidgets('LinkedScrollController - 2', (WidgetTester tester) async {
|
|
await tester.pumpWidget(new Test());
|
|
expect(find.text('Hello A'), findsOneWidget);
|
|
expect(find.text('Hello B'), findsOneWidget);
|
|
expect(find.text('Hello C'), findsNothing);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 1'), findsOneWidget);
|
|
expect(find.text('Hello 2'), findsOneWidget);
|
|
expect(find.text('Hello 3'), findsNothing);
|
|
expect(find.text('Hello 4'), findsNothing);
|
|
final TestGesture gestureTop = await tester.startGesture(const Offset(200.0, 150.0));
|
|
final TestGesture gestureBottom = await tester.startGesture(const Offset(600.0, 450.0));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
await gestureTop.moveBy(const Offset(0.0, -270.0));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(find.text('Hello A'), findsNothing);
|
|
expect(find.text('Hello B'), findsOneWidget);
|
|
expect(find.text('Hello C'), findsOneWidget);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 1'), findsOneWidget);
|
|
expect(find.text('Hello 2'), findsOneWidget);
|
|
expect(find.text('Hello 3'), findsNothing);
|
|
expect(find.text('Hello 4'), findsNothing);
|
|
await gestureBottom.moveBy(const Offset(0.0, -270.0));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(find.text('Hello A'), findsNothing);
|
|
expect(find.text('Hello B'), findsOneWidget);
|
|
expect(find.text('Hello C'), findsOneWidget);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 1'), findsNothing);
|
|
expect(find.text('Hello 2'), findsOneWidget);
|
|
expect(find.text('Hello 3'), findsOneWidget);
|
|
expect(find.text('Hello 4'), findsNothing);
|
|
await gestureTop.moveBy(const Offset(0.0, -270.0));
|
|
await gestureBottom.moveBy(const Offset(0.0, -270.0));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(find.text('Hello A'), findsNothing);
|
|
expect(find.text('Hello B'), findsNothing);
|
|
expect(find.text('Hello C'), findsOneWidget);
|
|
expect(find.text('Hello D'), findsOneWidget);
|
|
expect(find.text('Hello 1'), findsNothing);
|
|
expect(find.text('Hello 2'), findsNothing);
|
|
expect(find.text('Hello 3'), findsOneWidget);
|
|
expect(find.text('Hello 4'), findsOneWidget);
|
|
await gestureTop.moveBy(const Offset(0.0, 270.0));
|
|
await gestureBottom.moveBy(const Offset(0.0, 270.0));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(find.text('Hello A'), findsNothing);
|
|
expect(find.text('Hello B'), findsOneWidget);
|
|
expect(find.text('Hello C'), findsOneWidget);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 1'), findsNothing);
|
|
expect(find.text('Hello 2'), findsOneWidget);
|
|
expect(find.text('Hello 3'), findsOneWidget);
|
|
expect(find.text('Hello 4'), findsNothing);
|
|
await gestureBottom.moveBy(const Offset(0.0, 270.0));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(find.text('Hello A'), findsNothing);
|
|
expect(find.text('Hello B'), findsOneWidget);
|
|
expect(find.text('Hello C'), findsOneWidget);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 1'), findsOneWidget);
|
|
expect(find.text('Hello 2'), findsOneWidget);
|
|
expect(find.text('Hello 3'), findsNothing);
|
|
expect(find.text('Hello 4'), findsNothing);
|
|
await gestureBottom.moveBy(const Offset(0.0, 50.0));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(find.text('Hello A'), findsOneWidget);
|
|
expect(find.text('Hello B'), findsOneWidget);
|
|
expect(find.text('Hello C'), findsNothing);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 1'), findsOneWidget);
|
|
expect(find.text('Hello 2'), findsOneWidget);
|
|
expect(find.text('Hello 3'), findsNothing);
|
|
expect(find.text('Hello 4'), findsNothing);
|
|
await gestureBottom.moveBy(const Offset(0.0, 50.0));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(find.text('Hello A'), findsOneWidget);
|
|
expect(find.text('Hello B'), findsOneWidget);
|
|
expect(find.text('Hello C'), findsNothing);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 1'), findsOneWidget);
|
|
expect(find.text('Hello 2'), findsOneWidget);
|
|
expect(find.text('Hello 3'), findsNothing);
|
|
expect(find.text('Hello 4'), findsNothing);
|
|
await gestureBottom.moveBy(const Offset(0.0, 50.0));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(find.text('Hello A'), findsOneWidget);
|
|
expect(find.text('Hello B'), findsOneWidget);
|
|
expect(find.text('Hello C'), findsNothing);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 1'), findsOneWidget);
|
|
expect(find.text('Hello 2'), findsOneWidget);
|
|
expect(find.text('Hello 3'), findsNothing);
|
|
expect(find.text('Hello 4'), findsNothing);
|
|
await gestureTop.moveBy(const Offset(0.0, -270.0));
|
|
expect(find.text('Hello A'), findsOneWidget);
|
|
expect(find.text('Hello B'), findsOneWidget);
|
|
expect(find.text('Hello C'), findsNothing);
|
|
expect(find.text('Hello D'), findsNothing);
|
|
expect(find.text('Hello 1'), findsOneWidget);
|
|
expect(find.text('Hello 2'), findsOneWidget);
|
|
expect(find.text('Hello 3'), findsNothing);
|
|
expect(find.text('Hello 4'), findsNothing);
|
|
await tester.pump(const Duration(seconds: 1));
|
|
await tester.pump(const Duration(seconds: 60));
|
|
});
|
|
}
|