Created
August 29, 2021 15:44
-
-
Save gaetschwartz/86fd4dbc501257116afe1ae4bc734610 to your computer and use it in GitHub Desktop.
Expandable Dialog
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(const MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return const MaterialApp( | |
title: 'Flutter Demo', | |
home: MyHomePage(), | |
); | |
} | |
} | |
class MyHomePage extends StatefulWidget { | |
const MyHomePage({Key? key}) : super(key: key); | |
@override | |
State<MyHomePage> createState() => _MyHomePageState(); | |
} | |
class _MyHomePageState extends State<MyHomePage> { | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text("Expand dialog"), | |
), | |
body: Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: <Widget>[ | |
ElevatedButton( | |
onPressed: () { | |
Navigator.of(context).push(PageRouteBuilder( | |
pageBuilder: (_, __, ___) => ExpandableDialog( | |
insetAnimationCurve: Curves.bounceOut, | |
insetAnimationDuration: const Duration(milliseconds: 500), | |
builder: (context, setExpansion, expanded) => Column( | |
children: [ | |
for (var i = 0; i < 10; i++) | |
ListTile( | |
title: Text("Tile $i"), | |
), | |
SwitchListTile( | |
title: Text(expanded ? "Shrink" : "Expand"), | |
onChanged: (value) => setExpansion(value), | |
value: expanded, | |
), | |
], | |
mainAxisSize: MainAxisSize.max, | |
), | |
), | |
barrierDismissible: true, | |
barrierColor: Colors.black26, | |
opaque: false, | |
)); | |
}, | |
child: const Text("Open dialog")) | |
], | |
), | |
), | |
); | |
} | |
} | |
const EdgeInsets _defaultInsetPadding = | |
EdgeInsets.symmetric(horizontal: 40.0, vertical: 36.0); | |
class ExpandableDialog extends StatefulWidget { | |
const ExpandableDialog({ | |
Key? key, | |
this.backgroundColor, | |
this.elevation, | |
this.insetAnimationDuration = const Duration(milliseconds: 100), | |
this.insetAnimationCurve = Curves.decelerate, | |
this.insetPadding = _defaultInsetPadding, | |
this.clipBehavior = Clip.none, | |
this.shape, | |
required this.builder, | |
}) : super(key: key); | |
final Color? backgroundColor; | |
final double? elevation; | |
final Duration insetAnimationDuration; | |
final Curve insetAnimationCurve; | |
final EdgeInsets insetPadding; | |
final Clip clipBehavior; | |
final ShapeBorder? shape; | |
final Widget Function(BuildContext context, | |
void Function(bool expand) setExpansion, bool expanded) builder; | |
static const RoundedRectangleBorder _defaultDialogShape = | |
RoundedRectangleBorder( | |
borderRadius: BorderRadius.all(Radius.circular(4.0))); | |
static const double _defaultElevation = 24.0; | |
@override | |
State<ExpandableDialog> createState() => _ExpandableDialogState(); | |
} | |
class _ExpandableDialogState extends State<ExpandableDialog> { | |
bool expanded = false; | |
@override | |
Widget build(BuildContext context) { | |
final DialogTheme dialogTheme = DialogTheme.of(context); | |
final EdgeInsets effectivePadding = | |
MediaQuery.of(context).viewInsets + widget.insetPadding; | |
return AnimatedPadding( | |
padding: expanded ? const EdgeInsets.all(0) : effectivePadding, | |
duration: widget.insetAnimationDuration, | |
curve: widget.insetAnimationCurve, | |
child: MediaQuery.removeViewInsets( | |
removeLeft: true, | |
removeTop: true, | |
removeRight: true, | |
removeBottom: true, | |
context: context, | |
child: Center( | |
child: ConstrainedBox( | |
constraints: const BoxConstraints(minWidth: 280.0), | |
child: Material( | |
color: widget.backgroundColor ?? | |
dialogTheme.backgroundColor ?? | |
Theme.of(context).dialogBackgroundColor, | |
elevation: widget.elevation ?? | |
dialogTheme.elevation ?? | |
ExpandableDialog._defaultElevation, | |
shape: widget.shape ?? | |
dialogTheme.shape ?? | |
ExpandableDialog._defaultDialogShape, | |
type: MaterialType.card, | |
clipBehavior: widget.clipBehavior, | |
child: widget.builder( | |
context, | |
(expansion) => setState( | |
() => mounted ? (expanded = expansion) : null, | |
), | |
expanded, | |
), | |
), | |
), | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment