Unverified Commit f3c742c8 authored by Hans Muller's avatar Hans Muller Committed by GitHub

Added BottomAppBar docked FloatingActionButtonLocations (#16167)

* Added BottomAppBar docked FloationActionButtonLocations

* Moved the startTop FloatingActionButtonLocation to the demo

* fixed a typo
parent a80d557b
......@@ -19,9 +19,9 @@ class FabMotionDemo extends StatefulWidget {
class _FabMotionDemoState extends State<FabMotionDemo> {
static const List<FloatingActionButtonLocation> _floatingActionButtonLocations = const <FloatingActionButtonLocation>[
FloatingActionButtonLocation.endFloat,
FloatingActionButtonLocation.endFloat,
FloatingActionButtonLocation.centerFloat,
const _TopStartFloatingActionButtonLocation(),
const _StartTopFloatingActionButtonLocation(),
];
bool _showFab = true;
......@@ -29,25 +29,25 @@ class _FabMotionDemoState extends State<FabMotionDemo> {
@override
Widget build(BuildContext context) {
final Widget floatingActionButton = _showFab
final Widget floatingActionButton = _showFab
? new Builder(builder: (BuildContext context) {
// We use a widget builder here so that this inner context can find the Scaffold.
// This makes it possible to show the snackbar.
return new FloatingActionButton(
backgroundColor: Colors.yellow.shade900,
onPressed: () => _showSnackbar(context),
child: const Icon(Icons.add),
child: const Icon(Icons.add),
);
})
})
: null;
return new Scaffold(
appBar: new AppBar(
title: const Text('FAB Location'),
title: const Text('FAB Location'),
// Add 48dp of space onto the bottom of the appbar.
// This gives space for the top-start location to attach to without
// blocking the 'back' button.
bottom: const PreferredSize(
preferredSize: const Size.fromHeight(48.0),
preferredSize: const Size.fromHeight(48.0),
child: const SizedBox(),
),
),
......@@ -93,8 +93,8 @@ class _FabMotionDemoState extends State<FabMotionDemo> {
// Places the Floating Action Button at the top of the content area of the
// app, on the border between the body and the app bar.
class _TopStartFloatingActionButtonLocation extends FloatingActionButtonLocation {
const _TopStartFloatingActionButtonLocation();
class _StartTopFloatingActionButtonLocation extends FloatingActionButtonLocation {
const _StartTopFloatingActionButtonLocation();
@override
Offset getOffset(ScaffoldPrelayoutGeometry scaffoldGeometry) {
......@@ -130,7 +130,7 @@ class _TopStartFloatingActionButtonLocation extends FloatingActionButtonLocation
fabX = startPadding;
break;
}
// Finally, we'll place the Y coordinate for the Floating Action Button
// Finally, we'll place the Y coordinate for the Floating Action Button
// at the top of the content body.
//
// We want to place the middle of the Floating Action Button on the
......
......@@ -7,53 +7,31 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('Floating action button positioner', () {
Widget build(FloatingActionButton fab, FloatingActionButtonLocation fabLocation, [_GeometryListener listener]) {
return new Directionality(
textDirection: TextDirection.ltr,
child: new MediaQuery(
data: const MediaQueryData(
viewInsets: const EdgeInsets.only(bottom: 200.0),
),
child: new Scaffold(
appBar: new AppBar(title: const Text('FabLocation Test')),
floatingActionButtonLocation: fabLocation,
floatingActionButton: fab,
body: listener,
),
),
);
}
const FloatingActionButton fab1 = const FloatingActionButton(
onPressed: null,
child: const Text('1'),
);
group('Basic floating action button locations', () {
testWidgets('still animates motion when the floating action button is null', (WidgetTester tester) async {
await tester.pumpWidget(build(null, null));
await tester.pumpWidget(buildFrame(fab: null, location: null));
expect(find.byType(FloatingActionButton), findsNothing);
expect(tester.binding.transientCallbackCount, 0);
await tester.pumpWidget(build(null, FloatingActionButtonLocation.endFloat));
await tester.pumpWidget(buildFrame(fab: null, location: FloatingActionButtonLocation.endFloat));
expect(find.byType(FloatingActionButton), findsNothing);
expect(tester.binding.transientCallbackCount, greaterThan(0));
await tester.pumpWidget(build(null, FloatingActionButtonLocation.centerFloat));
await tester.pumpWidget(buildFrame(fab: null, location: FloatingActionButtonLocation.centerFloat));
expect(find.byType(FloatingActionButton), findsNothing);
expect(tester.binding.transientCallbackCount, greaterThan(0));
});
testWidgets('moves fab from center to end and back', (WidgetTester tester) async {
await tester.pumpWidget(build(fab1, FloatingActionButtonLocation.endFloat));
await tester.pumpWidget(buildFrame(location: FloatingActionButtonLocation.endFloat));
expect(tester.getCenter(find.byType(FloatingActionButton)), const Offset(756.0, 356.0));
expect(tester.binding.transientCallbackCount, 0);
await tester.pumpWidget(build(fab1, FloatingActionButtonLocation.centerFloat));
await tester.pumpWidget(buildFrame(location: FloatingActionButtonLocation.centerFloat));
expect(tester.binding.transientCallbackCount, greaterThan(0));
......@@ -62,8 +40,8 @@ void main() {
expect(tester.getCenter(find.byType(FloatingActionButton)), const Offset(400.0, 356.0));
expect(tester.binding.transientCallbackCount, 0);
await tester.pumpWidget(build(fab1, FloatingActionButtonLocation.endFloat));
await tester.pumpWidget(buildFrame(location: FloatingActionButtonLocation.endFloat));
expect(tester.binding.transientCallbackCount, greaterThan(0));
await tester.pumpAndSettle();
......@@ -73,11 +51,11 @@ void main() {
});
testWidgets('moves to and from custom-defined positions', (WidgetTester tester) async {
await tester.pumpWidget(build(fab1, _kTopStartFabLocation));
await tester.pumpWidget(buildFrame(location: const _StartTopFloatingActionButtonLocation()));
expect(tester.getCenter(find.byType(FloatingActionButton)), const Offset(44.0, 56.0));
await tester.pumpWidget(build(fab1, FloatingActionButtonLocation.centerFloat));
await tester.pumpWidget(buildFrame(location: FloatingActionButtonLocation.centerFloat));
expect(tester.binding.transientCallbackCount, greaterThan(0));
await tester.pumpAndSettle();
......@@ -85,8 +63,8 @@ void main() {
expect(tester.getCenter(find.byType(FloatingActionButton)), const Offset(400.0, 356.0));
expect(tester.binding.transientCallbackCount, 0);
await tester.pumpWidget(build(fab1, _kTopStartFabLocation));
await tester.pumpWidget(buildFrame(location: const _StartTopFloatingActionButtonLocation()));
expect(tester.binding.transientCallbackCount, greaterThan(0));
await tester.pumpAndSettle();
......@@ -122,23 +100,76 @@ void main() {
// We'll listen to the Scaffold's geometry for any 'jumps' to a size of 1 to detect changes in the size and rotation of the fab.
// Creating a scaffold with the fab at endFloat
await tester.pumpWidget(build(fab1, FloatingActionButtonLocation.endFloat, geometryListener));
await tester.pumpWidget(buildFrame(location: FloatingActionButtonLocation.endFloat, listener: geometryListener));
listenerState = tester.state(find.byType(_GeometryListener));
listenerState.geometryListenable.addListener(check);
// Moving the fab to centerFloat'
await tester.pumpWidget(build(fab1, FloatingActionButtonLocation.centerFloat, geometryListener));
await tester.pumpWidget(buildFrame(location: FloatingActionButtonLocation.centerFloat, listener: geometryListener));
await tester.pumpAndSettle();
// Moving the fab to the top start after finishing the previous motion
await tester.pumpWidget(build(fab1, _kTopStartFabLocation, geometryListener));
await tester.pumpWidget(buildFrame(location: const _StartTopFloatingActionButtonLocation(), listener: geometryListener));
// Interrupting motion to move to the end float
await tester.pumpWidget(build(fab1, FloatingActionButtonLocation.endFloat, geometryListener));
await tester.pumpWidget(buildFrame(location: FloatingActionButtonLocation.endFloat, listener: geometryListener));
await tester.pumpAndSettle();
});
});
testWidgets('Docked floating action button locations', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
location: FloatingActionButtonLocation.endDocked,
bab: const SizedBox(height: 100.0),
viewInsets: EdgeInsets.zero,
),
);
// Scaffold 800x600, FAB is 56x56, BAB is 800x100, FAB's center is
// at the top of the BAB.
expect(tester.getCenter(find.byType(FloatingActionButton)), const Offset(756.0, 500.0));
await tester.pumpWidget(
buildFrame(
location: FloatingActionButtonLocation.centerDocked,
bab: const SizedBox(height: 100.0),
viewInsets: EdgeInsets.zero,
),
);
await tester.pumpAndSettle();
expect(tester.getCenter(find.byType(FloatingActionButton)), const Offset(400.0, 500.0));
await tester.pumpWidget(
buildFrame(
location: FloatingActionButtonLocation.endDocked,
bab: const SizedBox(height: 100.0),
viewInsets: EdgeInsets.zero,
),
);
await tester.pumpAndSettle();
expect(tester.getCenter(find.byType(FloatingActionButton)), const Offset(756.0, 500.0));
});
testWidgets('Docked floating action button locations: no BAB, small BAB', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
location: FloatingActionButtonLocation.endDocked,
viewInsets: EdgeInsets.zero,
),
);
expect(tester.getCenter(find.byType(FloatingActionButton)), const Offset(756.0, 572.0));
await tester.pumpWidget(
buildFrame(
location: FloatingActionButtonLocation.endDocked,
bab: const SizedBox(height: 16.0),
viewInsets: EdgeInsets.zero,
),
);
expect(tester.getCenter(find.byType(FloatingActionButton)), const Offset(756.0, 572.0));
});
}
......@@ -169,7 +200,7 @@ class _GeometryListenerState extends State<_GeometryListener> {
if (geometryListenable != null)
geometryListenable.removeListener(onGeometryChanged);
geometryListenable = newListenable;
geometryListenable.addListener(onGeometryChanged);
cache = new _GeometryCachePainter(geometryListenable);
......@@ -201,15 +232,50 @@ class _GeometryCachePainter extends CustomPainter {
}
}
const _TopStartFabLocation _kTopStartFabLocation = const _TopStartFabLocation();
Widget buildFrame({
FloatingActionButton fab: const FloatingActionButton(
onPressed: null,
child: const Text('1'),
),
FloatingActionButtonLocation location,
_GeometryListener listener,
TextDirection textDirection: TextDirection.ltr,
EdgeInsets viewInsets: const EdgeInsets.only(bottom: 200.0),
Widget bab,
}) {
return new Directionality(
textDirection: textDirection,
child: new MediaQuery(
data: new MediaQueryData(viewInsets: viewInsets),
child: new Scaffold(
appBar: new AppBar(title: const Text('FabLocation Test')),
floatingActionButtonLocation: location,
floatingActionButton: fab,
bottomNavigationBar: bab,
body: listener,
),
),
);
}
class _TopStartFabLocation extends FloatingActionButtonLocation {
const _TopStartFabLocation();
class _StartTopFloatingActionButtonLocation extends FloatingActionButtonLocation {
const _StartTopFloatingActionButtonLocation();
@override
Offset getOffset(ScaffoldPrelayoutGeometry scaffoldGeometry) {
final double fabX = 16.0 + scaffoldGeometry.minInsets.left;
double fabX;
assert(scaffoldGeometry.textDirection != null);
switch (scaffoldGeometry.textDirection) {
case TextDirection.rtl:
final double startPadding = kFloatingActionButtonMargin + scaffoldGeometry.minInsets.right;
fabX = scaffoldGeometry.scaffoldSize.width - scaffoldGeometry.floatingActionButtonSize.width - startPadding;
break;
case TextDirection.ltr:
final double startPadding = kFloatingActionButtonMargin + scaffoldGeometry.minInsets.left;
fabX = startPadding;
break;
}
final double fabY = scaffoldGeometry.contentTop - (scaffoldGeometry.floatingActionButtonSize.height / 2.0);
return new Offset(fabX, fabY);
}
}
\ No newline at end of file
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment