简体   繁体   中英

Flutter transform animation breaks “on pressed” function of floating action button in bottom nav bar. Workaround needed

I'm currently trying to implement a radial menu appearing when the floating action button in the bottom navigation menu is clicked (image1). The animation and rendering works fine but after the animation, the on pressed button function is no even triggered, when the buttons get clicked. I already read that this is due to the stack, wrapping the buttons and the animation. The area of the transformed buttons is not defined a priori. This causes gesture detection of the Buttons to stay behind the FAB. Wrapping the Stack with a fixed size container solves the problem, however, it totally breaks the layout. Also, the clickable navigations icons in the background are not reachable as the container is laying above the navbar (see image2). Is there a known workaround to fix this problem? I am really looking forward to getting some professional help. :)

Image of not working buttons and desired layout

Image of the broken UI after wrapping the stack with a container. But in this solution button pressed is working

The code is attached here:

import 'package:flutter/material.dart';
import 'dart:math';
import 'package:vector_math/vector_math.dart' show radians;

class RadialFloatingActionButton extends StatefulWidget {
  @override
  _RadialFloatingActionButtonState createState() => _RadialFloatingActionButtonState();
 }

class _RadialFloatingActionButtonState extends State<RadialFloatingActionButton> with 
      SingleTickerProviderStateMixin {
      AnimationController controller;

      @override
      void initState() {
        super.initState();
        controller = AnimationController(
            duration: Duration(milliseconds: 1100), vsync: this);
      }

      @override
      Widget build(BuildContext context) {
        return RadialAnimation(controller: controller);
      }
    }

    class RadialAnimation extends StatelessWidget {
      RadialAnimation({Key key, this.controller})
          : scale = Tween<double>(
              begin: 1.5,
              end: 0.0,
            ).animate(
              CurvedAnimation(parent: controller, curve: Curves.elasticInOut),
             ),
            translation = Tween<double>(
              begin: 0.0,
              end: 90.0,
            ).animate(
              CurvedAnimation(parent: controller, curve: Curves.easeInOutCirc),
            ),
            super(key: key);

      final AnimationController controller;
      final Animation<double> scale;
      final Animation<double> translation;

      build(context) {
        return AnimatedBuilder(
             animation: controller,
             builder: (context, builder) {
              return Stack(alignment: Alignment.center, children: [
                _buildButton(200, color: Colors.black, emoji: "A"),
                _buildButton(245, color: Colors.black, emoji: "B"),
                _buildButton(295, color: Colors.black, emoji: "C"),
                _buildButtonMore(340, color: Colors.grey),
                Transform.scale(
                  scale: scale.value -
                      1.5, // subtract the beginning value to run the opposite animation
                  child: FloatingActionButton(
                      child: Icon(
                        Icons.close,
                      ),
                      onPressed: _close,
                      backgroundColor: Colors.black),
                 ),
                Transform.scale(
                  scale: scale.value,
                  child: FloatingActionButton(
                      child: Icon(
                        Icons.people,
                        color: Colors.white,
                      ),
                      onPressed: _open,
                      backgroundColor: Colors.black),
                 )
              ]);
            });
      }

      _buildButtonMore(double angle, {Color color}) {
        final double rad = radians(angle);
        return Transform(
            transform: Matrix4.identity()
              ..translate(
                  (translation.value) * cos(rad), (translation.value) * sin(rad)),
            child: FloatingActionButton(
                child: Icon(Icons.more_horiz),
                backgroundColor: color,
                onPressed: _close,
                elevation: 0));
      }

      _buildButton(double angle, {Color color, String emoji}) {
        final double rad = radians(angle);
        return Transform(
            transform: Matrix4.identity()
              ..translate(
                  (translation.value) * cos(rad), (translation.value) * sin(rad)),
            child: FloatingActionButton(
                child: Center(
                  child: new Text(
                    emoji,
                    style: TextStyle(fontSize: 30),
                    textAlign: TextAlign.center,
                  ),
                 ),
                backgroundColor: color,
                onPressed: _close,
                elevation: 0));
      }

      _open() {
        controller.forward();
      }

      _close() {
        controller.reverse();
      }
    }

I am not able to test right now, but wrapping in a container seems like the proper workaround, as Transforms seems to not apply to hit test behaviors in unbounding boxes (thats why Container makes it work). Maybe there's a way (some sort of widget or property) that allows hitTestBehavior like GestureDetector where you can set it to “opaque areas” instead the entire bounding box of the Container capturing the touches. Sorry cause i cant provide you an answer directly, will check it out later on the computer:)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM