Search code examples
flutterdartflutter-layoutflutter-animation

How to animate Align without using AnimatedAlign and control the animation


I am trying to achieve a pretty simple animation in Flutter but I am stuck.

The current animation looks like this (iPhone Screen recording): https://drive.google.com/file/d/10pzUadQrSr85eLLpyh7v3lt-x8XjFNec/view?usp=sharing

I am currently doing this with Positioned but I can not do it precisely as I want. Is there any chance that I can do this animation with Align Widget.

I have tried using AnimatedAlign and Tweens but none of them allow me to modify the Images position by scrolling up or down on the bottom sheet.

The Code:

late AnimationController bottomSheetController;

double bsOffset = 1;

@override
void initState() {
  super.initState();
  bottomSheetController = BottomSheet.createAnimationController(this);
  bottomSheetController.duration = Duration(milliseconds: 200);

  bottomSheetController.addListener(() {
    double size = (bottomSheetController.value.toDouble() - 1) * -1;

    setState(() {
      if (size != 0) {
        bsOffset = size;
      }
    });
  });
}
.
.
.
void _handleFABPressed() {
  showModalBottomSheet(
    backgroundColor: Colors.transparent,
    barrierColor: Colors.transparent,
    transitionAnimationController: bottomSheetController,
    context: context,
    builder: (context) {
      return Popover(
        child: Container(
          height: 400,
          // color: Colors.lightBlue,
        ),
      );
    },
  );
}
.
.
.
child: Stack(
  alignment: Alignment.topCenter,
  children: [
    Positioned(
      bottom: 50 - (50 * bsOffset),
      left: ((200 / 2) - 20) - (((200 / 2) - 20) * (bsOffset - 1) * -1),
      child: Image.asset(
        'assets/images/8.png',
        width: 200,
      ),
    ),
  ],
),

Thank You!


Solution

  • If I were to move the two widgets, both Align and the bottomsheet, I would do it like this:

      void main() {
        runApp(const MyApp());
      }
    
      class MyApp extends StatelessWidget {
        const MyApp({Key? key}) : super(key: key);
    
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: const MyHomePage(),
          );
        }
      }
    
      class MyHomePage extends StatefulWidget {
        const MyHomePage({Key? key}) : super(key: key);
    
        @override
        State<MyHomePage> createState() => _MyHomePageState();
      }
    
      class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
        late AnimationController _animationController;
        late Animation<Offset> _animationOffset;
        var _alignment = Alignment.bottomCenter;
    
        void _show() {
          showModalBottomSheet(
              backgroundColor: Colors.transparent,
              barrierColor: Colors.transparent,
              transitionAnimationController: _animationController,
              context: context,
              builder: (context) {
                return Container(
                  height: 400,
                  color: Colors.red,
                );
              });
        }
    
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            appBar: AppBar(
              title: const Text('Flutter Demo Home Page'),
            ),
            body: Stack(
              children: [
                Align(
                    alignment: _alignment,
                    child: const SizedBox(
                        width: 100, height: 100, child: Card(color: Colors.red)))
              ],
            ),
            floatingActionButton:
                FloatingActionButton(onPressed: _show, child: const Icon(Icons.add)),
          );
        }
    
        void _onListenerAnimation() {
          setState(() {
            _alignment =
                Alignment(_animationOffset.value.dx, _animationOffset.value.dy);
          });
        }
    
        @override
        void initState() {
          super.initState();
          _animationController = AnimationController(
              vsync: this, duration: const Duration(milliseconds: 200))
            ..addListener(_onListenerAnimation);
    
          _animationOffset = Tween<Offset>(
            begin: const Offset(0, 1),
            end: const Offset(-1, -0.6),
          ).animate(_animationController);
        }
    
        @override
        void dispose() {
          super.dispose();
          _animationController
            ..removeListener(_onListenerAnimation)
            ..dispose();
        }
      }