You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter带动画showBottomSheet不显示问题求助

问题描述

我想在多个Button组件的onPressed属性中使用带动画效果的showBottomSheet功能,但尝试实现后,底部弹窗并未显示,程序无报错直接结束。调试时流程卡在以下代码段:

class CustomShowBottomSheet extends Stateful Widget {
  const CustomShowBottomSheet({super.key, required this.mainWidget});

请问如何实现带动画效果的底部弹窗显示?烦请给出解决建议,谢谢。


相关代码

main.dart

import 'package:flutter/material.dart';
import 'custom_show_bottom_sheet.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Home(),
    );
  }
}

class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
            child: TextButton(
      onPressed: () => CustomShowBottomSheet(
        mainWidget: Container(height: 200, color: Colors.amber),
      ),
      child: const Text('show'),
    )));
  }
}

custom_show_bottom_sheet.dart

import 'package:flutter/material.dart';

class CustomShowBottomSheet extends StatefulWidget {
  const CustomShowBottomSheet({super.key, required this.mainWidget});

  final Widget mainWidget;

  @override
  State<CustomShowBottomSheet> createState() => _CustomShowBottomSheetState();
}

class _CustomShowBottomSheetState extends State<CustomShowBottomSheet>
    with TickerProviderStateMixin {
  late AnimationController animationController;
  static final scaffoldKey = GlobalKey<ScaffoldState>();

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: scaffoldKey,
      body: GestureDetector(
        onTap: showCustomBottomSheet,
        child: widget.mainWidget,
      ),
    );
  }

  void showCustomBottomSheet() {
    showModalBottomSheet<void>(
      context: context,
      builder: (BuildContext context) {
   
        return SlideTransition(
          position: Tween<Offset>(
            begin: const Offset(0, 1),
            end: Offset.zero,
          ).animate(animationController),
          child: widget.mainWidget,
        );
      },
    );
  }
}

解决建议

核心问题分析

  1. 错误调用Widget实例:在TextButton的onPressed里直接创建CustomShowBottomSheet实例,只是生成了一个Widget对象,没有插入到Widget树,也没触发弹窗逻辑,所以无弹窗行为。
  2. 组件设计偏差:CustomShowBottomSheet被定义为StatefulWidget,但你的需求是可复用的弹窗工具,而非页面级组件,设计不符合使用场景。
  3. 动画未触发:即使弹窗显示,AnimationController没调用forward()方法,弹窗会停在初始位置。

修正方案

将CustomShowBottomSheet改成静态工具类/函数,封装带动画的弹窗逻辑,可在任意onPressed中调用:

1. 重构弹窗工具类

import 'package:flutter/material.dart';

class AnimatedBottomSheet {
  static void show(BuildContext context, Widget content) {
    showModalBottomSheet(
      context: context,
      backgroundColor: Colors.transparent,
      builder: (context) {
        return _AnimatedBottomSheetContent(content: content);
      },
    );
  }
}

class _AnimatedBottomSheetContent extends StatefulWidget {
  final Widget content;

  const _AnimatedBottomSheetContent({required this.content});

  @override
  State<_AnimatedBottomSheetContent> createState() => _AnimatedBottomSheetContentState();
}

class _AnimatedBottomSheetContentState extends State<_AnimatedBottomSheetContent> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<Offset> _offsetAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 300),
      vsync: this,
    );
    _offsetAnimation = Tween<Offset>(
      begin: const Offset(0, 1),
      end: Offset.zero,
    ).animate(CurvedAnimation(
      parent: _controller,
      curve: Curves.easeOutCubic,
    ));

    // 触发入场动画
    _controller.forward();
  }

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

  @override
  Widget build(BuildContext context) {
    return SlideTransition(
      position: _offsetAnimation,
      child: Container(
        decoration: const BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
        ),
        child: widget.content,
      ),
    );
  }
}

2. 在按钮中调用

修改Home组件的onPressed逻辑:

class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
            child: TextButton(
      onPressed: () {
        AnimatedBottomSheet.show(
          context,
          Container(height: 200, color: Colors.amber),
        );
      },
      child: const Text('show'),
    )));
  }
}

额外优化点

  • 给动画添加曲线(如Curves.easeOutCubic),让过渡更自然
  • 设置弹窗背景为透明,避免默认灰色背景干扰动画
  • 可监听弹窗关闭事件,调用_controller.reverse()实现退场动画

内容的提问来源于stack exchange,提问作者sub

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 21:56:01