Skip to content

Instantly share code, notes, and snippets.

@esDotDev
Last active December 31, 2022 13:37
Show Gist options
  • Save esDotDev/8879b345a2f5eef780fa4840c3a4be30 to your computer and use it in GitHub Desktop.
Save esDotDev/8879b345a2f5eef780fa4840c3a4be30 to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
/// Click the content to expand / constact the card.
/// These overflow warnings are not helpful, and impact developer efficiency and joy.
// It would be nice to turn them off for this specific widget.
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _isOpen = false;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: GestureDetector(
onTap: () => setState(() => _isOpen = !_isOpen),
child: Center(
child: OpeningCard(
isOpen: _isOpen,
background: Container(color: Colors.grey),
openBuilder: (_) => FlutterLogo(size: 300),
closedBuilder: (_) => Text('I am small!'),
),
),
),
),
);
}
}
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
'Hello, World!',
style: Theme.of(context).textTheme.headline4,
);
}
}
class MeasurableWidget extends SingleChildRenderObjectWidget {
const MeasurableWidget(
{Key? key, required this.onChange, required Widget child})
: super(key: key, child: child);
final void Function(Size size) onChange;
@override
RenderObject createRenderObject(BuildContext context) =>
MeasureSizeRenderObject(onChange);
}
class MeasureSizeRenderObject extends RenderProxyBox {
MeasureSizeRenderObject(this.onChange);
void Function(Size size) onChange;
Size _prevSize = Size.zero;
@override
void performLayout() {
super.performLayout();
Size newSize = child?.size ?? Size.zero;
if (_prevSize == newSize) return;
_prevSize = newSize;
WidgetsBinding.instance?.addPostFrameCallback((_) => onChange(newSize));
}
}
class OpeningCard extends StatefulWidget {
const OpeningCard(
{Key? key,
required this.closedBuilder,
required this.openBuilder,
required this.isOpen,
this.background,
this.padding})
: super(key: key);
final Widget Function(BuildContext) closedBuilder;
final Widget Function(BuildContext) openBuilder;
final Widget? background;
final bool isOpen;
final EdgeInsets? padding;
@override
State<OpeningCard> createState() => _OpeningCardState();
}
class _OpeningCardState extends State<OpeningCard> {
Size _size = Size.zero;
@override
Widget build(BuildContext context) {
return TweenAnimationBuilder<Size>(
duration: Duration(seconds: 1),
curve: Curves.easeOut,
builder: (_, value, child) => Stack(
children: [
if (widget.background != null) ...[
Positioned.fill(child: widget.background!),
],
Padding(
padding: widget.padding ?? EdgeInsets.zero,
child: SizedBox(
width: value.width,
height: value.height,
child: child,
),
),
],
),
tween: Tween(begin: _size, end: _size),
child: AnimatedSwitcher(
duration: Duration(seconds: 1),
child: ClipRect(
key: ValueKey(widget.isOpen),
child: UnconstrainedBox(
child: MeasurableWidget(
onChange: (size) {
setState(() => _size = size);
},
child: widget.isOpen
? widget.openBuilder(context)
: widget.closedBuilder(context),
),
),
),
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment