不透明度在打开和关闭时都有效,高度变化仅在打开时有动画效果,在关闭时没有动画效果。
下面是我的代码,但一直没有成功。
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;
}
}
查询了各种方案
§§ 你的代码表达出来的思想挺好的^_^(^o^)/~ §§
把BuildBarrier中的GestureDetector放到BuildPage中来,另外AnimationController 有一个addStatusListener的方法,可以观察动画执行的状态;
下面是完整的修改过后的代码:
也许类似下面的写法会更好,重点就是不重写BuildBarrier,而是利用系统提供的API,这样就可以不用写controller啦。
( ̄o ̄) . z Z