animated_switcher_widget.dart 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/material.dart';
  3. class AnimatedSwitcherWidget extends StatefulWidget {
  4. final SwitcherController controller;
  5. final Duration duration;
  6. const AnimatedSwitcherWidget({
  7. super.key,
  8. required this.controller,
  9. this.duration = const Duration(milliseconds: 1500),
  10. });
  11. @override
  12. State<AnimatedSwitcherWidget> createState() => _AnimatedSwitcherWidgetState();
  13. }
  14. class _AnimatedSwitcherWidgetState extends State<AnimatedSwitcherWidget> {
  15. Widget? _currentWidget;
  16. @override
  17. void initState() {
  18. super.initState();
  19. // 注册更新回调
  20. widget.controller._registerOnUpdate(() => setState(() {
  21. _currentWidget = widget.controller._currentWidget;
  22. }));
  23. }
  24. @override
  25. Widget build(BuildContext context) {
  26. return ClipRRect(
  27. child: AnimatedSwitcher(
  28. duration: widget.duration,
  29. transitionBuilder: (Widget child, Animation<double> animation) {
  30. return SlideTransitionX(
  31. direction: AxisDirection.down,
  32. position: animation,
  33. child: child,
  34. );
  35. },
  36. child: Center(key: ValueKey(_currentWidget), child: _currentWidget),
  37. ),
  38. );
  39. }
  40. }
  41. class SwitcherController {
  42. // 保存当前组件和回调函数
  43. Widget? _currentWidget;
  44. VoidCallback? _onUpdate;
  45. // 更新组件并触发回调
  46. void updateWidget(Widget newWidget) {
  47. _currentWidget = newWidget;
  48. _onUpdate?.call();
  49. }
  50. // 注册更新回调(供State内部调用)
  51. void _registerOnUpdate(VoidCallback onUpdate) {
  52. _onUpdate = onUpdate;
  53. }
  54. }
  55. class SlideTransitionX extends AnimatedWidget {
  56. SlideTransitionX({
  57. super.key,
  58. required Animation<double> position,
  59. this.transformHitTests = true,
  60. this.direction = AxisDirection.down,
  61. required this.child,
  62. }) : super(listenable: position) {
  63. switch (direction) {
  64. case AxisDirection.up:
  65. _tween = Tween(begin: const Offset(0, 1), end: const Offset(0, 0));
  66. break;
  67. case AxisDirection.right:
  68. _tween = Tween(begin: const Offset(-1, 0), end: const Offset(0, 0));
  69. break;
  70. case AxisDirection.down:
  71. _tween = Tween(begin: const Offset(0, -1), end: const Offset(0, 0));
  72. break;
  73. case AxisDirection.left:
  74. _tween = Tween(begin: const Offset(1, 0), end: const Offset(0, 0));
  75. break;
  76. }
  77. }
  78. final bool transformHitTests;
  79. final Widget child;
  80. final AxisDirection direction;
  81. late final Tween<Offset> _tween;
  82. @override
  83. Widget build(BuildContext context) {
  84. final position = listenable as Animation<double>;
  85. Offset offset = _tween.evaluate(position);
  86. if (position.status == AnimationStatus.reverse) {
  87. switch (direction) {
  88. case AxisDirection.up:
  89. offset = Offset(offset.dx, -offset.dy);
  90. break;
  91. case AxisDirection.right:
  92. offset = Offset(-offset.dx, offset.dy);
  93. break;
  94. case AxisDirection.down:
  95. offset = Offset(offset.dx, -offset.dy);
  96. break;
  97. case AxisDirection.left:
  98. offset = Offset(-offset.dx, offset.dy);
  99. break;
  100. }
  101. }
  102. return FractionalTranslation(
  103. translation: offset,
  104. transformHitTests: transformHitTests,
  105. child: child,
  106. );
  107. }
  108. }