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

Flutter实现含图标计时器的左侧按钮与右侧提交按钮的行布局及弹窗

实现Flutter Row布局与底部弹窗方案

整体布局框架

先搭好核心的Row结构,让左右两个按钮分别靠向屏幕两端,再给整体加些内边距避免贴边:

Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
  children: [
    // 左侧带计时器的播放/暂停按钮
    const TimerControlButton(),
    // 右侧提交按钮
    _buildSubmitButton(),
  ],
)

左侧播放/暂停+计时器按钮

这个按钮需要维护播放状态和剩余时间,所以用StatefulWidget来实现:

class TimerControlButton extends StatefulWidget {
  const TimerControlButton({super.key});

  @override
  State<TimerControlButton> createState() => _TimerControlButtonState();
}

class _TimerControlButtonState extends State<TimerControlButton> {
  int _remainingSeconds = 20 * 60; // 初始20分钟
  bool _isPlaying = false;
  Timer? _countdownTimer;

  // 把秒数格式化为 mm:ss 样式
  String _formatTime(int seconds) {
    final minutes = (seconds ~/ 60).toString().padLeft(2, '0');
    final secs = (seconds % 60).toString().padLeft(2, '0');
    return '$minutes:$secs';
  }

  // 切换播放/暂停逻辑
  void _toggleTimer() {
    setState(() => _isPlaying = !_isPlaying);
    
    if (_isPlaying) {
      _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
        setState(() {
          if (_remainingSeconds > 0) {
            _remainingSeconds--;
          } else {
            _countdownTimer?.cancel();
            _isPlaying = false;
          }
        });
      });
    } else {
      _countdownTimer?.cancel();
    }
    
    // 触发底部弹窗
    _showBottomSheet();
  }

  // 显示底部弹窗
  void _showBottomSheet() {
    showModalBottomSheet(
      context: context,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
      ),
      builder: (context) {
        return Container(
          padding: const EdgeInsets.all(16),
          height: 220,
          child: const Center(
            child: Text('这里可以放弹窗的自定义内容哦'),
          ),
        );
      },
    );
  }

  @override
  void dispose() {
    _countdownTimer?.cancel(); // 页面销毁时取消计时器,防止内存泄漏
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return TextButton(
      onPressed: _toggleTimer,
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(_isPlaying ? Icons.pause_rounded : Icons.play_arrow_rounded),
          const SizedBox(width: 8),
          Text(_formatTime(_remainingSeconds)),
        ],
      ),
    );
  }
}

右侧提交按钮

逻辑很直接,点击后直接唤起底部弹窗即可:

Widget _buildSubmitButton() {
  return ElevatedButton(
    onPressed: () {
      showModalBottomSheet(
        context: context,
        shape: const RoundedRectangleBorder(
          borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
        ),
        builder: (context) {
          return Container(
            padding: const EdgeInsets.all(16),
            height: 220,
            child: const Center(
              child: Text('提交操作的弹窗内容'),
            ),
          );
        },
      );
    },
    child: const Text('提交'),
  );
}

小提示

  • 弹窗的样式和内容可以完全自定义,比如加表单、操作按钮或者滚动视图
  • 如果需要重置计时器,可以给按钮加个长按逻辑或者额外的重置按钮,直接把_remainingSeconds设回20*60即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:12:28