flutter学习第 6 节:按钮与交互组件

交互是移动应用的核心功能之一,而按钮和各种交互组件则是实现用户交互的基础。在 Flutter 中,提供了丰富的内置交互组件,能够满足各种场景下的用户交互需求。本节课将详细介绍 Flutter 中常用的按钮组件、其他交互控件以及手势识别机制,帮助你构建响应式的用户界面。

一、常用按钮组件

Flutter 提供了几种预定义的按钮组件,它们遵循 Material Design 设计规范,适用于不同的界面场景。

1. ElevatedButton(悬浮按钮)

ElevatedButton 是带有阴影和背景色的按钮,点击时会有悬浮效果,适合用于主要操作。

dart 复制代码
ElevatedButton(
  // 按钮点击回调
  onPressed: () {
    print("ElevatedButton 被点击了");
  },
  // 按钮文本内容
  child: const Text("提交"),
  // 按钮样式
  style: ElevatedButton.styleFrom(
    // 背景颜色
    backgroundColor: Colors.blue,
    // 文本颜色
    foregroundColor: Colors.white,
    // 按钮内边距
    padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
    // 字体大小
    textStyle: const TextStyle(fontSize: 16),
    // 圆角
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(8),
    ),
    // 阴影
    elevation: 4,
  ),
)

onPressednull 时,按钮会处于禁用状态:

dart 复制代码
ElevatedButton(
  onPressed: null, // 禁用按钮
  child: const Text("禁用状态"),
)

2. TextButton(文本按钮)

TextButton 是没有背景色和阴影的文本按钮,仅包含文本和点击效果,适合用于次要操作。

dart 复制代码
TextButton(
  onPressed: () {
    print("TextButton 被点击了");
  },
  child: const Text("取消"),
  style: TextButton.styleFrom(
    foregroundColor: Colors.grey[700], // 文本颜色
    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
    textStyle: const TextStyle(fontSize: 14),
  ),
)

3. OutlinedButton(轮廓按钮)

OutlinedButton 是带有边框的按钮,没有背景色,适合用于 tertiary(第三级)操作。

dart 复制代码
OutlinedButton(
  onPressed: () {
    print("OutlinedButton 被点击了");
  },
  child: const Text("查看详情"),
  style: OutlinedButton.styleFrom(
    foregroundColor: Colors.blue, // 文本和边框颜色
    side: const BorderSide(color: Colors.blue), // 边框样式
    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(4),
    ),
  ),
)

4. 带图标的按钮

所有按钮都可以通过 child 属性添加图标,实现图文结合的效果:

dart 复制代码
// 带图标的 ElevatedButton
ElevatedButton.icon(
  onPressed: () {},
  icon: const Icon(Icons.save, size: 18), // 图标
  label: const Text("保存"), // 文本
)

// 带图标的 TextButton
TextButton.icon(
  onPressed: () {},
  icon: const Icon(Icons.share, size: 18),
  label: const Text("分享"),
)

// 带图标的 OutlinedButton
OutlinedButton.icon(
  onPressed: () {},
  icon: const Icon(Icons.delete, size: 18),
  label: const Text("删除"),
)

5. 按钮主题统一管理

为了保持应用中按钮样式的一致性,可以使用 ThemeButtonTheme 统一配置按钮样式:

dart 复制代码
Theme(
  data: ThemeData(
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        backgroundColor: Colors.purple,
        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(8),
        ),
      ),
    ),
  ),
  child: Column(
    children: [
      ElevatedButton(
        onPressed: () {},
        child: const Text("按钮1"), // 会应用主题样式
      ),
      ElevatedButton(
        onPressed: () {},
        child: const Text("按钮2"), // 会应用主题样式
      ),
    ],
  ),
)

二、按钮点击事件与状态变化

按钮的核心功能是响应用户的点击操作,通过 onPressed 回调实现。同时,按钮的状态会随着交互发生变化。

1. 基本点击事件

onPressed 是按钮最基本的回调函数,当用户点击按钮时触发:

dart 复制代码
ElevatedButton(
  onPressed: () {
    // 处理点击事件
    print("按钮被点击");
    // 可以在这里更新状态、导航到新页面或执行其他操作
  },
  child: const Text("点击我"),
)

2. 长按事件

除了点击,按钮还支持长按事件,通过 onLongPress 回调实现:

dart 复制代码
ElevatedButton(
  onPressed: () {
    print("点击事件");
  },
  onLongPress: () {
    print("长按事件");
  },
  child: const Text("长按试试"),
)

3. 按钮状态管理

按钮的状态(启用 / 禁用、加载中)通常需要根据应用状态动态变化:

dart 复制代码
import 'package:flutter/material.dart';

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

  @override
  State<ButtonStateExample> createState() => _ButtonStateExampleState();
}

class _ButtonStateExampleState extends State<ButtonStateExample> {
  bool _isLoading = false;
  bool _isEnabled = true;

  void _handleSubmit() async {
    // 禁用按钮并显示加载状态
    setState(() {
      _isLoading = true;
      _isEnabled = false;
    });

    // 模拟网络请求
    await Future.delayed(const Duration(seconds: 2));

    // 恢复按钮状态
    setState(() {
      _isLoading = false;
      _isEnabled = true;
    });
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _isEnabled ? _handleSubmit : null,
      child: _isLoading
          ? const Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                CircularProgressIndicator(color: Colors.white, strokeWidth: 2),
                SizedBox(width: 8),
                Text("处理中..."),
              ],
            )
          : const Text("提交"),
    );
  }
}

三、其他交互组件

除了按钮,Flutter 还提供了多种常用的交互组件,用于实现表单输入、选择等功能。

1. Checkbox(复选框)

Checkbox 用于多选场景,允许用户选择多个选项:

dart 复制代码
import 'package:flutter/material.dart';

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

  @override
  State<CheckboxExample> createState() => _CheckboxExampleState();
}

class _CheckboxExampleState extends State<CheckboxExample> {
  bool _isChecked = false;

  @override
  Widget build(BuildContext context) {
    return Checkbox(
      value: _isChecked, // 当前选中状态
      onChanged: (value) {
        // 状态变化回调
        setState(() {
          _isChecked = value ?? false;
        });
      },
      activeColor: Colors.blue, // 选中时的颜色
      checkColor: Colors.white, // 勾选图标的颜色
    );
  }
}

通常会与 Text 组合使用,并通过 Row 布局:

dart 复制代码
Row(
  children: [
    Checkbox(
      value: _isChecked,
      onChanged: (value) {
        setState(() => _isChecked = value ?? false);
      },
    ),
    const Text("我已阅读并同意用户协议"),
  ],
)

CheckboxListTile 是一个更完整的组件,包含复选框、文本和图标:

dart 复制代码
CheckboxListTile(
  value: _isChecked,
  onChanged: (value) {
    setState(() => _isChecked = value ?? false);
  },
  title: const Text("接收推送通知"),
  subtitle: const Text("接收最新活动和优惠信息"),
  secondary: const Icon(Icons.notifications),
  controlAffinity: ListTileControlAffinity.leading, // 复选框位置
)

2. Radio(单选按钮)

Radio 用于单选场景,一组单选按钮中只能有一个被选中:

dart 复制代码
import 'package:flutter/material.dart';

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

  @override
  State<RadioExample> createState() => _RadioExampleState();
}

class _RadioExampleState extends State<RadioExample> {
  String? _selectedOption;
  final List<String> _options = ['选项A', '选项B', '选项C'];

  @override
  Widget build(BuildContext context) {
    return Column(
      children: _options.map((option) {
        return RadioListTile(
          title: Text(option),
          value: option,
          groupValue: _selectedOption,
          onChanged: (value) {
            setState(() {
              _selectedOption = value;
            });
          },
        );
      }).toList(),
    );
  }
}

RadioListTile 是包含单选按钮和文本的完整组件,比单独使用 Radio 更方便。

3. Switch(开关)

Switch 用于切换两种状态(开启 / 关闭),通常用于设置项:

dart 复制代码
import 'package:flutter/material.dart';

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

  @override
  State<SwitchExample> createState() => _SwitchExampleState();
}

class _SwitchExampleState extends State<SwitchExample> {
  bool _isEnabled = false;

  @override
  Widget build(BuildContext context) {
    return Switch(
      value: _isEnabled,
      onChanged: (value) {
        setState(() {
          _isEnabled = value;
        });
      },
      activeColor: Colors.green, // 开启状态的颜色
    );
  }
}

SwitchListTile 是包含开关和文本的完整组件:

dart 复制代码
SwitchListTile(
  title: const Text("深色模式"),
  subtitle: const Text("切换到深色显示模式"),
  value: _isDarkMode,
  onChanged: (value) {
    setState(() => _isDarkMode = value);
  },
  secondary: const Icon(Icons.dark_mode),
)

4. Slider(滑块)

Slider 用于在一个范围内选择数值,如音量调节、亮度设置等:

dart 复制代码
import 'package:flutter/material.dart';

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

  @override
  State<SliderExample> createState() => _SliderExampleState();
}

class _SliderExampleState extends State<SliderExample> {
  double _value = 50;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Slider(
          value: _value,
          min: 0, // 最小值
          max: 100, // 最大值
          divisions: 10, // 分成多少等分
          label: "${_value.round()}", // 拖动时显示的标签
          onChanged: (value) {
            setState(() {
              _value = value;
            });
          },
          onChangeStart: (value) {
            print("开始拖动: $value");
          },
          onChangeEnd: (value) {
            print("结束拖动: $value");
          },
        ),
        Text("当前值: ${_value.round()}"),
      ],
    );
  }
}

四、手势识别:GestureDetector

GestureDetector 是一个功能强大的手势识别组件,它可以包裹任何 Widget 并检测各种用户手势,如点击、滑动、长按等。

1. 基本点击手势

dart 复制代码
GestureDetector(
  // 单击
  onTap: () {
    print("单击");
  },
  // 双击
  onDoubleTap: () {
    print("双击");
  },
  // 长按
  onLongPress: () {
    print("长按");
  },
  child: Container(
    width: 100,
    height: 100,
    color: Colors.blue,
    child: const Center(
      child: Text("点我", style: TextStyle(color: Colors.white)),
    ),
  ),
)

2. 滑动手势

GestureDetector 可以检测各种滑动手势,包括水平滑动、垂直滑动等:

dart 复制代码
import 'package:flutter/material.dart';

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

  @override
  State<SwipeExample> createState() => _SwipeExampleState();
}

class _SwipeExampleState extends State<SwipeExample> {
  String _direction = "请滑动";

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      // 滑动开始
      onPanStart: (details) {
        print("滑动开始: ${details.globalPosition}");
      },
      // 滑动更新
      onPanUpdate: (details) {
        // 滑动偏移量
        print("滑动偏移: ${details.delta}");
      },
      // 滑动结束
      onPanEnd: (details) {
        // 滑动速度和方向
        final velocity = details.velocity.pixelsPerSecond;
        setState(() {
          if (velocity.dx.abs() > velocity.dy.abs()) {
            // 水平滑动
            _direction = velocity.dx > 0 ? "向右滑动" : "向左滑动";
          } else {
            // 垂直滑动
            _direction = velocity.dy > 0 ? "向下滑动" : "向上滑动";
          }
        });
      },
      child: Container(
        width: 300,
        height: 200,
        color: Colors.grey[200],
        child: Center(
          child: Text(
            _direction,
            style: const TextStyle(fontSize: 18),
          ),
        ),
      ),
    );
  }
}

3. 缩放手势

GestureDetector 还可以检测缩放手势,实现图片缩放等功能:

dart 复制代码
import 'package:flutter/material.dart';

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

  @override
  State<ScaleExample> createState() => _ScaleExampleState();
}

class _ScaleExampleState extends State<ScaleExample> {
  double _scale = 1.0;
  double _previousScale = 1.0;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onScaleStart: (details) {
        _previousScale = _scale;
      },
      onScaleUpdate: (details) {
        setState(() {
          _scale = _previousScale * details.scale;
          // 限制缩放范围
          _scale = _scale.clamp(0.5, 3.0);
        });
      },
      child: Transform.scale(
        scale: _scale,
        child: Image.network(
          "https://picsum.photos/400/300",
          width: 400,
          height: 300,
          fit: BoxFit.cover,
        ),
      ),
    );
  }
}

4. 手势竞争与优先级

当多个手势可能同时被识别时(如点击和长按),Flutter 有一套手势竞争机制来决定哪个手势应该被响应。可以通过 behavior 属性控制手势检测的行为:

dart 复制代码
GestureDetector(
  behavior: HitTestBehavior.opaque, // 即使子组件透明也能检测手势
  onTap: () {
    print("容器被点击");
  },
  child: Container(
    width: 100,
    height: 100,
    // 透明容器也能响应手势
  ),
)

HitTestBehavior 的取值包括:

  • deferToChild:默认值,子组件优先响应手势
  • opaque:当前组件优先响应手势,即使透明
  • translucent:当前组件和子组件都可以响应手势
相关推荐
FinelyYang2 小时前
UniApp + LiveKit:Android 端为什么要用原生做屏幕共享,以及我们怎么落地的
android·uni-app
蓝速科技2 小时前
蓝速科技信创落地:平衡安全合规与项目成本的实战方案
android·运维·人工智能·科技·安全·电脑·鸿蒙
又见情义5 小时前
RK3568 Android 13 SDK 开机自动开启共享热点实践
android
mmsx5 小时前
return false 不是拦截:一次按键触发两个动作的事件冒泡陷阱
android·bug
DU_YULIN6 小时前
Camera HAL ThreadManager概述
android·camera hal
淡淡的香烟7 小时前
Android中的MQTT通信原理解析
android·物联网
淡淡的香烟7 小时前
Android Camera开发详解
android·音视频
mmsx8 小时前
基于 Android 的校园信息管理系统源码
android·java·okhttp
xcyxiner9 小时前
flutter wsl2安装记录(使用宿主机虚拟机)
前端·flutter
YF02119 小时前
如何进入Android工程模式
android