I am implementing FAB as Expandable with little dark overlay when FAB is clicked. My problem is there is right margin in Scaffold and my overlay is not filling whole view.
How to remove that right margin?
Here is how it's looking:
Here is my Scaffold code:
return Scaffold(
resizeToAvoidBottomInset: true,
floatingActionButton: !isSearchBarVisible ? SizedBox.expand(
child: ExpandableFab(
//key: _key,
distance: size.height * 0.09,
children: [
/* not needed to show problem */
],
),
) : null,
body: /* some body */
and here is ExpandableFab class
class ExpandableFab extends StatefulWidget {
const ExpandableFab({
Key? key,
this.initialOpen,
required this.distance,
required this.children,
}) : super(key: key);
final bool? initialOpen;
final double distance;
final List<Widget> children;
@override
_ExpandableFabState createState() => _ExpandableFabState();
}
class _ExpandableFabState extends State<ExpandableFab>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
Animation<double>? _expandAnimation;
bool _open = false;
@override
void initState() {
super.initState();
_open = widget.initialOpen ?? false;
_controller = AnimationController(
value: _open ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
vsync: this,
);
_expandAnimation = CurvedAnimation(
curve: Curves.fastOutSlowIn,
reverseCurve: Curves.easeOutQuad,
parent: _controller,
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _toggle() {
setState(() {
_open = !_open;
if (_open) {
_controller.forward();
} else {
_controller.reverse();
}
});
}
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return GestureDetector(
onTap: () => _toggle(),
child: Stack(
alignment: Alignment.bottomRight,
clipBehavior: Clip.none,
children: [
IgnorePointer(
ignoring: !_open,
child: TweenAnimationBuilder<double>(
tween: Tween<double>(begin: 0.0, end: _open ? 1.0 : 0.0),
duration: Duration(milliseconds: 500),
curve: Curves.easeInOut,
builder: (_, value, child) {
if (value < 0.001) {
return child!;
}
return BackdropFilter(
filter: ImageFilter.blur(sigmaX: value, sigmaY: value),
child: child,
);
},
child: Container(color: Colors.transparent),
),
),
IgnorePointer(
ignoring: !_open,
child: AnimatedOpacity(
duration: Duration(milliseconds: 500),
opacity: _open ? 1 : 0,
curve: Curves.easeInOut,
child: Container(
color: Colors.black12,
),
),
),
Transform.translate(
offset: Offset(0, 0),
child: Stack(
alignment: Alignment.bottomRight,
children: [
Positioned(
bottom: size.height * 0.14,
child: _buildTapToCloseFab(size)
),
Positioned(
bottom: size.height * 0.14,
child: _buildTapToOpenFab(size)
),
..._buildExpandingActionButtons(),
],
),
),
],
),
);
}
Widget _buildTapToCloseFab(Size size) {
return SizedBox(
width: 56.0,
height: 56.0,
child: Center(
child: Material(
shape: const CircleBorder(),
clipBehavior: Clip.antiAlias,
elevation: 4.0,
child: InkWell(
onTap: _toggle,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.close,
color: Theme.of(context).primaryColor,
),
),
),
),
),
);
}
Widget _buildTapToOpenFab(Size size) {
return IgnorePointer(
ignoring: _open,
child: AnimatedContainer(
transformAlignment: Alignment.center,
transform: Matrix4.diagonal3Values(
_open ? 0.7 : 1.0,
_open ? 0.7 : 1.0,
1.0,
),
duration: const Duration(milliseconds: 250),
curve: const Interval(0.0, 0.5, curve: Curves.easeOut),
child: AnimatedOpacity(
opacity: _open ? 0.0 : 1.0,
curve: const Interval(0.25, 1.0, curve: Curves.easeInOut),
duration: const Duration(milliseconds: 250),
child: FloatingActionButton(
onPressed: _toggle,
child: Icon(
Icons.add,
),
),
),
),
);
}
List<Widget> _buildExpandingActionButtons() {
final children = <Widget>[];
final count = widget.children.length;
final step = 90.0 / (count - 1);
var dist;
for (var i = 0, angleInDegrees = 0.0;
i < count;
i++, angleInDegrees += step) {
if (i == 0) {
dist = (widget.distance) * (i + 1);
}
else {
dist = (widget.distance) * (i + 1);
}
children.add(
_ExpandingActionButton(
directionInDegrees: 90,
maxDistance: dist,
progress: _expandAnimation,
child: widget.children[i],
),
);
}
return children;
}
}
class _ExpandingActionButton extends StatelessWidget {
const _ExpandingActionButton({
Key? key,
required this.directionInDegrees,
required this.maxDistance,
required this.progress,
required this.child,
}) : super(key: key);
final double directionInDegrees;
final double maxDistance;
final Animation<double>? progress;
final Widget child;
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return AnimatedBuilder(
animation: progress!,
builder: (BuildContext context, Widget? child) {
final offset = Offset.fromDirection(
directionInDegrees * (math.pi / 180.0),
progress!.value * maxDistance,
);
return Positioned(
right: 4.0 + offset.dx,
bottom: (size.height * 0.14) + 4.0 + offset.dy,
child: Transform.rotate(
angle: (1.0 - progress!.value) * math.pi / 2,
child: child,
),
);
},
child: FadeTransition(
opacity: progress!,
child: child,
),
);
}
}
It's mostly code from Flutter tutorial: https://docs.flutter.dev/cookbook/effects/expandable-fab with some little changes like vertical expand or overlay when FAB is expanded.
