Opacity is effective both on and off, height changes only have animation effects when turned on, and not when turned off.
The following is a runnable code that I have tried for a long time, but it has not been successful. I suspect it may be a problem with the controller. I can use the global controller to control it and execute the reverse method
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: MyHomePage(),
));
}
}
class MyHomePage extends StatelessWidget {
void _showDismissibleDialog(BuildContext context) async {
Navigator.of(context).push(DismissibleDialog());
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Dismissible Dialog Example'),
),
body: Center(
child: ElevatedButton(
onPressed: () => _showDismissibleDialog(context),
child: Text('Show Dialog'),
),
),
);
}
}
class DismissibleDialog<T> extends PopupRoute<T> {
@override
Color? get barrierColor => Colors.transparent;
@override
bool get barrierDismissible => true;
@override
String? get barrierLabel => 'Dismissible Dialog';
@override
Duration get transitionDuration => const Duration(milliseconds: 300);
@override
bool get opaque => false;
@override
Widget buildModalBarrier() {
return const AnimatiedContainer(
child: BuildBarrier(),
);
}
@override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return const AnimatiedContainer(
child: BuildPage(),
);
}
}
class BuildBarrier extends StatelessWidget {
const BuildBarrier({super.key});
@override
Widget build(BuildContext context) {
final opacityController =
AnimationControllerProvider.of(context).opacityController;
final heightController =
AnimationControllerProvider.of(context).heightController;
return SafeArea(
child: Stack(
children: [
Positioned(
top: kToolbarHeight,
left: 0,
right: 0,
bottom: 0,
child: GestureDetector(
onTap: () {
Future.wait([
opacityController.reverse(),
heightController.reverse(),
]).then((res) {
Navigator.of(context).pop();
});
},
child: FadeTransition(
opacity: Tween<double>(begin: 0, end: 0.5).animate(
CurvedAnimation(
parent: opacityController, curve: Curves.easeInOut),
),
child: Container(
color: Colors.black.withOpacity(0.5),
),
),
),
)
],
),
);
}
}
class BuildPage extends StatelessWidget {
const BuildPage({super.key});
@override
Widget build(BuildContext context) {
final heightController =
AnimationControllerProvider.of(context).heightController;
return SafeArea(
child: DefaultTextStyle(
style: Theme.of(context).textTheme.bodyMedium!,
child: Stack(
children: [
Positioned(
top: kToolbarHeight,
left: 0,
child: SizeTransition(
sizeFactor: Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(
parent: heightController, curve: Curves.easeInOut),
),
axisAlignment: -1,
child: Container(
width: MediaQuery.of(context).size.width,
padding: const EdgeInsets.all(20.0),
decoration: const BoxDecoration(
color: Colors.white,
),
child: Column(
children: <Widget>[
Text(
'Dismissible Dialog',
style: Theme.of(context).textTheme.headlineSmall,
),
],
),
),
),
),
],
),
),
);
}
}
class AnimatiedContainer extends StatefulWidget {
final Widget child;
const AnimatiedContainer({super.key, required this.child});
@override
State<AnimatiedContainer> createState() => _AnimatiedContainerState();
}
class _AnimatiedContainerState extends State<AnimatiedContainer>
with TickerProviderStateMixin {
late final AnimationController _opacityController;
late final AnimationController _heightController;
bool isExpand = false;
@override
void initState() {
super.initState();
_heightController = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
)..forward();
_opacityController = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
)..forward();
}
@override
void dispose() {
_opacityController.dispose();
_heightController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimationControllerProvider(
opacityController: _opacityController,
heightController: _heightController,
isExpand: isExpand,
child: Container(
child: widget.child,
),
);
}
}
class AnimationControllerProvider extends InheritedWidget {
final AnimationController opacityController;
final AnimationController heightController;
final bool isExpand;
const AnimationControllerProvider({
Key? key,
required this.opacityController,
required this.heightController,
required Widget child,
this.isExpand = false,
}) : super(key: key, child: child);
static AnimationControllerProvider of(BuildContext context) {
final AnimationControllerProvider? result = context
.dependOnInheritedWidgetOfExactType<AnimationControllerProvider>();
assert(result != null, 'No AnimationController found in context');
return result!;
}
@override
bool updateShouldNotify(AnimationControllerProvider old) {
return opacityController != old.opacityController ||
heightController != old.heightController;
}
}