mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

**FYI for Reviewers:** Much of the API surface matches that of the 2D TreeView in https://github.com/flutter/packages/pull/6592. If it changes here, it should change there, and vice versa. ð [Design Document](https://docs.google.com/document/d/1-aFI7VjkF9yMkWpP94J8T_JREDS-M3bOak26PVehUYg/edit?usp=sharing) This adds classes and associated callbacks and controllers for TreeSliver. Core components: - TreeSliver - RenderTreeSliver - TreeSliverNode - TreeSliverController - TreeSliverStateMixin - TreeSliverIndentationType Fixes https://github.com/flutter/flutter/issues/114299 https://github.com/flutter/flutter/assets/16964204/3facd095-7262-4068-aa33-d713e2deca99 https://github.com/flutter/flutter/assets/16964204/f851ae30-8e71-45c7-82a4-9606986a5872
861 lines
29 KiB
Dart
861 lines
29 KiB
Dart
// Copyright 2014 The Flutter 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/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
List<TreeSliverNode<String>> _setUpNodes() {
|
|
return <TreeSliverNode<String>>[
|
|
TreeSliverNode<String>('First'),
|
|
TreeSliverNode<String>(
|
|
'Second',
|
|
children: <TreeSliverNode<String>>[
|
|
TreeSliverNode<String>(
|
|
'alpha',
|
|
children: <TreeSliverNode<String>>[
|
|
TreeSliverNode<String>('uno'),
|
|
TreeSliverNode<String>('dos'),
|
|
TreeSliverNode<String>('tres'),
|
|
],
|
|
),
|
|
TreeSliverNode<String>('beta'),
|
|
TreeSliverNode<String>('kappa'),
|
|
],
|
|
),
|
|
TreeSliverNode<String>(
|
|
'Third',
|
|
expanded: true,
|
|
children: <TreeSliverNode<String>>[
|
|
TreeSliverNode<String>('gamma'),
|
|
TreeSliverNode<String>('delta'),
|
|
TreeSliverNode<String>('epsilon'),
|
|
],
|
|
),
|
|
TreeSliverNode<String>('Fourth'),
|
|
];
|
|
}
|
|
|
|
List<TreeSliverNode<String>> treeNodes = _setUpNodes();
|
|
|
|
void main() {
|
|
testWidgets('asserts proper axis directions', (WidgetTester tester) async {
|
|
final List<Object?> exceptions = <Object?>[];
|
|
final FlutterExceptionHandler? oldHandler = FlutterError.onError;
|
|
FlutterError.onError = (FlutterErrorDetails details) {
|
|
exceptions.add(details.exception);
|
|
};
|
|
addTearDown(() {
|
|
FlutterError.onError = oldHandler;
|
|
});
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: CustomScrollView(
|
|
reverse: true,
|
|
slivers: <Widget>[
|
|
TreeSliver<String>(tree: treeNodes),
|
|
],
|
|
),
|
|
));
|
|
|
|
FlutterError.onError = oldHandler;
|
|
expect(exceptions.isNotEmpty, isTrue);
|
|
expect(
|
|
exceptions[0].toString(),
|
|
contains('TreeSliver is only supported in Viewports with an AxisDirection.down.'),
|
|
);
|
|
|
|
exceptions.clear();
|
|
await tester.pumpWidget(Container());
|
|
FlutterError.onError = (FlutterErrorDetails details) {
|
|
exceptions.add(details.exception);
|
|
};
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: CustomScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
reverse: true,
|
|
slivers: <Widget>[
|
|
TreeSliver<String>(tree: treeNodes),
|
|
],
|
|
),
|
|
));
|
|
|
|
FlutterError.onError = oldHandler;
|
|
expect(exceptions.isNotEmpty, isTrue);
|
|
expect(
|
|
exceptions[0].toString(),
|
|
contains('TreeSliver is only supported in Viewports with an AxisDirection.down.'),
|
|
);
|
|
|
|
exceptions.clear();
|
|
await tester.pumpWidget(Container());
|
|
FlutterError.onError = (FlutterErrorDetails details) {
|
|
exceptions.add(details.exception);
|
|
};
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: CustomScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
slivers: <Widget>[
|
|
TreeSliver<String>(tree: treeNodes),
|
|
],
|
|
),
|
|
));
|
|
|
|
FlutterError.onError = oldHandler;
|
|
expect(exceptions.isNotEmpty, isTrue);
|
|
expect(
|
|
exceptions[0].toString(),
|
|
contains('TreeSliver is only supported in Viewports with an AxisDirection.down.'),
|
|
);
|
|
});
|
|
|
|
testWidgets('Basic layout', (WidgetTester tester) async {
|
|
treeNodes = _setUpNodes();
|
|
// Default layout, custom indentation values, row extents.
|
|
TreeSliver<String> treeSliver = TreeSliver<String>(
|
|
tree: treeNodes,
|
|
);
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: CustomScrollView(
|
|
slivers: <Widget>[ treeSliver ],
|
|
),
|
|
));
|
|
await tester.pump();
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 88.0))
|
|
..paragraph(offset: const Offset(56.0, 128.0))
|
|
..paragraph(offset: const Offset(56.0, 168.0))
|
|
..paragraph(offset: const Offset(56.0, 208.0))
|
|
..paragraph(offset: const Offset(46.0, 248.0))
|
|
);
|
|
expect(find.text('First'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('First')),
|
|
const Rect.fromLTRB(46.0, 8.0, 286.0, 32.0),
|
|
);
|
|
expect(find.text('Second'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('Second')),
|
|
const Rect.fromLTRB(46.0, 48.0, 334.0, 72.0),
|
|
);
|
|
expect(find.text('Third'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('Third')),
|
|
const Rect.fromLTRB(46.0, 88.0, 286.0, 112.0),
|
|
);
|
|
expect(find.text('gamma'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('gamma')),
|
|
const Rect.fromLTRB(46.0, 128.0, 286.0, 152.0),
|
|
);
|
|
expect(find.text('delta'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('delta')),
|
|
const Rect.fromLTRB(46.0, 168.0, 286.0, 192.0),
|
|
);
|
|
expect(find.text('epsilon'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('epsilon')),
|
|
const Rect.fromLTRB(46.0, 208.0, 382.0, 232.0),
|
|
);
|
|
expect(find.text('Fourth'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('Fourth')),
|
|
const Rect.fromLTRB(46.0, 248.0, 334.0, 272.0),
|
|
);
|
|
|
|
treeSliver = TreeSliver<String>(
|
|
tree: treeNodes,
|
|
indentation: TreeSliverIndentationType.none,
|
|
);
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: CustomScrollView(
|
|
slivers: <Widget>[ treeSliver ],
|
|
),
|
|
));
|
|
await tester.pump();
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 88.0))
|
|
..paragraph(offset: const Offset(46.0, 128.0))
|
|
..paragraph(offset: const Offset(46.0, 168.0))
|
|
..paragraph(offset: const Offset(46.0, 208.0))
|
|
..paragraph(offset: const Offset(46.0, 248.0))
|
|
);
|
|
expect(find.text('First'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('First')),
|
|
const Rect.fromLTRB(46.0, 8.0, 286.0, 32.0),
|
|
);
|
|
expect(find.text('Second'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('Second')),
|
|
const Rect.fromLTRB(46.0, 48.0, 334.0, 72.0),
|
|
);
|
|
expect(find.text('Third'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('Third')),
|
|
const Rect.fromLTRB(46.0, 88.0, 286.0, 112.0),
|
|
);
|
|
expect(find.text('gamma'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('gamma')),
|
|
const Rect.fromLTRB(46.0, 128.0, 286.0, 152.0),
|
|
);
|
|
expect(find.text('delta'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('delta')),
|
|
const Rect.fromLTRB(46.0, 168.0, 286.0, 192.0),
|
|
);
|
|
expect(find.text('epsilon'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('epsilon')),
|
|
const Rect.fromLTRB(46.0, 208.0, 382.0, 232.0),
|
|
);
|
|
expect(find.text('Fourth'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('Fourth')),
|
|
const Rect.fromLTRB(46.0, 248.0, 334.0, 272.0),
|
|
);
|
|
|
|
treeSliver = TreeSliver<String>(
|
|
tree: treeNodes,
|
|
indentation: TreeSliverIndentationType.custom(50.0),
|
|
);
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: CustomScrollView(
|
|
slivers: <Widget>[ treeSliver ],
|
|
),
|
|
));
|
|
await tester.pump();
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 88.0))
|
|
..paragraph(offset: const Offset(96.0, 128.0))
|
|
..paragraph(offset: const Offset(96.0, 168.0))
|
|
..paragraph(offset: const Offset(96.0, 208.0))
|
|
..paragraph(offset: const Offset(46.0, 248.0))
|
|
);
|
|
expect(find.text('First'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('First')),
|
|
const Rect.fromLTRB(46.0, 8.0, 286.0, 32.0),
|
|
);
|
|
expect(find.text('Second'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('Second')),
|
|
const Rect.fromLTRB(46.0, 48.0, 334.0, 72.0),
|
|
);
|
|
expect(find.text('Third'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('Third')),
|
|
const Rect.fromLTRB(46.0, 88.0, 286.0, 112.0),
|
|
);
|
|
expect(find.text('gamma'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('gamma')),
|
|
const Rect.fromLTRB(46.0, 128.0, 286.0, 152.0),
|
|
);
|
|
expect(find.text('delta'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('delta')),
|
|
const Rect.fromLTRB(46.0, 168.0, 286.0, 192.0),
|
|
);
|
|
expect(find.text('epsilon'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('epsilon')),
|
|
const Rect.fromLTRB(46.0, 208.0, 382.0, 232.0),
|
|
);
|
|
expect(find.text('Fourth'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('Fourth')),
|
|
const Rect.fromLTRB(46.0, 248.0, 334.0, 272.0),
|
|
);
|
|
|
|
treeSliver = TreeSliver<String>(
|
|
tree: treeNodes,
|
|
treeRowExtentBuilder: (_, __) => 100,
|
|
);
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: CustomScrollView(
|
|
slivers: <Widget>[ treeSliver ],
|
|
),
|
|
));
|
|
await tester.pump();
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 26.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 126.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 226.0))
|
|
..paragraph(offset: const Offset(56.0, 326.0))
|
|
..paragraph(offset: const Offset(56.0, 426.0))
|
|
..paragraph(offset: const Offset(56.0, 526.0))
|
|
);
|
|
expect(find.text('First'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('First')),
|
|
const Rect.fromLTRB(46.0, 26.0, 286.0, 74.0),
|
|
);
|
|
expect(find.text('Second'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('Second')),
|
|
const Rect.fromLTRB(46.0, 126.0, 334.0, 174.0),
|
|
);
|
|
expect(find.text('Third'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('Third')),
|
|
const Rect.fromLTRB(46.0, 226.0, 286.0, 274.0),
|
|
);
|
|
expect(find.text('gamma'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('gamma')),
|
|
const Rect.fromLTRB(46.0, 326.0, 286.0, 374.0),
|
|
);
|
|
expect(find.text('delta'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('delta')),
|
|
const Rect.fromLTRB(46.0, 426.0, 286.0, 474.0),
|
|
);
|
|
expect(find.text('epsilon'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('epsilon')),
|
|
const Rect.fromLTRB(46.0, 526.0, 382.0, 574.0),
|
|
);
|
|
expect(find.text('Fourth'), findsNothing);
|
|
});
|
|
|
|
testWidgets('Animating node segment', (WidgetTester tester) async {
|
|
treeNodes = _setUpNodes();
|
|
TreeSliver<String> treeSliver = TreeSliver<String>(tree: treeNodes);
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: CustomScrollView(
|
|
slivers: <Widget>[ treeSliver ],
|
|
),
|
|
));
|
|
await tester.pump();
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 88.0))
|
|
..paragraph(offset: const Offset(56.0, 128.0))
|
|
..paragraph(offset: const Offset(56.0, 168.0))
|
|
..paragraph(offset: const Offset(56.0, 208.0))
|
|
..paragraph(offset: const Offset(46.0, 248.0))
|
|
);
|
|
expect(find.text('alpha'), findsNothing);
|
|
await tester.tap(find.byType(Icon).first);
|
|
await tester.pump();
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0))
|
|
..paragraph(offset: const Offset(56.0, 8.0)) // beta animating in
|
|
..paragraph(offset: const Offset(56.0, 48.0)) // kappa animating in
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 88.0))
|
|
..paragraph(offset: const Offset(56.0, 128.0))
|
|
..paragraph(offset: const Offset(56.0, 168.0))
|
|
..paragraph(offset: const Offset(56.0, 208.0))
|
|
..paragraph(offset: const Offset(46.0, 248.0))
|
|
);
|
|
// New nodes have been inserted into the tree, alpha
|
|
// is not visible yet.
|
|
expect(find.text('alpha'), findsNothing);
|
|
expect(find.text('beta'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('beta')),
|
|
const Rect.fromLTRB(46.0, 8.0, 238.0, 32.0),
|
|
);
|
|
expect(find.text('kappa'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('kappa')),
|
|
const Rect.fromLTRB(46.0, 48.0, 286.0, 72.0),
|
|
);
|
|
// Progress the animation.
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0))
|
|
..paragraph() // alpha icon
|
|
..paragraph(offset: const Offset(56.0, 8.0)) // alpha animating in
|
|
..paragraph(offset: const Offset(56.0, 48.0)) // beta animating in
|
|
..paragraph(offset: const Offset(56.0, 88.0)) // kappa animating in
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 128.0))
|
|
..paragraph(offset: const Offset(56.0, 168.0))
|
|
..paragraph(offset: const Offset(56.0, 208.0))
|
|
..paragraph(offset: const Offset(56.0, 248.0))
|
|
..paragraph(offset: const Offset(46.0, 288.0))
|
|
);
|
|
expect(
|
|
tester.getRect(find.text('alpha')).top.floor(),
|
|
8.0,
|
|
);
|
|
expect(find.text('beta'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('beta')).top.floor(),
|
|
48.0,
|
|
);
|
|
expect(find.text('kappa'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('kappa')).top.floor(),
|
|
88.0,
|
|
);
|
|
// Complete the animation
|
|
await tester.pumpAndSettle();
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0)) // First
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0)) // Second
|
|
..paragraph() // alpha icon
|
|
..paragraph(offset: const Offset(56.0, 88.0)) // alpha
|
|
..paragraph(offset: const Offset(56.0, 128.0)) // beta
|
|
..paragraph(offset: const Offset(56.0, 168.0)) // kappa
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 208.0)) // Third
|
|
..paragraph(offset: const Offset(56.0, 248.0)) // gamma
|
|
..paragraph(offset: const Offset(56.0, 288.0)) // delta
|
|
..paragraph(offset: const Offset(56.0, 328.0)) // epsilon
|
|
..paragraph(offset: const Offset(46.0, 368.0)) // Fourth
|
|
);
|
|
expect(find.text('alpha'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('alpha')),
|
|
const Rect.fromLTRB(46.0, 88.0, 286.0, 112.0),
|
|
);
|
|
expect(find.text('beta'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('beta')),
|
|
const Rect.fromLTRB(46.0, 128.0, 238.0, 152.0),
|
|
);
|
|
expect(find.text('kappa'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('kappa')),
|
|
const Rect.fromLTRB(46.0, 168.0, 286.0, 192.0),
|
|
);
|
|
|
|
// Customize the animation
|
|
treeSliver = TreeSliver<String>(
|
|
tree: treeNodes,
|
|
toggleAnimationStyle: AnimationStyle(
|
|
duration: const Duration(milliseconds: 500),
|
|
curve: Curves.bounceIn,
|
|
),
|
|
);
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: CustomScrollView(
|
|
slivers: <Widget>[ treeSliver ],
|
|
),
|
|
));
|
|
await tester.pump();
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0)) // First
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0)) // Second
|
|
..paragraph() // alpha icon
|
|
..paragraph(offset: const Offset(56.0, 88.0)) // alpha
|
|
..paragraph(offset: const Offset(56.0, 128.0)) // beta
|
|
..paragraph(offset: const Offset(56.0, 168.0)) // kappa
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 208.0)) // Third
|
|
..paragraph(offset: const Offset(56.0, 248.0)) // gamma
|
|
..paragraph(offset: const Offset(56.0, 288.0)) // delta
|
|
..paragraph(offset: const Offset(56.0, 328.0)) // epsilon
|
|
..paragraph(offset: const Offset(46.0, 368.0)) // Fourth
|
|
);
|
|
// Still visible from earlier.
|
|
expect(find.text('alpha'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('alpha')),
|
|
const Rect.fromLTRB(46.0, 88.0, 286.0, 112.0),
|
|
);
|
|
// Collapse the node now
|
|
await tester.tap(find.byType(Icon).first);
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 200));
|
|
expect(find.text('alpha'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('alpha')).top.floor(),
|
|
-22,
|
|
);
|
|
expect(find.text('beta'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('beta')).top.floor(),
|
|
18,
|
|
);
|
|
expect(find.text('kappa'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('kappa')).top.floor(),
|
|
58,
|
|
);
|
|
// Progress the animation.
|
|
await tester.pump(const Duration(milliseconds: 200));
|
|
expect(find.text('alpha'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('alpha')).top.floor(),
|
|
-25,
|
|
);
|
|
expect(find.text('beta'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('beta')).top.floor(),
|
|
15,
|
|
);
|
|
expect(find.text('kappa'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('kappa')).top.floor(),
|
|
55.0,
|
|
);
|
|
// Complete the animation
|
|
await tester.pumpAndSettle();
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 88.0))
|
|
..paragraph(offset: const Offset(56.0, 128.0))
|
|
..paragraph(offset: const Offset(56.0, 168.0))
|
|
..paragraph(offset: const Offset(56.0, 208.0))
|
|
..paragraph(offset: const Offset(46.0, 248.0))
|
|
);
|
|
expect(find.text('alpha'), findsNothing);
|
|
|
|
// Disable the animation
|
|
treeSliver = TreeSliver<String>(
|
|
tree: treeNodes,
|
|
toggleAnimationStyle: AnimationStyle.noAnimation,
|
|
);
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: CustomScrollView(
|
|
slivers: <Widget>[ treeSliver ],
|
|
),
|
|
));
|
|
await tester.pump();
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 88.0))
|
|
..paragraph(offset: const Offset(56.0, 128.0))
|
|
..paragraph(offset: const Offset(56.0, 168.0))
|
|
..paragraph(offset: const Offset(56.0, 208.0))
|
|
..paragraph(offset: const Offset(46.0, 248.0))
|
|
);
|
|
// Not in the tree.
|
|
expect(find.text('alpha'), findsNothing);
|
|
// Collapse the node now
|
|
await tester.tap(find.byType(Icon).first);
|
|
await tester.pump();
|
|
// No animating, straight to positions.
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0)) // First
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0)) // Second
|
|
..paragraph() // alpha icon
|
|
..paragraph(offset: const Offset(56.0, 88.0)) // alpha
|
|
..paragraph(offset: const Offset(56.0, 128.0)) // beta
|
|
..paragraph(offset: const Offset(56.0, 168.0)) // kappa
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 208.0)) // Third
|
|
..paragraph(offset: const Offset(56.0, 248.0)) // gamma
|
|
..paragraph(offset: const Offset(56.0, 288.0)) // delta
|
|
..paragraph(offset: const Offset(56.0, 328.0)) // epsilon
|
|
..paragraph(offset: const Offset(46.0, 368.0)) // Fourth
|
|
);
|
|
expect(find.text('alpha'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('alpha')),
|
|
const Rect.fromLTRB(46.0, 88.0, 286.0, 112.0),
|
|
);
|
|
expect(find.text('beta'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('beta')),
|
|
const Rect.fromLTRB(46.0, 128.0, 238.0, 152.0),
|
|
);
|
|
expect(find.text('kappa'), findsOneWidget);
|
|
expect(
|
|
tester.getRect(find.text('kappa')),
|
|
const Rect.fromLTRB(46.0, 168.0, 286.0, 192.0),
|
|
);
|
|
});
|
|
|
|
testWidgets('Multiple animating node segments', (WidgetTester tester) async {
|
|
treeNodes = _setUpNodes();
|
|
final TreeSliver<String> treeSliver = TreeSliver<String>(tree: treeNodes);
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: CustomScrollView(
|
|
slivers: <Widget>[ treeSliver ],
|
|
),
|
|
));
|
|
await tester.pump();
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 88.0))
|
|
..paragraph(offset: const Offset(56.0, 128.0))
|
|
..paragraph(offset: const Offset(56.0, 168.0))
|
|
..paragraph(offset: const Offset(56.0, 208.0))
|
|
..paragraph(offset: const Offset(46.0, 248.0))
|
|
);
|
|
expect(find.text('Second'), findsOneWidget);
|
|
expect(find.text('alpha'), findsNothing); // Second is collapsed
|
|
expect(find.text('Third'), findsOneWidget);
|
|
expect(find.text('gamma'), findsOneWidget); // Third is expanded
|
|
|
|
expect(
|
|
tester.getRect(find.text('Second')),
|
|
const Rect.fromLTRB(46.0, 48.0, 334.0, 72.0),
|
|
);
|
|
expect(
|
|
tester.getRect(find.text('Third')),
|
|
const Rect.fromLTRB(46.0, 88.0, 286.0, 112.0),
|
|
);
|
|
expect(
|
|
tester.getRect(find.text('gamma')),
|
|
const Rect.fromLTRB(46.0, 128.0, 286.0, 152.0),
|
|
);
|
|
|
|
// Trigger two animations to run together.
|
|
// Collapse Third
|
|
await tester.tap(find.byType(Icon).last);
|
|
// Expand Second
|
|
await tester.tap(find.byType(Icon).first);
|
|
await tester.pump(const Duration(milliseconds: 15));
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0))
|
|
..paragraph(offset: const Offset(56.0, 8.0)) // beta entering
|
|
..paragraph(offset: const Offset(56.0, 48.0)) // kappa entering
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 88.0))
|
|
..paragraph(offset: const Offset(56.0, 128.0))
|
|
..paragraph(offset: const Offset(56.0, 168.0))
|
|
..paragraph(offset: const Offset(56.0, 208.0))
|
|
..paragraph(offset: const Offset(46.0, 248.0))
|
|
);
|
|
// Third is collapsing
|
|
expect(
|
|
tester.getRect(find.text('Third')),
|
|
const Rect.fromLTRB(46.0, 88.0, 286.0, 112.0),
|
|
);
|
|
expect(
|
|
tester.getRect(find.text('gamma')),
|
|
const Rect.fromLTRB(46.0, 128.0, 286.0, 152.0),
|
|
);
|
|
// Second is expanding
|
|
expect(
|
|
tester.getRect(find.text('Second')),
|
|
const Rect.fromLTRB(46.0, 48.0, 334.0, 72.0),
|
|
);
|
|
// beta has been added and is animating into view.
|
|
expect(
|
|
tester.getRect(find.text('beta')).top.floor(),
|
|
8.0,
|
|
);
|
|
await tester.pump(const Duration(milliseconds: 15));
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0))
|
|
..paragraph() // alpha icon animating
|
|
..paragraph(offset: const Offset(56.0, -20.0)) // alpha naimating
|
|
..paragraph(offset: const Offset(56.0, 20.0)) // beta
|
|
..paragraph(offset: const Offset(56.0, 60.0)) // kappa
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 100.0)) // Third
|
|
// Children of Third are animating, but the expand and
|
|
// collapse counter each other, so their position is unchanged.
|
|
..paragraph(offset: const Offset(56.0, 128.0))
|
|
..paragraph(offset: const Offset(56.0, 168.0))
|
|
..paragraph(offset: const Offset(56.0, 208.0))
|
|
..paragraph(offset: const Offset(46.0, 248.0))
|
|
);
|
|
// Third is still collapsing. Third is sliding down
|
|
// as Seconds's children slide in, gamma is still exiting.
|
|
expect(
|
|
tester.getRect(find.text('Third')).top.floor(),
|
|
100.0,
|
|
);
|
|
// gamma appears to not have moved, this is because it is
|
|
// intersecting both animations, the positive offset of
|
|
// Second animation == the negative offset of Third
|
|
expect(
|
|
tester.getRect(find.text('gamma')),
|
|
const Rect.fromLTRB(46.0, 128.0, 286.0, 152.0),
|
|
);
|
|
// Second is still expanding
|
|
expect(
|
|
tester.getRect(find.text('Second')),
|
|
const Rect.fromLTRB(46.0, 48.0, 334.0, 72.0),
|
|
);
|
|
// alpha is still animating into view.
|
|
expect(
|
|
tester.getRect(find.text('alpha')).top.floor(),
|
|
-20.0,
|
|
);
|
|
// Progress the animation further
|
|
await tester.pump(const Duration(milliseconds: 15));
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0))
|
|
..paragraph() // alpha icon animating
|
|
..paragraph(offset: const Offset(56.0, -8.0)) // alpha animating
|
|
..paragraph(offset: const Offset(56.0, 32.0)) // beta
|
|
..paragraph(offset: const Offset(56.0, 72.0)) // kappa
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 112.0)) // Third
|
|
// Children of Third are animating, but the expand and
|
|
// collapse counter each other, so their position is unchanged.
|
|
..paragraph(offset: const Offset(56.0, 128.0))
|
|
..paragraph(offset: const Offset(56.0, 168.0))
|
|
..paragraph(offset: const Offset(56.0, 208.0))
|
|
..paragraph(offset: const Offset(46.0, 248.0))
|
|
);
|
|
// Third is still collapsing. Third is sliding down
|
|
// as Seconds's children slide in, gamma is still exiting.
|
|
expect(
|
|
tester.getRect(find.text('Third')).top.floor(),
|
|
112.0,
|
|
);
|
|
// gamma appears to not have moved, this is because it is
|
|
// intersecting both animations, the positive offset of
|
|
// Second animation == the negative offset of Third
|
|
expect(
|
|
tester.getRect(find.text('gamma')),
|
|
const Rect.fromLTRB(46.0, 128.0, 286.0, 152.0),
|
|
);
|
|
// Second is still expanding
|
|
expect(
|
|
tester.getRect(find.text('Second')),
|
|
const Rect.fromLTRB(46.0, 48.0, 334.0, 72.0),
|
|
);
|
|
// alpha is still animating into view.
|
|
expect(
|
|
tester.getRect(find.text('alpha')).top.floor(),
|
|
-8.0,
|
|
);
|
|
// Complete the animations
|
|
await tester.pumpAndSettle();
|
|
expect(
|
|
find.byType(TreeSliver<String>),
|
|
paints
|
|
..paragraph(offset: const Offset(46.0, 8.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 48.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(56.0, 88.0))
|
|
..paragraph(offset: const Offset(56.0, 128.0))
|
|
..paragraph(offset: const Offset(56.0, 168.0))
|
|
..paragraph() // Icon
|
|
..paragraph(offset: const Offset(46.0, 208.0))
|
|
..paragraph(offset: const Offset(46.0, 248.0))
|
|
);
|
|
expect(
|
|
tester.getRect(find.text('Third')),
|
|
const Rect.fromLTRB(46.0, 208.0, 286.0, 232.0),
|
|
);
|
|
// gamma has left the building
|
|
expect(find.text('gamma'), findsNothing);
|
|
expect(
|
|
tester.getRect(find.text('Second')),
|
|
const Rect.fromLTRB(46.0, 48.0, 334.0, 72.0),
|
|
);
|
|
// alpha is in place.
|
|
expect(
|
|
tester.getRect(find.text('alpha')),
|
|
const Rect.fromLTRB(46.0, 88.0, 286.0, 112.0),
|
|
);
|
|
});
|
|
|
|
testWidgets('only paints visible rows', (WidgetTester tester) async {
|
|
treeNodes = _setUpNodes();
|
|
final ScrollController scrollController = ScrollController();
|
|
addTearDown(scrollController.dispose);
|
|
treeNodes = _setUpNodes();
|
|
final TreeSliver<String> treeSliver = TreeSliver<String>(
|
|
treeRowExtentBuilder: (_, __) => 200,
|
|
tree: treeNodes,
|
|
);
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: CustomScrollView(
|
|
controller: scrollController,
|
|
slivers: <Widget>[ treeSliver ],
|
|
),
|
|
));
|
|
await tester.pump();
|
|
expect(scrollController.position.pixels, 0.0);
|
|
expect(scrollController.position.maxScrollExtent, 800.0);
|
|
bool rowNeedsPaint(String row) {
|
|
return find.text(row).evaluate().first.renderObject!.debugNeedsPaint;
|
|
}
|
|
|
|
expect(rowNeedsPaint('First'), isFalse);
|
|
expect(rowNeedsPaint('Second'), isFalse);
|
|
expect(rowNeedsPaint('Third'), isFalse);
|
|
expect(find.text('gamma'), findsNothing); // Not visible
|
|
|
|
// Change the scroll offset
|
|
scrollController.jumpTo(200);
|
|
await tester.pump();
|
|
expect(find.text('First'), findsNothing);
|
|
expect(rowNeedsPaint('Second'), isFalse);
|
|
expect(rowNeedsPaint('Third'), isFalse);
|
|
expect(rowNeedsPaint('gamma'), isFalse); // Now visible
|
|
});
|
|
}
|