flutter/examples/flutter_gallery/lib/demo/material/fab_motion_demo.dart
David Shuckerow dd0acea1ec
Add support for placing the FAB in different positions (#14368)
* Add support to move the fab between positions.

* Motion demo for the FAB works between center and end floating.

* Add a Material curve to the offset animation.

* Move the fab position into an object

* Updates to docs

* Updates to docs

* Fix a lint on the bottom sheet type

* Add a ScaffoldGeometry class

* Improve the documentation

* Improve the documentation

* Add a fab motion animator

* Add position and scale animations

* FAB entrance and motion animations work

* Get started on FAB motion

* Make fab animation work properly.

* Change the fab animator to be stored in the state of the scaffold.

* Add a layout test

* Fix spacing being off

* Fix the entrance/exit animation test.

* Add a textDirection to the layout delegate.

* Fix const constructor lint checks

* Add toStrings for the fab positioner/animator

* Add a toString for CurveTween

* Change the fab motion demo icon to a simple add icon.

* Add tests and a custom fab positioner to the demo.

* Do not start the fab's motion animation when the fab is null.

* Adjust the code to pass the new tests.

* Rename for in response to Hans' comment.

* Revert the tabs fab demo

* Use timeDilation, and clean up the animation code a little.

* Clean up the prelayout geometry docs and ctr order

* Cleanup fab transition widget code

* Clean up comments on Scaffold, add cross-references between the two geometries

* Explain the fab motion animation scheduling better

* Add a const to the fab motion demo

* Make the fab animation never jank by keeping track of where to move the fab to in the future.

* Add a default fab positioner constant

* Add space after comma in the demo

* Add boilerplate dartdoc to all const constructors

* Comment improvement

* Rename 'fabSize' to 'floatingActionButtonSize'

* Rename 'fabSize' to 'floatingActionButtonSize'

* Rename 'fabSize' to 'floatingActionButtonSize'

* Clean up the prelayout geometry object's dartdoc

* Clean up the prelayout geometry object's dartdoc

* Remove extraneous comment

* Change possessive uses of Scaffold's to use dartdoc-compatible [Scaffold]'s

* Rename the horizontalFabPadding to an expansion

* Clean up controller cleanup and setState usage

* Animate instead of lerp

* Make the fab position animation use offsets instead of animations

* Streamline the fab motion demo

* Set up the animator to start from a reasonable place when interrupting animations.

* Doc cleanup on the new animation interruption

* Expand some uses of fab and clean up constants

* Expand remaining public uses of fab to floating action button

* Expand remaining public uses of fab to floating action button

* Expand on the documentation for the fab positioner and animator

* Refactor animations to broadcast the position properly.

* Add the ability to turn on and off the fab to the motion demo.

* Remove unused code

* Change the fab animator to animate even when the fab is exitting.

* Remove extra positioner.

* Apps -> Applications in docs

* Explain the scale animation.

* Name the child parameter in the animated builder

* RTL before LTR

* Wrap the AppBar in the example code

* const the fab motion demo name

* Start a test against animation jumps

* Test for jumps in the fab motion animation

* Dont initialize values to null

* Use constants, fix spacing from some of Hans' comments

* Clarify the relationship between fab positioners and prelayout geometries

* Explain the fab animmator a bit better

* Explain the animation progress in the fab animation

* Explain the animation restart better

* Explain the animation restart better

* Explain the prelayout geometry better

* Explain that height is a vertical distance.

* Explain the horizontal fab padding

* Update the scaffold size description to explain what happens when a wild keyboard appears

* Remove print statements

* Update the scaffold geometry with information about it being available at paint time.

* In one step of a transition

* Explain how the top-start fab positioner works

* Explain how the top-start fab positioner works

* Refactor the scaffold layout to just pass a padding instead of a bottom, top, start and end.

* Refactor the scaffold layout to just pass a padding instead of a bottom, top, start and end.

* Action buttons with with custom positioners.

* Add a rotation animation example.

* Use a swap animation to show swapping between two different animations.

* Use a swap animation to show swapping between two different animations.

* Add an example for the size animations.

* 2018 copyright

* Extra empty line

* Return new Scaffold

* Extra blank line fix

* All its contents have been laid out

* Position the fab

* Explain what the scaffold geometry is for.

* Move asserts to different lines

* The scaffoldsize will not

* Initial rename of FabPositioners to FloatingActionButtonLocation

* Rename comments in example to refer to location instead of positioner.

* Rename fabpositioner to location in tests and in the scaffold field

* Finish removing references to positioner in scaffold code.

* Split the fab location and animation out into a separate file.

* Make things more private

* Import foundation instead of meta

* Const curve instead of final.
2018-03-15 17:04:24 -07:00

148 lines
5.8 KiB
Dart

// Copyright 2018 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.
import 'package:flutter/material.dart';
const String _explanatoryText =
"When the Scaffold's floating action button location changes, "
'the floating action button animates to its new position';
class FabMotionDemo extends StatefulWidget {
static const String routeName = '/material/fab-motion';
@override
_FabMotionDemoState createState() {
return new _FabMotionDemoState();
}
}
class _FabMotionDemoState extends State<FabMotionDemo> {
static const List<FloatingActionButtonLocation> _floatingActionButtonLocations = const <FloatingActionButtonLocation>[
FloatingActionButtonLocation.endFloat,
FloatingActionButtonLocation.centerFloat,
const _TopStartFloatingActionButtonLocation(),
];
bool _showFab = true;
FloatingActionButtonLocation _floatingActionButtonLocation = FloatingActionButtonLocation.endFloat;
@override
Widget build(BuildContext context) {
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),
);
})
: null;
return new Scaffold(
appBar: new AppBar(
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),
child: const SizedBox(),
),
),
floatingActionButtonLocation: _floatingActionButtonLocation,
floatingActionButton: floatingActionButton,
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new RaisedButton(
onPressed: _moveFab,
child: const Text('MOVE FAB'),
),
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Toggle FAB'),
new Switch(value: _showFab, onChanged: _toggleFab),
],
),
],
),
),
);
}
void _moveFab() {
setState(() {
_floatingActionButtonLocation = _floatingActionButtonLocations[(_floatingActionButtonLocations.indexOf(_floatingActionButtonLocation) + 1) % _floatingActionButtonLocations.length];
});
}
void _toggleFab(bool showFab) {
setState(() {
_showFab = showFab;
});
}
void _showSnackbar(BuildContext context) {
Scaffold.of(context).showSnackBar(const SnackBar(content: const Text(_explanatoryText)));
}
}
// 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();
@override
Offset getOffset(ScaffoldPrelayoutGeometry scaffoldGeometry) {
// First, we'll place the X coordinate for the Floating Action Button
// at the start of the screen, based on the text direction.
double fabX;
assert(scaffoldGeometry.textDirection != null);
switch (scaffoldGeometry.textDirection) {
case TextDirection.rtl:
// In RTL layouts, the start of the screen is on the right side,
// and the end of the screen is on the left.
//
// We need to align the right edge of the floating action button with
// the right edge of the screen, then move it inwards by the designated padding.
//
// The Scaffold's origin is at its top-left, so we need to offset fabX
// by the Scaffold's width to get the right edge of the screen.
//
// The Floating Action Button's origin is at its top-left, so we also need
// to subtract the Floating Action Button's width to align the right edge
// of the Floating Action Button instead of the left edge.
final double startPadding = kFloatingActionButtonMargin + scaffoldGeometry.minInsets.right;
fabX = scaffoldGeometry.scaffoldSize.width - scaffoldGeometry.floatingActionButtonSize.width - startPadding;
break;
case TextDirection.ltr:
// In LTR layouts, the start of the screen is on the left side,
// and the end of the screen is on the right.
//
// Placing the fabX at 0.0 will align the left edge of the
// Floating Action Button with the left edge of the screen, so all
// we need to do is offset fabX by the designated padding.
final double startPadding = kFloatingActionButtonMargin + scaffoldGeometry.minInsets.left;
fabX = startPadding;
break;
}
// 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
// border between the Scaffold's app bar and its body. To do this,
// we place fabY at the scaffold geometry's contentTop, then subtract
// half of the Floating Action Button's height to place the center
// over the contentTop.
//
// We don't have to worry about which way is the top like we did
// for left and right, so we place fabY in this one-liner.
final double fabY = scaffoldGeometry.contentTop - (scaffoldGeometry.floatingActionButtonSize.height / 2.0);
return new Offset(fabX, fabY);
}
}