Widget 基础

Widget 是 Flutter 应用的构建块。理解 Widget 的概念和工作原理是掌握 Flutter 的关键。

核心概念:在 Flutter 中,"一切皆 Widget"。从按钮到布局,从文本到动画,所有内容都是 Widget。

1. 什么是 Widget?

Widget 是 Flutter 中的核心概念,它们是:

  • UI 元素:按钮、文本、图片等
  • 布局组件:行、列、堆栈等
  • 功能组件:手势、主题、导航等
  • 不可变的:一旦创建就不能修改

2. Widget 类型

StatelessWidget(无状态 Widget)

无状态 Widget 是不可变的,一旦创建就不会改变。适用于静态内容。

import 'package:flutter/material.dart';

class MyTextWidget extends StatelessWidget {
  final String text;

  const MyTextWidget({super.key, required this.text});

  @override
  Widget build(BuildContext context) {
    return Text(
      text,
      style: const TextStyle(
        fontSize: 18,
        color: Colors.blue,
      ),
    );
  }
}

// 使用
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: MyTextWidget(text: 'Hello, Widget!'),
        ),
      ),
    );
  }
}

StatefulWidget(有状态 Widget)

有状态 Widget 可以在其生命周期内改变状态。适用于需要动态更新的内容。

import 'package:flutter/material.dart';

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

  @override
  State createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State {
  int _count = 0;

  void _increment() {
    setState(() {
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          '计数: $_count',
          style: const TextStyle(fontSize: 24),
        ),
        const SizedBox(height: 20),
        ElevatedButton(
          onPressed: _increment,
          child: const Text('增加'),
        ),
      ],
    );
  }
}

3. 常用基础 Widget

Container

Container 是一个多功能组合 Widget,可以包含子 Widget、设置内边距、外边距、装饰等。

Container(
  width: 200,
  height: 200,
  padding: const EdgeInsets.all(20),
  margin: const EdgeInsets.symmetric(horizontal: 10),
  decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(10),
    boxShadow: [
      BoxShadow(
        color: Colors.black.withOpacity(0.2),
        blurRadius: 10,
        spreadRadius: 5,
      ),
    ],
  ),
  child: const Center(
    child: Text(
      'Container',
      style: TextStyle(color: Colors.white, fontSize: 18),
    ),
  ),
)

Text

Text Widget 用于显示文本内容。

const Text(
  'Hello, Flutter!',
  style: TextStyle(
    fontSize: 24,
    fontWeight: FontWeight.bold,
    color: Colors.blue,
    letterSpacing: 1.5,
    wordSpacing: 2.0,
  ),
  textAlign: TextAlign.center,
)

Image

Image Widget 用于显示图片。

// 从网络加载图片
Image.network(
  'https://example.com/image.jpg',
  width: 200,
  height: 200,
  fit: BoxFit.cover,
)

// 从资源加载图片
Image.asset('assets/images/logo.png')

// 从文件加载
Image.file(File('/path/to/image.jpg'))

Icon

Icon Widget 显示图标。

const Icon(
  Icons.favorite,
  size: 48,
  color: Colors.red,
)

// 自定义颜色和大小
Icon(
  Icons.star,
  color: Colors.yellow,
  size: 32,
)

4. 布局 Widget

Row(行布局)

Row Widget 在水平方向上排列子 Widget。

Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  crossAxisAlignment: CrossAxisAlignment.center,
  children: const [
    Icon(Icons.star, color: Colors.yellow),
    Icon(Icons.star, color: Colors.yellow),
    Icon(Icons.star, color: Colors.yellow),
    Icon(Icons.star_half, color: Colors.yellow),
    Icon(Icons.star_border, color: Colors.yellow),
  ],
)

Column(列布局)

Column Widget 在垂直方向上排列子 Widget。

Column(
  mainAxisAlignment: MainAxisAlignment.center,
  crossAxisAlignment: CrossAxisAlignment.stretch,
  children: [
    const Text(
      '用户信息',
      style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
    ),
    const SizedBox(height: 10),
    const TextField(
      decoration: InputDecoration(
        hintText: '用户名',
        border: OutlineInputBorder(),
      ),
    ),
    const SizedBox(height: 10),
    const TextField(
      decoration: InputDecoration(
        hintText: '密码',
        border: OutlineInputBorder(),
      ),
    ),
    const SizedBox(height: 20),
    ElevatedButton(
      onPressed: () {},
      child: const Text('登录'),
    ),
  ],
)

Stack(堆叠布局)

Stack Widget 允许子 Widget 重叠。

Stack(
  alignment: Alignment.center,
  children: [
    // 背景图片
    Container(
      width: 200,
      height: 200,
      decoration: const BoxDecoration(
        color: Colors.blue,
        shape: BoxShape.circle,
      ),
    ),
    // 文字在中心
    const Text(
      '中心',
      style: TextStyle(
        color: Colors.white,
        fontSize: 24,
        fontWeight: FontWeight.bold,
      ),
    ),
    // 右下角图标
    Positioned(
      bottom: 10,
      right: 10,
      child: Container(
        padding: const EdgeInsets.all(8),
        decoration: BoxDecoration(
          color: Colors.white,
          shape: BoxShape.circle,
        ),
        child: const Icon(Icons.edit, size: 20),
      ),
    ),
  ],
)

5. 交互 Widget

按钮

Flutter 提供了多种按钮类型。

// ElevatedButton(Material 风格凸起按钮)
ElevatedButton(
  onPressed: () {
    print('ElevatedButton 被点击');
  },
  style: ElevatedButton.styleFrom(
    backgroundColor: Colors.blue,
    padding: const EdgeInsets.symmetric(
      horizontal: 20,
      vertical: 15,
    ),
  ),
  child: const Text('点击我'),
)

// TextButton(文本按钮)
TextButton(
  onPressed: () {},
  child: const Text('文本按钮'),
)

// OutlinedButton(轮廓按钮)
OutlinedButton(
  onPressed: () {},
  style: OutlinedButton.styleFrom(
    side: const BorderSide(color: Colors.blue),
  ),
  child: const Text('轮廓按钮'),
)

// IconButton(图标按钮)
IconButton(
  onPressed: () {},
  icon: const Icon(Icons.favorite),
  iconSize: 32,
  color: Colors.red,
)

TextField

TextField Widget 用于接收用户输入。

TextField(
  decoration: InputDecoration(
    labelText: '用户名',
    hintText: '请输入用户名',
    prefixIcon: const Icon(Icons.person),
    border: OutlineInputBorder(
      borderRadius: BorderRadius.circular(10),
    ),
    filled: true,
    fillColor: Colors.grey[100],
  ),
  onChanged: (value) {
    print('输入: $value');
  },
  onSubmitted: (value) {
    print('提交: $value');
  },
)

6. Widget 树

Flutter 应用的 UI 由 Widget 树组成。理解 Widget 树对于构建复杂 UI 至关重要。

MaterialApp
 └── Scaffold
     ├── AppBar
     │   └── Text('My App')
     ├── Body
     │   ├── Column
     │   │   ├── Text('Hello')
     │   │   └── Button('Click Me')
     │   └── Container
     └── FloatingActionButton

7. 生命周期

理解 StatefulWidget 的生命周期对于管理状态和资源很重要。

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

  @override
  State createState() => _LifecycleWidgetState();
}

class _LifecycleWidgetState extends State {
  @override
  void initState() {
    super.initState();
    print('initState: Widget 初始化');
    // 初始化操作,如加载数据
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('didChangeDependencies: 依赖变化');
    // 依赖项(如 Theme)变化时调用
  }

  @override
  void didUpdateWidget(LifecycleWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    print('didUpdateWidget: Widget 更新');
    // 父 Widget 重建时调用
  }

  @override
  void setState(VoidCallback fn) {
    super.setState(fn);
    print('setState: 状态更新');
  }

  @override
  void deactivate() {
    super.deactivate();
    print('deactivate: Widget 即将被移除');
  }

  @override
  void dispose() {
    super.dispose();
    print('dispose: Widget 被销毁');
    // 清理资源,如关闭流、取消订阅
  }

  @override
  Widget build(BuildContext context) {
    print('build: Widget 构建');
    return Container(
      child: const Text('生命周期示例'),
    );
  }
}

8. 最佳实践

  • 合理拆分 Widget:将大 Widget 拆分成小的、可重用的组件
  • 使用 const:对于不变的 Widget 使用 const 构造函数
  • 避免过度嵌套:使用私有方法或独立 Widget 减少嵌套层级
  • 选择合适的状态管理:根据应用复杂度选择 setState、Provider、Bloc 等
  • 使用 Keys:在需要控制 Widget 身份时使用 Key
提示:使用 Flutter DevTools 可以帮助可视化 Widget 树和调试 Widget 性能。
注意:频繁调用 setState 可能会影响性能。在构建复杂 Widget 树时,考虑将可变部分提取为独立的 Widget。