blob: 3fabccbb2390810fa11d1306b4850aed1ae1daaa [file] [log] [blame]
// Copyright 2013 The Flutter 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:animations/animations.dart';
import 'package:flutter/material.dart';
const String _loremIpsumParagraph =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod '
'tempor incididunt ut labore et dolore magna aliqua. Vulputate dignissim '
'suspendisse in est. Ut ornare lectus sit amet. Eget nunc lobortis mattis '
'aliquam faucibus purus in. Hendrerit gravida rutrum quisque non tellus '
'orci ac auctor. Mattis aliquam faucibus purus in massa. Tellus rutrum '
'tellus pellentesque eu tincidunt tortor. Nunc eget lorem dolor sed. Nulla '
'at volutpat diam ut venenatis tellus in metus. Tellus cras adipiscing enim '
'eu turpis. Pretium fusce id velit ut tortor. Adipiscing enim eu turpis '
'egestas pretium. Quis varius quam quisque id. Blandit aliquam etiam erat '
'velit scelerisque. In nisl nisi scelerisque eu. Semper risus in hendrerit '
'gravida rutrum quisque. Suspendisse in est ante in nibh mauris cursus '
'mattis molestie. Adipiscing elit duis tristique sollicitudin nibh sit '
'amet commodo nulla. Pretium viverra suspendisse potenti nullam ac tortor '
'vitae.\n'
'\n'
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod '
'tempor incididunt ut labore et dolore magna aliqua. Vulputate dignissim '
'suspendisse in est. Ut ornare lectus sit amet. Eget nunc lobortis mattis '
'aliquam faucibus purus in. Hendrerit gravida rutrum quisque non tellus '
'orci ac auctor. Mattis aliquam faucibus purus in massa. Tellus rutrum '
'tellus pellentesque eu tincidunt tortor. Nunc eget lorem dolor sed. Nulla '
'at volutpat diam ut venenatis tellus in metus. Tellus cras adipiscing enim '
'eu turpis. Pretium fusce id velit ut tortor. Adipiscing enim eu turpis '
'egestas pretium. Quis varius quam quisque id. Blandit aliquam etiam erat '
'velit scelerisque. In nisl nisi scelerisque eu. Semper risus in hendrerit '
'gravida rutrum quisque. Suspendisse in est ante in nibh mauris cursus '
'mattis molestie. Adipiscing elit duis tristique sollicitudin nibh sit '
'amet commodo nulla. Pretium viverra suspendisse potenti nullam ac tortor '
'vitae';
const double _fabDimension = 56.0;
/// The demo page for [OpenContainerTransform].
class OpenContainerTransformDemo extends StatefulWidget {
/// Creates the demo page for [OpenContainerTransform].
const OpenContainerTransformDemo({Key? key}) : super(key: key);
@override
State<OpenContainerTransformDemo> createState() {
return _OpenContainerTransformDemoState();
}
}
class _OpenContainerTransformDemoState
extends State<OpenContainerTransformDemo> {
ContainerTransitionType _transitionType = ContainerTransitionType.fade;
void _showMarkedAsDoneSnackbar(bool? isMarkedAsDone) {
if (isMarkedAsDone ?? false) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('Marked as done!'),
));
}
}
void _showSettingsBottomModalSheet(BuildContext context) {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setModalState) {
return Container(
height: 125,
padding: const EdgeInsets.all(15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Fade mode',
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 12),
ToggleButtons(
borderRadius: BorderRadius.circular(2.0),
selectedBorderColor: Theme.of(context).colorScheme.primary,
onPressed: (int index) {
setModalState(() {
setState(() {
_transitionType = index == 0
? ContainerTransitionType.fade
: ContainerTransitionType.fadeThrough;
});
});
},
isSelected: <bool>[
_transitionType == ContainerTransitionType.fade,
_transitionType == ContainerTransitionType.fadeThrough,
],
children: const <Widget>[
Text('FADE'),
Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: Text('FADE THROUGH'),
),
],
),
],
),
);
},
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Container transform'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
_showSettingsBottomModalSheet(context);
},
),
],
),
body: ListView(
padding: const EdgeInsets.all(8.0),
children: <Widget>[
_OpenContainerWrapper(
transitionType: _transitionType,
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return _ExampleCard(openContainer: openContainer);
},
onClosed: _showMarkedAsDoneSnackbar,
),
const SizedBox(height: 16.0),
_OpenContainerWrapper(
transitionType: _transitionType,
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return _ExampleSingleTile(openContainer: openContainer);
},
onClosed: _showMarkedAsDoneSnackbar,
),
const SizedBox(height: 16.0),
Row(
children: <Widget>[
Expanded(
child: _OpenContainerWrapper(
transitionType: _transitionType,
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return _SmallerCard(
openContainer: openContainer,
subtitle: 'Secondary text',
);
},
onClosed: _showMarkedAsDoneSnackbar,
),
),
const SizedBox(width: 8.0),
Expanded(
child: _OpenContainerWrapper(
transitionType: _transitionType,
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return _SmallerCard(
openContainer: openContainer,
subtitle: 'Secondary text',
);
},
onClosed: _showMarkedAsDoneSnackbar,
),
),
],
),
const SizedBox(height: 16.0),
Row(
children: <Widget>[
Expanded(
child: _OpenContainerWrapper(
transitionType: _transitionType,
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return _SmallerCard(
openContainer: openContainer,
subtitle: 'Secondary',
);
},
onClosed: _showMarkedAsDoneSnackbar,
),
),
const SizedBox(width: 8.0),
Expanded(
child: _OpenContainerWrapper(
transitionType: _transitionType,
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return _SmallerCard(
openContainer: openContainer,
subtitle: 'Secondary',
);
},
onClosed: _showMarkedAsDoneSnackbar,
),
),
const SizedBox(width: 8.0),
Expanded(
child: _OpenContainerWrapper(
transitionType: _transitionType,
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return _SmallerCard(
openContainer: openContainer,
subtitle: 'Secondary',
);
},
onClosed: _showMarkedAsDoneSnackbar,
),
),
],
),
const SizedBox(height: 16.0),
...List<Widget>.generate(10, (int index) {
return OpenContainer<bool>(
transitionType: _transitionType,
openBuilder: (BuildContext _, VoidCallback openContainer) {
return const _DetailsPage();
},
onClosed: _showMarkedAsDoneSnackbar,
tappable: false,
closedShape: const RoundedRectangleBorder(),
closedElevation: 0.0,
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return ListTile(
leading: Image.asset(
'assets/avatar_logo.png',
width: 40,
),
onTap: openContainer,
title: Text('List item ${index + 1}'),
subtitle: const Text('Secondary text'),
);
},
);
}),
],
),
floatingActionButton: OpenContainer(
transitionType: _transitionType,
openBuilder: (BuildContext context, VoidCallback _) {
return const _DetailsPage(
includeMarkAsDoneButton: false,
);
},
closedElevation: 6.0,
closedShape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(_fabDimension / 2),
),
),
closedColor: Theme.of(context).colorScheme.secondary,
closedBuilder: (BuildContext context, VoidCallback openContainer) {
return SizedBox(
height: _fabDimension,
width: _fabDimension,
child: Center(
child: Icon(
Icons.add,
color: Theme.of(context).colorScheme.onSecondary,
),
),
);
},
),
);
}
}
class _OpenContainerWrapper extends StatelessWidget {
const _OpenContainerWrapper({
required this.closedBuilder,
required this.transitionType,
required this.onClosed,
});
final CloseContainerBuilder closedBuilder;
final ContainerTransitionType transitionType;
final ClosedCallback<bool?> onClosed;
@override
Widget build(BuildContext context) {
return OpenContainer<bool>(
transitionType: transitionType,
openBuilder: (BuildContext context, VoidCallback _) {
return const _DetailsPage();
},
onClosed: onClosed,
tappable: false,
closedBuilder: closedBuilder,
);
}
}
class _ExampleCard extends StatelessWidget {
const _ExampleCard({required this.openContainer});
final VoidCallback openContainer;
@override
Widget build(BuildContext context) {
return _InkWellOverlay(
openContainer: openContainer,
height: 300,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Container(
color: Colors.black38,
child: Center(
child: Image.asset(
'assets/placeholder_image.png',
width: 100,
),
),
),
),
const ListTile(
title: Text('Title'),
subtitle: Text('Secondary text'),
),
Padding(
padding: const EdgeInsets.only(
left: 16.0,
right: 16.0,
bottom: 16.0,
),
child: Text(
'Lorem ipsum dolor sit amet, consectetur '
'adipiscing elit, sed do eiusmod tempor.',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.black54),
),
),
],
),
);
}
}
class _SmallerCard extends StatelessWidget {
const _SmallerCard({
required this.openContainer,
required this.subtitle,
});
final VoidCallback openContainer;
final String subtitle;
@override
Widget build(BuildContext context) {
return _InkWellOverlay(
openContainer: openContainer,
height: 225,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
color: Colors.black38,
height: 150,
child: Center(
child: Image.asset(
'assets/placeholder_image.png',
width: 80,
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Title',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 4),
Text(
subtitle,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
),
],
),
);
}
}
class _ExampleSingleTile extends StatelessWidget {
const _ExampleSingleTile({required this.openContainer});
final VoidCallback openContainer;
@override
Widget build(BuildContext context) {
const double height = 100.0;
return _InkWellOverlay(
openContainer: openContainer,
height: height,
child: Row(
children: <Widget>[
Container(
color: Colors.black38,
height: height,
width: height,
child: Center(
child: Image.asset(
'assets/placeholder_image.png',
width: 60,
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Title',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Text(
'Lorem ipsum dolor sit amet, consectetur '
'adipiscing elit,',
style: Theme.of(context).textTheme.bodySmall),
],
),
),
),
],
),
);
}
}
class _InkWellOverlay extends StatelessWidget {
const _InkWellOverlay({
this.openContainer,
this.height,
this.child,
});
final VoidCallback? openContainer;
final double? height;
final Widget? child;
@override
Widget build(BuildContext context) {
return SizedBox(
height: height,
child: InkWell(
onTap: openContainer,
child: child,
),
);
}
}
class _DetailsPage extends StatelessWidget {
const _DetailsPage({this.includeMarkAsDoneButton = true});
final bool includeMarkAsDoneButton;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Details page'),
actions: <Widget>[
if (includeMarkAsDoneButton)
IconButton(
icon: const Icon(Icons.done),
onPressed: () => Navigator.pop(context, true),
tooltip: 'Mark as done',
)
],
),
body: ListView(
children: <Widget>[
Container(
color: Colors.black38,
height: 250,
child: Padding(
padding: const EdgeInsets.all(70.0),
child: Image.asset(
'assets/placeholder_image.png',
),
),
),
Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Title',
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
color: Colors.black54,
fontSize: 30.0,
),
),
const SizedBox(height: 10),
Text(
_loremIpsumParagraph,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Colors.black54,
height: 1.5,
fontSize: 16.0,
),
),
],
),
),
],
),
);
}
}