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:当前组件和子组件都可以响应手势
相关推荐
whysqwhw1 小时前
安卓内存优化
android
落魄的Android开发1 小时前
FLutter 如何在跨平台下实现国际化多语言开发
flutter
用户2018792831671 小时前
TabLayout禁止滑动 + 左对齐排列实现
android
whysqwhw2 小时前
安卓Drawable分类
android
Andy_GF2 小时前
纯血鸿蒙HarmonyOS Next 远程测试包分发
前端·ios·harmonyos
libo_20253 小时前
HarmonyOS5原生开发 vs. Flutter:谁更适合你的项目?
flutter
_祝你今天愉快3 小时前
SparseArray & ArrayMap
android·数据结构
归辞...3 小时前
「iOS」————自动释放池底层原理
macos·ios·cocoa
libo_20253 小时前
ArkTS还是Flutter?HarmonyOS5开发框架选型指南
flutter
libo_20253 小时前
Flutter开发者在HarmonyOS5生态中的优势与局限
flutter