Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 113 additions & 4 deletions packages/animations/example/lib/container_transition.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ class OpenContainerTransformDemo extends StatefulWidget {
class _OpenContainerTransformDemoState
extends State<OpenContainerTransformDemo> {
ContainerTransitionType _transitionType = ContainerTransitionType.fade;
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();

static int doneCounter = 0;
Comment thread
Melvin-Abraham marked this conversation as resolved.
Outdated
final ValueKey<int> counterKey = ValueKey<int>(doneCounter);

void _showSettingsBottomModalSheet(BuildContext context) {
showModalBottomSheet<void>(
Expand Down Expand Up @@ -103,6 +107,7 @@ class _OpenContainerTransformDemoState
@override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: const Text('Container transform'),
actions: <Widget>[
Expand All @@ -117,18 +122,63 @@ class _OpenContainerTransformDemoState
body: ListView(
padding: const EdgeInsets.all(8.0),
children: <Widget>[
Container(
Comment thread
Melvin-Abraham marked this conversation as resolved.
Outdated
key: counterKey,
padding: const EdgeInsets.symmetric(
vertical: 15.0,
horizontal: 10.0,
),
margin: const EdgeInsets.only(bottom: 10.0),
decoration: BoxDecoration(
color: Colors.purple[100],
borderRadius: BorderRadius.circular(5.0),
),
child: Row(
children: <Widget>[
const Padding(
padding: EdgeInsets.only(right: 10.0),
child: Icon(Icons.done),
),
RichText(
text: TextSpan(
style: const TextStyle(fontSize: 18.0, color: Colors.black),
children: <TextSpan>[
const TextSpan(text: 'Marked as done '),
TextSpan(
text: '$doneCounter',
style: const TextStyle(fontWeight: FontWeight.bold),
),
const TextSpan(text: ' times'),
],
),
),
],
),
),
_OpenContainerWrapper(
transitionType: _transitionType,
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return _ExampleCard(openContainer: openContainer);
},
onClosed: (bool isMarkedAsDone) {
Comment thread
Melvin-Abraham marked this conversation as resolved.
Outdated
if (isMarkedAsDone)
setState(() {
doneCounter++;
});
},
),
const SizedBox(height: 16.0),
_OpenContainerWrapper(
transitionType: _transitionType,
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return _ExampleSingleTile(openContainer: openContainer);
},
onClosed: (bool isMarkedAsDone) {
if (isMarkedAsDone)
setState(() {
doneCounter++;
});
},
),
const SizedBox(height: 16.0),
Row(
Expand All @@ -142,6 +192,12 @@ class _OpenContainerTransformDemoState
subtitle: 'Secondary text',
);
},
onClosed: (bool isMarkedAsDone) {
if (isMarkedAsDone)
setState(() {
doneCounter++;
});
},
),
),
const SizedBox(width: 8.0),
Expand All @@ -154,6 +210,12 @@ class _OpenContainerTransformDemoState
subtitle: 'Secondary text',
);
},
onClosed: (bool isMarkedAsDone) {
if (isMarkedAsDone)
setState(() {
doneCounter++;
});
},
),
),
],
Expand All @@ -170,6 +232,12 @@ class _OpenContainerTransformDemoState
subtitle: 'Secondary',
);
},
onClosed: (bool isMarkedAsDone) {
if (isMarkedAsDone)
setState(() {
doneCounter++;
});
},
),
),
const SizedBox(width: 8.0),
Expand All @@ -182,6 +250,12 @@ class _OpenContainerTransformDemoState
subtitle: 'Secondary',
);
},
onClosed: (bool isMarkedAsDone) {
if (isMarkedAsDone)
setState(() {
doneCounter++;
});
},
),
),
const SizedBox(width: 8.0),
Expand All @@ -194,17 +268,34 @@ class _OpenContainerTransformDemoState
subtitle: 'Secondary',
);
},
onClosed: (bool isMarkedAsDone) {
if (isMarkedAsDone)
setState(() {
doneCounter++;
});
},
),
),
],
),
const SizedBox(height: 16.0),
...List<Widget>.generate(10, (int index) {
return OpenContainer(
return OpenContainer<bool>(
transitionType: _transitionType,
openBuilder: (BuildContext _, VoidCallback openContainer) {
return _DetailsPage();
},
onClosed: (bool isMarkedAsDone) {
if (isMarkedAsDone) {
scaffoldKey.currentState.showSnackBar(const SnackBar(
content: Text('Marked as done!'),
));

setState(() {
doneCounter++;
});
}
},
tappable: false,
closedShape: const RoundedRectangleBorder(),
closedElevation: 0.0,
Expand All @@ -223,7 +314,7 @@ class _OpenContainerTransformDemoState
}),
],
),
floatingActionButton: OpenContainer(
floatingActionButton: OpenContainer<bool>(
transitionType: _transitionType,
openBuilder: (BuildContext context, VoidCallback _) {
return _DetailsPage();
Expand All @@ -235,6 +326,12 @@ class _OpenContainerTransformDemoState
),
),
closedColor: Theme.of(context).colorScheme.secondary,
onClosed: (bool isMarkedAsDone) {
if (isMarkedAsDone)
setState(() {
doneCounter++;
});
},
closedBuilder: (BuildContext context, VoidCallback openContainer) {
return SizedBox(
height: _fabDimension,
Expand All @@ -256,18 +353,21 @@ class _OpenContainerWrapper extends StatelessWidget {
const _OpenContainerWrapper({
this.closedBuilder,
this.transitionType,
this.onClosed,
});

final OpenContainerBuilder closedBuilder;
final ContainerTransitionType transitionType;
final onClosedCallback<bool> onClosed;

@override
Widget build(BuildContext context) {
return OpenContainer(
return OpenContainer<bool>(
transitionType: transitionType,
openBuilder: (BuildContext context, VoidCallback _) {
return _DetailsPage();
},
onClosed: onClosed,
tappable: false,
closedBuilder: closedBuilder,
);
Expand Down Expand Up @@ -456,7 +556,16 @@ class _DetailsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Details page')),
appBar: AppBar(
title: const Text('Details page'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.done),
onPressed: () => Navigator.pop(context, true),
tooltip: 'Mark as done',
)
],
),
body: ListView(
children: <Widget>[
Container(
Expand Down
31 changes: 21 additions & 10 deletions packages/animations/lib/src/open_container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ enum ContainerTransitionType {
fadeThrough,
}

/// Callback function which is called when the [OpenContainer]
/// is closed.
typedef onClosedCallback<S> = void Function(S data);
Comment thread
Melvin-Abraham marked this conversation as resolved.
Outdated

/// A container that grows to fill the screen to reveal new content when tapped.
///
/// While the container is closed, it shows the [Widget] returned by
Expand All @@ -45,17 +49,21 @@ enum ContainerTransitionType {
/// [closedBuilder] exist in the tree at the same time. Therefore, the widgets
/// returned by these builders cannot include the same global key.
///
/// `T` refers to the type of data returned by the route when the container
/// is closed. This value can be accessed in the `onClosed` function.
///
// TODO(goderbauer): Add example animations and sample code.
///
/// See also:
///
/// * [Transitions with animated containers](https://material.io/design/motion/choreography.html#transformation)
/// in the Material spec.
class OpenContainer extends StatefulWidget {
@optionalTypeArgs
class OpenContainer<T> extends StatefulWidget {
Comment thread
Melvin-Abraham marked this conversation as resolved.
Outdated
/// Creates an [OpenContainer].
///
/// All arguments except for [key] must not be null. The arguments
/// [closedBuilder] and [closedBuilder] are required.
/// [openBuilder] and [closedBuilder] are required.
const OpenContainer({
Key key,
this.closedColor = Colors.white,
Expand Down Expand Up @@ -167,7 +175,9 @@ class OpenContainer extends StatefulWidget {
final ShapeBorder openShape;

/// Called when the container was popped and has returned to the closed state.
final VoidCallback onClosed;
/// The return value from the popped screen is passed to this function as an
Comment thread
Melvin-Abraham marked this conversation as resolved.
/// argument.
Comment thread
Melvin-Abraham marked this conversation as resolved.
final onClosedCallback<T> onClosed;

/// Called to obtain the child for the container in the closed state.
///
Expand Down Expand Up @@ -218,10 +228,10 @@ class OpenContainer extends StatefulWidget {
final bool useRootNavigator;

@override
_OpenContainerState createState() => _OpenContainerState();
_OpenContainerState<T> createState() => _OpenContainerState<T>();
}

class _OpenContainerState extends State<OpenContainer> {
class _OpenContainerState<T> extends State<OpenContainer<T>> {
// Key used in [_OpenContainerRoute] to hide the widget returned by
// [OpenContainer.openBuilder] in the source route while the container is
// opening/open. A copy of that widget is included in the
Expand All @@ -235,8 +245,9 @@ class _OpenContainerState extends State<OpenContainer> {
final GlobalKey _closedBuilderKey = GlobalKey();

Future<void> openContainer() async {
await Navigator.of(context, rootNavigator: widget.useRootNavigator)
.push(_OpenContainerRoute(
final T data =
await Navigator.of(context, rootNavigator: widget.useRootNavigator)
.push(_OpenContainerRoute<T>(
Comment thread
Melvin-Abraham marked this conversation as resolved.
Outdated
closedColor: widget.closedColor,
openColor: widget.openColor,
closedElevation: widget.closedElevation,
Expand All @@ -251,7 +262,7 @@ class _OpenContainerState extends State<OpenContainer> {
transitionType: widget.transitionType,
));
if (widget.onClosed != null) {
widget.onClosed();
widget.onClosed(data);
}
}

Expand Down Expand Up @@ -347,7 +358,7 @@ class _HideableState extends State<_Hideable> {
}
}

class _OpenContainerRoute extends ModalRoute<void> {
class _OpenContainerRoute<T> extends ModalRoute<T> {
_OpenContainerRoute({
@required this.closedColor,
@required this.openColor,
Expand Down Expand Up @@ -580,7 +591,7 @@ class _OpenContainerRoute extends ModalRoute<void> {
}

@override
bool didPop(void result) {
bool didPop(T result) {
_takeMeasurements(
navigatorContext: subtreeContext,
delayForSourceRoute: true,
Expand Down
Loading