Skip to content

交互型组件

交互型组件让用户能与 App 进行操作——点击按钮、输入文本、选择选项等。

Button — 按钮

Flutter 提供了多种按钮组件,按视觉风格和使用场景区分。

按钮总览

组件风格典型场景
ElevatedButton有阴影,突出主要操作
FilledButton填充色,Material 3主要操作(推荐)
OutlinedButton边框,不填充次要操作
TextButton无边框,文字按钮文字链接、对话框按钮
IconButton仅图标工具栏操作
FloatingActionButton悬浮圆形页面主要操作

基本用法

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('按钮示例')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // ElevatedButton — 有阴影的按钮
            // ─── ★ 各按钮类型 ──────────────
            ElevatedButton(
              onPressed: () {},
              child: const Text('Elevated'),
            ),
            // ─── ☆ 各按钮类型 ──────────────
            const SizedBox(height: 12),

            // FilledButton — 填充按钮(Material 3 推荐)
            FilledButton(
              onPressed: () {},
              child: const Text('Filled'),
            ),
            const SizedBox(height: 12),

            // OutlinedButton — 边框按钮
            OutlinedButton(
              onPressed: () {},
              child: const Text('Outlined'),
            ),
            const SizedBox(height: 12),

            // TextButton — 文字按钮
            TextButton(
              onPressed: () {},
              child: const Text('Text'),
            ),
            const SizedBox(height: 12),

            // IconButton — 图标按钮
            IconButton(
              onPressed: () {},
              icon: const Icon(Icons.favorite),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        child: const Icon(Icons.add),
      ),
    );
  }
}

按钮状态

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;

  Future<void> _handleSubmit() async {
    setState(() => _isLoading = true);
    await Future.delayed(const Duration(seconds: 2)); // 模拟网络请求
    setState(() => _isLoading = false);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('按钮状态')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 禁用状态
            const ElevatedButton(
              onPressed: null,  // null 表示禁用
              child: Text('不可点击'),
            ),
            const SizedBox(height: 16),

            // 加载状态
            ElevatedButton(
              onPressed: _isLoading ? null : _handleSubmit,
              child: _isLoading
                  ? const SizedBox(
                      width: 16, height: 16,
                      child: CircularProgressIndicator(strokeWidth: 2),
                    )
                  : const Text('提交'),
            ),
          ],
        ),
      ),
    );
  }
}

自定义按钮样式

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('自定义按钮样式')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {},
          style: ElevatedButton.styleFrom(
            backgroundColor: Colors.blue,
            foregroundColor: Colors.white,
            elevation: 4,
            padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(8),
            ),
          ),
          child: const Text('自定义按钮'),
        ),
      ),
    );
  }
}

WidgetState(按钮状态样式)

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('WidgetState 按钮样式')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 方式一:resolveWith
            ElevatedButton(
              onPressed: () {},
              style: ButtonStyle(
                backgroundColor: WidgetStateProperty.resolveWith((states) {
                  if (states.contains(WidgetState.pressed)) return Colors.blue[700];
                  if (states.contains(WidgetState.disabled)) return Colors.grey;
                  return Colors.blue;
                }),
              ),
              child: const Text('resolveWith 按钮'),
            ),
            const SizedBox(height: 16),

            // 方式二:fromMap(Flutter 3.41+ 推荐,更简洁)
            ElevatedButton(
              onPressed: () {},
              style: ButtonStyle(
                backgroundColor: WidgetStateProperty.fromMap({
                  WidgetState.pressed: Colors.blue[700],
                  WidgetState.disabled: Colors.grey,
                  WidgetState.any: Colors.blue,
                }),
              ),
              child: const Text('fromMap 按钮'),
            ),
          ],
        ),
      ),
    );
  }
}

TextField — 文本输入

TextField 是最常用的输入组件,用于接收用户输入的文字。

基本用法

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('TextField 示例')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: TextField(
          decoration: const InputDecoration(
            hintText: '请输入用户名',
            prefixIcon: Icon(Icons.person),
          ),
          onChanged: (value) {
            print('输入内容: $value');
          },
        ),
      ),
    );
  }
}

InputDecoration — 输入框装饰

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('InputDecoration 示例')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: TextField(
          decoration: InputDecoration(
            labelText: '用户名',
            hintText: '请输入用户名',
            prefixIcon: const Icon(Icons.person),
            suffixIcon: const Icon(Icons.clear),
            border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(8),
            ),
            filled: true,
            fillColor: Colors.grey[100],
            counterText: '0/20',
          ),
        ),
      ),
    );
  }
}

TextEditingController

TextEditingController 控制输入框的值:

dart
class MyForm extends StatefulWidget {
  @override
  State<MyForm> createState() => _MyFormState();
}

class _MyFormState extends State<MyForm> {
  final _controller = TextEditingController();

  @override
  void dispose() {
    _controller.dispose();  // 必须释放
    super.dispose();
  }

  void _clear() {
    _controller.clear();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(
          controller: _controller,
          decoration: InputDecoration(
            hintText: '请输入',
            suffixIcon: IconButton(
              icon: Icon(Icons.clear),
              onPressed: _clear,
            ),
          ),
        ),
        Text('当前输入: ${_controller.text}'),
      ],
    );
  }
}

常用属性

属性说明
controller控制器
obscureText是否隐藏文字(密码)
maxLines最大行数(null 为不限)
maxLength最大字符数
keyboardType键盘类型
textInputAction键盘回车键行为
readOnly是否只读
enabled是否可用

键盘类型

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('键盘类型')),
      body: const Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          children: [
            TextField(keyboardType: TextInputType.text, decoration: InputDecoration(labelText: '默认')),
            SizedBox(height: 16),
            TextField(keyboardType: TextInputType.number, decoration: InputDecoration(labelText: '数字')),
            SizedBox(height: 16),
            TextField(keyboardType: TextInputType.emailAddress, decoration: InputDecoration(labelText: '邮箱')),
            SizedBox(height: 16),
            TextField(keyboardType: TextInputType.phone, decoration: InputDecoration(labelText: '电话')),
          ],
        ),
      ),
    );
  }
}

Form / TextFormField — 表单

Form + TextFormField 提供了完整的表单验证功能。

基本用法

dart
class LoginForm extends StatefulWidget {
  @override
  State<LoginForm> createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          TextFormField(
            decoration: InputDecoration(labelText: '邮箱'),
            validator: (value) {
              if (value == null || value.isEmpty) return '请输入邮箱';
              if (!value.contains('@')) return '邮箱格式不正确';
              return null;
            },
          ),
          TextFormField(
            decoration: InputDecoration(labelText: '密码'),
            obscureText: true,
            validator: (value) {
              if (value == null || value.length < 6) return '密码至少6位';
              return null;
            },
          ),
          SizedBox(height: 16),
          ElevatedButton(
            onPressed: () {
              if (_formKey.currentState!.validate()) {
                // 验证通过,提交表单
              }
            },
            child: Text('登录'),
          ),
        ],
      ),
    );
  }
}

Dialog — 对话框

AlertDialog

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

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

  void _showDeleteDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('确认删除'),
        content: const Text('删除后不可恢复,确定继续吗?'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('取消'),
          ),
          FilledButton(
            onPressed: () {
              Navigator.pop(context);
            },
            child: const Text('删除'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Dialog 示例')),
      body: Center(
        child: ElevatedButton(
          onPressed: () => _showDeleteDialog(context),
          child: const Text('显示对话框'),
        ),
      ),
    );
  }
}

简单对话框

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

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

  Future<void> _showConfirmDialog(BuildContext context) async {
    final confirmed = await showDialog<bool>(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('退出'),
        content: const Text('确定退出吗?'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context, false),
            child: const Text('取消'),
          ),
          TextButton(
            onPressed: () => Navigator.pop(context, true),
            child: const Text('确定'),
          ),
        ],
      ),
    );
    if (confirmed == true) {
      // 用户确认
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('确认对话框')),
      body: Center(
        child: ElevatedButton(
          onPressed: () => _showConfirmDialog(context),
          child: const Text('显示确认对话框'),
        ),
      ),
    );
  }
}

BottomSheet — 底部弹窗

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

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

  void _showBottomSheet(BuildContext context) {
    showModalBottomSheet(
      context: context,
      builder: (context) => Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text('选择操作', style: Theme.of(context).textTheme.titleMedium),
            const SizedBox(height: 16),
            ListTile(
              leading: const Icon(Icons.camera_alt),
              title: const Text('拍照'),
              onTap: () => Navigator.pop(context),
            ),
            ListTile(
              leading: const Icon(Icons.photo_library),
              title: const Text('从相册选择'),
              onTap: () => Navigator.pop(context),
            ),
          ],
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('BottomSheet 示例')),
      body: Center(
        child: ElevatedButton(
          onPressed: () => _showBottomSheet(context),
          child: const Text('显示底部弹窗'),
        ),
      ),
    );
  }
}

SnackBar — 提示消息

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

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

  void _showSnackBar(BuildContext context) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: const Text('操作成功'),
        duration: const Duration(seconds: 2),
        action: SnackBarAction(
          label: '撤销',
          onPressed: () { /* 撤销操作 */ },
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('SnackBar 示例')),
      body: Center(
        child: ElevatedButton(
          onPressed: () => _showSnackBar(context),
          child: const Text('显示 SnackBar'),
        ),
      ),
    );
  }
}

Flutter 3.38+ 变更

action 的 SnackBar 不再自动消失,用户必须手动操作。无 action 的 SnackBar 仍按 duration 自动消失。


Selection — 选择类组件

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 _checked = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Checkbox 示例')),
      body: Column(
        children: [
          Checkbox(
            value: _checked,
            onChanged: (value) => setState(() => _checked = value ?? false),
          ),
          CheckboxListTile(
            value: _checked,
            onChanged: (value) => setState(() => _checked = value ?? false),
            title: const Text('同意协议'),
            controlAffinity: ListTileControlAffinity.leading,
          ),
        ],
      ),
    );
  }
}

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 _isOn = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Switch 示例')),
      body: Column(
        children: [
          Switch(
            value: _isOn,
            onChanged: (value) => setState(() => _isOn = value),
          ),
          SwitchListTile(
            value: _isOn,
            onChanged: (value) => setState(() => _isOn = value),
            title: const Text('深色模式'),
          ),
        ],
      ),
    );
  }
}

Radio — 单选

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

enum Gender { male, female }

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

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

class _RadioExampleState extends State<RadioExample> {
  Gender? _gender;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Radio 示例')),
      body: Column(
        children: [
          Radio<Gender>(
            value: Gender.male,
            groupValue: _gender,
            onChanged: (value) => setState(() => _gender = value),
          ),
          RadioListTile<Gender>(
            title: const Text('男'),
            value: Gender.male,
            groupValue: _gender,
            onChanged: (value) => setState(() => _gender = value),
          ),
          RadioListTile<Gender>(
            title: const Text('女'),
            value: Gender.female,
            groupValue: _gender,
            onChanged: (value) => setState(() => _gender = value),
          ),
        ],
      ),
    );
  }
}

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 _volume = 50;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Slider 示例')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text('音量: ${_volume.round()}', style: const TextStyle(fontSize: 24)),
          Slider(
            value: _volume,
            min: 0,
            max: 100,
            divisions: 10,
            label: _volume.round().toString(),
            onChanged: (value) => setState(() => _volume = value),
          ),
        ],
      ),
    );
  }
}

GestureDetector — 手势检测

GestureDetector 可以检测各种手势操作。

常用手势

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('GestureDetector 示例')),
      body: Center(
        child: GestureDetector(
          onTap: () => print('点击'),
          onDoubleTap: () => print('双击'),
          onLongPress: () => print('长按'),
          onHorizontalDragEnd: (details) => print('水平拖拽结束'),
          child: Container(
            color: Colors.blue,
            width: 100,
            height: 100,
            child: const Center(child: Text('点我', style: TextStyle(color: Colors.white))),
          ),
        ),
      ),
    );
  }
}

常用回调

回调手势
onTap单击
onDoubleTap双击
onLongPress长按
onVerticalDragStart/Update/End垂直拖拽
onHorizontalDragStart/Update/End水平拖拽
onScaleStart/Update/End缩放/旋转
onPanStart/Update/End自由拖拽

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

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

  @override
  State<NavigationBarExample> createState() => _NavigationBarExampleState();
}

class _NavigationBarExampleState extends State<NavigationBarExample> {
  int _currentIndex = 0;

  final _pages = const [
    Center(child: Text('首页', style: TextStyle(fontSize: 24))),
    Center(child: Text('发现', style: TextStyle(fontSize: 24))),
    Center(child: Text('我的', style: TextStyle(fontSize: 24))),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _pages[_currentIndex],
      bottomNavigationBar: NavigationBar(
        selectedIndex: _currentIndex,
        onDestinationSelected: (index) => setState(() => _currentIndex = index),
        destinations: const [
          NavigationDestination(icon: Icon(Icons.home), label: '首页'),
          NavigationDestination(icon: Icon(Icons.explore), label: '发现'),
          NavigationDestination(icon: Icon(Icons.person), label: '我的'),
        ],
      ),
    );
  }
}
组件说明
NavigationBarMaterial 3 风格(推荐)
BottomNavigationBar旧版 Material 2 风格

交互型组件速查

组件用途
ElevatedButton / FilledButton主要操作按钮
OutlinedButton / TextButton次要操作按钮
IconButton图标按钮
FloatingActionButton悬浮按钮
TextField文本输入
Form / TextFormField表单与验证
AlertDialog对话框
BottomSheet底部弹窗
SnackBar轻提示
Checkbox / Switch / Radio / Slider选择类
GestureDetector手势检测
NavigationBar底部导航

下一步

基于 Flutter 官方文档整理