dart Flutter:按钮在定位小部件onPressed()内不工作

bbuxkriu  于 2023-05-11  发布在  Flutter
关注(0)|答案(2)|浏览(202)

我试图实现一个WhatsApp一样的聊天框与语音消息功能完全一样的WhatsApp,语音消息的UI设计工作正常,但删除,发送,暂停按钮内的定位部件不工作(这些按钮在录制音频时使用的锁定概念完全在WhatsApp中)当我点击这些按钮或在定位小部件内键盘弹出底层消息框时,我如何才能避免这种情况,并使按钮工作时,点击。下面是我的代码
主箭头:

class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
late AnimationController controller;

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

@override
void dispose() {
    controller.dispose();
    super.dispose();
}

@override
Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(
        title: const Text("Audio Chat"),
    ),
    body: Padding(
        padding: const EdgeInsets.all(Globals.defaultPadding),
        child: Column(
        children: [
            const Expanded(child: AudioList()),
            Row(
            mainAxisSize: MainAxisSize.max,
            children: [
                ChatBox(controller: controller),
                const SizedBox(width: 4),
                RecordButton(controller: controller),
            ],
            ),
        ],
        ),
    ),
    );
}
}

record_button.dart:

class _RecordButtonState extends State<RecordButton> {
static const double size = 55;

final double lockerHeight = 200;
double timerWidth = 0;

late Animation<double> buttonScaleAnimation;
late Animation<double> timerAnimation;
late Animation<double> lockerAnimation;

DateTime? startTime;
Timer? timer;
String recordDuration = "00:00";
late Record record;

bool isLocked = false;
bool showLottie = false;

@override
void initState() {
super.initState();
buttonScaleAnimation = Tween<double>(begin: 1, end: 2).animate(
  CurvedAnimation(
    parent: widget.controller,
    curve: const Interval(0.0, 0.6, curve: Curves.elasticInOut),
  ),
);
widget.controller.addListener(() {
  setState(() {});
});
}

@override
void didChangeDependencies() {
super.didChangeDependencies();
timerWidth = MediaQuery.of(context).size.width - 2 * Globals.defaultPadding - 4 + 5;
timerAnimation =
    Tween<double>(begin: timerWidth + Globals.defaultPadding, end: 0)
        .animate(
      CurvedAnimation(
        parent: widget.controller,
        curve: const Interval(0.2, 1, curve: Curves.easeIn),
      ),
    );
lockerAnimation =
    Tween<double>(begin: lockerHeight + Globals.defaultPadding, end: 0)
        .animate(
      CurvedAnimation(
        parent: widget.controller,
        curve: const Interval(0.2, 1, curve: Curves.easeIn),
      ),
    );
}

@override
void dispose() {
record.dispose();
timer?.cancel();
timer = null;
super.dispose();
}

@override
Widget build(BuildContext context) {
return Stack(
  clipBehavior: Clip.none,
  children: [
    lockSlider(),
    cancelSlider(),
    audioButton(),
    if (isLocked) timerLocked(),
  ],
);
}

Widget lockSlider() {
return Positioned(
  bottom: -lockerAnimation.value,
  child: Container(
    height: lockerHeight,
    width: size,
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(Globals.borderRadius),
      color: Colors.black,
    ),
    padding: const EdgeInsets.symmetric(vertical: 15),
    child: Column(
      mainAxisAlignment: MainAxisAlignment.start,
      children: [
        const Icon(FontAwesomeIcons.lock, size: 20),
        const SizedBox(height: 8),
        FlowShader(
          direction: Axis.vertical,
          child: Column(
            children: const [
              Icon(Icons.keyboard_arrow_up),
              Icon(Icons.keyboard_arrow_up),
              Icon(Icons.keyboard_arrow_up),
            ],
          ),
        ),
      ],
    ),
  ),
);
}

Widget cancelSlider() {
return Positioned(
  right: -timerAnimation.value,
  child: Container(
    height: size,
    width: timerWidth,
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(Globals.borderRadius),
      color: Colors.black,
    ),
    child: Padding(
      padding: const EdgeInsets.symmetric(horizontal: 15),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        mainAxisSize: MainAxisSize.max,
        children: [
          showLottie ? const LottieAnimation() : Text(recordDuration),
          const SizedBox(width: size),
          FlowShader(
            child: Row(
              children: const [
                Icon(Icons.keyboard_arrow_left),
                Text("Slide to cancel")
              ],
            ),
            duration: const Duration(seconds: 3),
            flowColors: const [Colors.white, Colors.grey],
          ),
          const SizedBox(width: size),
        ],
      ),
    ),
  ),
);
}

Widget timerLocked() {
return Positioned(
  right: 0,
  bottom: 0,
  child: Container(
    height: 80,
    width: timerWidth,
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(Globals.borderRadius - 10),
      color: Colors.lightBlue,
    ),
    child: Padding(
      padding: const EdgeInsets.only(left: 15, right: 25),
      child: Column(
        children: [
          Flexible(
            flex: 1,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              mainAxisSize: MainAxisSize.max,
              children: [
                Text(recordDuration),
                FlowShader(
                  child: const Text("Tap lock to stop"),
                  duration: const Duration(seconds: 3),
                  flowColors: const [Colors.white, Colors.grey],
                ),
                GestureDetector(
                  behavior: HitTestBehavior.opaque,
                  onTap: () async {
                    print('Recording finished');
                  },
                  child: const Center(
                    child: Icon(
                      FontAwesomeIcons.check,
                      size: 18,
                      color: Colors.black,
                    ),
                  ),
                ),
              ],
            ),
          ),
          Flexible(
            flex: 1,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              mainAxisSize: MainAxisSize.max,
              children: [
                Center(
                  child: InkWell(
                    onTap: () {
                      print('Action Delete');
                    },
                    child: Icon(FontAwesomeIcons.trash,size: 18,color: Colors.black,),
                  ),
                ),
                GestureDetector(
                  behavior: HitTestBehavior.opaque,
                  onTap: () async {
                    print('Action Pause');
                  },
                  child: const Center(
                    child: Icon(
                      FontAwesomeIcons.pause,
                      size: 18,
                      color: Colors.black,
                    ),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    ),
  ),
);
}

Widget audioButton() {
return GestureDetector(
  child: Transform.scale(
    scale: buttonScaleAnimation.value,
    child: Container(
      child: const Icon(Icons.mic),
      height: size,
      width: size,
      clipBehavior: Clip.hardEdge,
      decoration: BoxDecoration(
        shape: BoxShape.circle,
        color: Theme.of(context).primaryColor,
      ),
    ),
  ),
  onLongPressDown: (_) {
    widget.controller.forward();
  },
  onLongPressEnd: (details) async {

    if (isCancelled(details.localPosition, context)) {

    } else if (checkIsLocked(details.localPosition)) {
      widget.controller.reverse();

      Vibrate.feedback(FeedbackType.heavy);
      setState(() {
        isLocked = true;
      });
    } else {
      print('Recording Finished');
    }
  },
  onLongPressCancel: () {
    widget.controller.reverse();
  },
  onLongPress: () async {
    Vibrate.feedback(FeedbackType.success);
    if (await Record().hasPermission()) {
      record = Record();
      await record.start(
        path: Globals.documentPath +
            "audio_${DateTime.now().millisecondsSinceEpoch}.m4a",
        encoder: AudioEncoder.aacEld,
        bitRate: 128000,
        samplingRate: 44100,
      );
      startTime = DateTime.now();
      timer = Timer.periodic(const Duration(seconds: 1), (_) {
        final minDur = DateTime.now().difference(startTime!).inMinutes;
        final secDur = DateTime.now().difference(startTime!).inSeconds % 60;
        String min = minDur < 10 ? "0$minDur" : minDur.toString();
        String sec = secDur < 10 ? "0$secDur" : secDur.toString();
        setState(() {
          recordDuration = "$min:$sec";
        });
      });
    }
  },
);
}

bool checkIsLocked(Offset offset) {
return (offset.dy < -35);
}

bool isCancelled(Offset offset, BuildContext context) {
return (offset.dx < -(MediaQuery.of(context).size.width * 0.2));
}
}

更新:
到目前为止,我找到的唯一解决方法是用Stack,SizedBox,Positioned封装timerLocked Positioned小部件,如下所示。

Widget timerLocked() {
return Positioned(
  child: SizedBox(
    height: 80,
    width: timerWidth,
    child: Stack(
      children: [
        Positioned(
        right: 0,
        bottom: 0,
        height: 80,
        width: timerWidth,
        child: Container(
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(Globals.borderRadius - 10),
          color: Colors.lightBlue,
        ),
        child: Padding(
          padding: const EdgeInsets.only(left: 15, right: 25),
          child: Column(
            children: [
              
            ],
          ),
        ),),
        ),
      ],),),);
}
ilmyapht

ilmyapht1#

要解决这个问题,我们需要用IgnorePointer小部件 Package 按钮,并确保手势不会传递给底层小部件。

// ...
//...
Flexible(
      flex: 1,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        mainAxisSize: MainAxisSize.max,
        children: [
          IgnorePointer(
            ignoring: false,
            child: Center(
              child: InkWell(
                onTap: () {
                  print('Action Delete');
                },
                child: Icon(
                  FontAwesomeIcons.trash,
                  size: 18,
                  color: Colors.black,
                ),
              ),
            ),
          ),
          IgnorePointer(
            ignoring: false,
            child: GestureDetector(
              behavior: HitTestBehavior.opaque,
              onTap: () async {
                print('Action Pause');
              },
              child: const Center(
                child: Icon(
                  FontAwesomeIcons.pause,
                  size: 18,
                  color: Colors.black,
                ),
              ),
            ),
          ),
        ],
      ),
    );
6jjcrrmo

6jjcrrmo2#

最后我找到了一个解决方案.我用ExpandTapWidget(ExpandTapWidget)替换了Positioned小部件,解决了问题.这是一个bug/documentaion问题,并发布了here,帮助我解决了这个问题.下面是代码,

Widget timerLocked() {
  return ExpandTapWidget(
  onTap: () {
    print('********** ExpandTapWidget Clicked ********');
  },
  tapPadding: EdgeInsets.all(1),
  child: Container(
          height: 80,
          width: timerWidth,
          decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(Globals.borderRadius - 10),
          color: Colors.lightBlue,
          ),
          child: Padding(
          padding: const EdgeInsets.only(left: 15, right: 25),
          child: Column(
            children: [
              
            ],
          ),
          ),),
        );
}

相关问题