add physics to TabBarView (#11150)
This commit is contained in:
parent
744921fa02
commit
898c19d753
@ -795,6 +795,7 @@ class TabBarView extends StatefulWidget {
|
|||||||
Key key,
|
Key key,
|
||||||
@required this.children,
|
@required this.children,
|
||||||
this.controller,
|
this.controller,
|
||||||
|
this.physics,
|
||||||
}) : assert(children != null), super(key: key);
|
}) : assert(children != null), super(key: key);
|
||||||
|
|
||||||
/// This widget's selection and animation state.
|
/// This widget's selection and animation state.
|
||||||
@ -806,6 +807,17 @@ class TabBarView extends StatefulWidget {
|
|||||||
/// One widget per tab.
|
/// One widget per tab.
|
||||||
final List<Widget> children;
|
final List<Widget> children;
|
||||||
|
|
||||||
|
/// How the page view should respond to user input.
|
||||||
|
///
|
||||||
|
/// For example, determines how the page view continues to animate after the
|
||||||
|
/// user stops dragging the page view.
|
||||||
|
///
|
||||||
|
/// The physics are modified to snap to page boundaries using
|
||||||
|
/// [PageScrollPhysics] prior to being used.
|
||||||
|
///
|
||||||
|
/// Defaults to matching platform conventions.
|
||||||
|
final ScrollPhysics physics;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_TabBarViewState createState() => new _TabBarViewState();
|
_TabBarViewState createState() => new _TabBarViewState();
|
||||||
}
|
}
|
||||||
@ -954,7 +966,7 @@ class _TabBarViewState extends State<TabBarView> {
|
|||||||
onNotification: _handleScrollNotification,
|
onNotification: _handleScrollNotification,
|
||||||
child: new PageView(
|
child: new PageView(
|
||||||
controller: _pageController,
|
controller: _pageController,
|
||||||
physics: _kTabBarViewPhysics,
|
physics: widget.physics == null ? _kTabBarViewPhysics : _kTabBarViewPhysics.applyTo(widget.physics),
|
||||||
children: _children,
|
children: _children,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -7,6 +7,7 @@ import 'dart:ui' show SemanticsFlags, SemanticsAction;
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter/physics.dart';
|
||||||
|
|
||||||
import '../rendering/mock_canvas.dart';
|
import '../rendering/mock_canvas.dart';
|
||||||
import '../rendering/recording_canvas.dart';
|
import '../rendering/recording_canvas.dart';
|
||||||
@ -128,6 +129,24 @@ class TabIndicatorRecordingCanvas extends TestRecordingCanvas {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TestScrollPhysics extends ScrollPhysics {
|
||||||
|
const TestScrollPhysics({ ScrollPhysics parent }) : super(parent: parent);
|
||||||
|
|
||||||
|
@override
|
||||||
|
TestScrollPhysics applyTo(ScrollPhysics ancestor) {
|
||||||
|
return new TestScrollPhysics(parent: buildParent(ancestor));
|
||||||
|
}
|
||||||
|
|
||||||
|
static final SpringDescription _kDefaultSpring = new SpringDescription.withDampingRatio(
|
||||||
|
mass: 0.5,
|
||||||
|
springConstant: 500.0,
|
||||||
|
ratio: 1.1,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
SpringDescription get spring => _kDefaultSpring;
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('TabBar tap selects tab', (WidgetTester tester) async {
|
testWidgets('TabBar tap selects tab', (WidgetTester tester) async {
|
||||||
final List<String> tabs = <String>['A', 'B', 'C'];
|
final List<String> tabs = <String>['A', 'B', 'C'];
|
||||||
@ -812,6 +831,53 @@ void main() {
|
|||||||
expect(tabController.index, 2);
|
expect(tabController.index, 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('TabBarView scrolls end very close to a new page with custom physics', (WidgetTester tester) async {
|
||||||
|
final TabController tabController = new TabController(
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
initialIndex: 1,
|
||||||
|
length: 3,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new SizedBox.expand(
|
||||||
|
child: new Center(
|
||||||
|
child: new SizedBox(
|
||||||
|
width: 400.0,
|
||||||
|
height: 400.0,
|
||||||
|
child: new TabBarView(
|
||||||
|
controller: tabController,
|
||||||
|
physics: const TestScrollPhysics(),
|
||||||
|
children: <Widget>[
|
||||||
|
const Center(child: const Text('0')),
|
||||||
|
const Center(child: const Text('1')),
|
||||||
|
const Center(child: const Text('2')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(tabController.index, 1);
|
||||||
|
|
||||||
|
final PageView pageView = tester.widget(find.byType(PageView));
|
||||||
|
final PageController pageController = pageView.controller;
|
||||||
|
final ScrollPosition position = pageController.position;
|
||||||
|
|
||||||
|
// The TabBarView's page width is 400, so page 0 is at scroll offset 0.0,
|
||||||
|
// page 1 is at 400.0, page 2 is at 800.0.
|
||||||
|
|
||||||
|
expect(position.pixels, 400.0);
|
||||||
|
|
||||||
|
// Not close enough to switch to page 2
|
||||||
|
pageController.jumpTo(800.0 - 1.25 * position.physics.tolerance.distance);
|
||||||
|
expect(tabController.index, 1);
|
||||||
|
|
||||||
|
// Close enough to switch to page 2
|
||||||
|
pageController.jumpTo(800.0 - 0.75 * position.physics.tolerance.distance);
|
||||||
|
expect(tabController.index, 2);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('Scrollable TabBar with a non-zero TabController initialIndex', (WidgetTester tester) async {
|
testWidgets('Scrollable TabBar with a non-zero TabController initialIndex', (WidgetTester tester) async {
|
||||||
// This is a regression test for https://github.com/flutter/flutter/issues/9374
|
// This is a regression test for https://github.com/flutter/flutter/issues/9374
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user