Implement Backdrop with Flutter

I introduce how to implement simple Backdrop of material design with flutter. I refer to Flutter’s gallery app for the many parts.

Final product

Step1

First, create a base. Define animation controller to control animations. value:1.0 represents the initial state. Default is 0.0 if you do not specify anything, but I want to open the panel in the initial state, so set to 1.0.

class _BackdropPageState extends State<Step1Page>
with SingleTickerProviderStateMixin {
AnimationController _controller;
@override
void initState() {
super.initState();
_controller = new AnimationController(
duration: const Duration(milliseconds: 100), value: 1.0, vsync: this);
}

Don’t forget dispose of AnimationController.

@override
void dispose() {
super.dispose();
_controller.dispose();
}

Next, make a layout. This step, just place an AnimatedIcon. In Backdrop, AppBar’s elevation is not necessary, so set to 0.0. Animation of panel and animation of icon are synchronized by passing _controller.view to the progress of AnimatedIcon.

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
elevation: 0.0,
title: new Text("Step1"),
leading: new IconButton(
onPressed: () {},
icon: new AnimatedIcon(
icon: AnimatedIcons.close_menu,
progress: _controller.view,
),
),
),
body: new Text("text"));
}

Step2

Here, Change AnimatedIcon according to the state of animation. Defined the following getter. About AnimationStatus, easy to understand to read document comments.

bool get _isPanelVisible {
final AnimationStatus status = _controller.status;
return status == AnimationStatus.completed ||
status == AnimationStatus.forward;
}

And animation will execute when button is pressed.

leading: new IconButton(
onPressed: () {
_controller.fling(velocity: _isPanelVisible ? -1.0 : 1.0);
},

Build this, you can check animation of AnimatedIcon ;)

Step3

This step, make a Backdrop’s layout. Defined a following function. For the panel, corners are rounded according to the material design guideline’s example.

static const _PANEL_HEADER_HEIGHT = 32.0;
...
Widget _buildStack(BuildContext context, BoxConstraints constraints) {
final ThemeData theme = Theme.of(context);
return new Container(
color: theme.primaryColor,
child: new Stack(
children: <Widget>[
new Center(
child: new Text("base"),
),
new Material(
borderRadius: const BorderRadius.only(
topLeft: const Radius.circular(16.0),
topRight: const Radius.circular(16.0)),
elevation: 12.0,
child: new Column(children: <Widget>[
new Container(
height: _PANEL_HEADER_HEIGHT,
child: new Center(child: new Text("panel")),
),
new Expanded(child: new Center(child: new Text("content")))
]),
),
],
),
);
}

Pass this layout to LayoutBuilder.

@override
Widget build(BuildContext context) {

...
    body: new LayoutBuilder(
builder: _buildStack,
),

Panel does not move at this point. Let’s go to next :)

Step4

This step is last. First, defined the animation of the panel. Create Animation to pass to PositionedTransition. The height of the header is subtracted from height because display the header with the panel closed.

Animation<RelativeRect> _getPanelAnimation(BoxConstraints constraints) {
final double height = constraints.biggest.height;
final double top = height - _PANEL_HEADER_HEIGHT;
final double bottom = -_PANEL_HEADER_HEIGHT;
return new RelativeRectTween(
begin: new RelativeRect.fromLTRB(0.0, top, 0.0, bottom),
end: new RelativeRect.fromLTRB(0.0, 0.0, 0.0, 0.0),
).animate(new CurvedAnimation(parent: _controller, curve: Curves.linear));
}

Define panel as a child of PositionedTransition because I want to animate the panel when IconButton is pressed. PositionedTransition transitions the child’s position from start to end and animates according to the passed Animation. The following parts….

final ThemeData theme = Theme.of(context);
return new Container(
color: theme.primaryColor,
child: new Stack(
children: <Widget>[
new Center(
child: new Text("base"),
),
new Material(

Rewrite like this.

final Animation<RelativeRect> animation = _getPanelAnimation(constraints);
final ThemeData theme = Theme.of(context);
return new Container(
color: theme.primaryColor,
child: new Stack(
children: <Widget>[
new Center(
child: new Text("base"),
),
new PositionedTransition(
rect: animation,
child: new Material(

That’s all ;)

You can see that the panel move according to icon if build this. This article do not handle panel control by gesture, but it is implemented in flutter’s gallery app, please refer to it if necessary.

This code is in the following repository.

References