104 lines
2.7 KiB
Dart
104 lines
2.7 KiB
Dart
|
|
import 'package:flutter/material.dart';
|
||
|
|
|
||
|
|
|
||
|
|
class PanelToggleAffordance extends StatefulWidget {
|
||
|
|
static const double _radius = 8;
|
||
|
|
static const Color _activeFill = Color(0x2EFFFFFF);
|
||
|
|
static const Duration _openDuration = Duration(milliseconds: 220);
|
||
|
|
static const Duration _closeDuration = Duration(milliseconds: 130);
|
||
|
|
|
||
|
|
final bool isOpen;
|
||
|
|
final VoidCallback onTap;
|
||
|
|
final String? tooltip;
|
||
|
|
final EdgeInsets padding;
|
||
|
|
final Widget child;
|
||
|
|
|
||
|
|
const PanelToggleAffordance({
|
||
|
|
super.key,
|
||
|
|
required this.isOpen,
|
||
|
|
required this.onTap,
|
||
|
|
required this.child,
|
||
|
|
this.tooltip,
|
||
|
|
this.padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||
|
|
});
|
||
|
|
|
||
|
|
@override
|
||
|
|
State<PanelToggleAffordance> createState() => _PanelToggleAffordanceState();
|
||
|
|
}
|
||
|
|
|
||
|
|
class _PanelToggleAffordanceState extends State<PanelToggleAffordance>
|
||
|
|
with SingleTickerProviderStateMixin {
|
||
|
|
late final AnimationController _ctrl;
|
||
|
|
late final Animation<double> _t;
|
||
|
|
|
||
|
|
@override
|
||
|
|
void initState() {
|
||
|
|
super.initState();
|
||
|
|
_ctrl = AnimationController(
|
||
|
|
vsync: this,
|
||
|
|
duration: PanelToggleAffordance._openDuration,
|
||
|
|
reverseDuration: PanelToggleAffordance._closeDuration,
|
||
|
|
value: widget.isOpen ? 1.0 : 0.0,
|
||
|
|
);
|
||
|
|
_t = CurvedAnimation(
|
||
|
|
parent: _ctrl,
|
||
|
|
curve: Curves.easeInOutCubic,
|
||
|
|
reverseCurve: Curves.easeInCubic,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
void didUpdateWidget(covariant PanelToggleAffordance oldWidget) {
|
||
|
|
super.didUpdateWidget(oldWidget);
|
||
|
|
if (oldWidget.isOpen != widget.isOpen) {
|
||
|
|
if (widget.isOpen) {
|
||
|
|
_ctrl.forward();
|
||
|
|
} else {
|
||
|
|
_ctrl.reverse();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
void dispose() {
|
||
|
|
_ctrl.dispose();
|
||
|
|
super.dispose();
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
final inkWell = Material(
|
||
|
|
color: Colors.transparent,
|
||
|
|
child: InkWell(
|
||
|
|
borderRadius: BorderRadius.circular(PanelToggleAffordance._radius),
|
||
|
|
onTap: widget.onTap,
|
||
|
|
child: AnimatedBuilder(
|
||
|
|
animation: _t,
|
||
|
|
builder: (context, child) {
|
||
|
|
final fill = Color.lerp(
|
||
|
|
Colors.transparent,
|
||
|
|
PanelToggleAffordance._activeFill,
|
||
|
|
_t.value,
|
||
|
|
)!;
|
||
|
|
return Container(
|
||
|
|
padding: widget.padding,
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
borderRadius: BorderRadius.circular(
|
||
|
|
PanelToggleAffordance._radius,
|
||
|
|
),
|
||
|
|
color: fill,
|
||
|
|
),
|
||
|
|
child: child,
|
||
|
|
);
|
||
|
|
},
|
||
|
|
child: widget.child,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
|
||
|
|
final t = widget.tooltip;
|
||
|
|
if (t == null) return inkWell;
|
||
|
|
return Tooltip(message: t, child: inkWell);
|
||
|
|
}
|
||
|
|
}
|