Skip to content

setState

setState() 是 Flutter 最基础的状态更新方式,用于在 StatefulWidgetState 类中通知框架状态已变更。

基本用法

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

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

  @override
  State<CounterPage> createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  int _count = 0;

  void _increment() {
    // ─── ★ setState 修改状态 ──────────────
    setState(() {
      _count++;   // 在回调中修改状态
    });
    // ─── ☆ setState 修改状态 ──────────────
    // setState 调用后,build() 会被重新执行
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('setState 示例')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('$_count', style: const TextStyle(fontSize: 48)),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: _increment,
              child: const Text('+1'),
            ),
          ],
        ),
      ),
    );
  }
}

工作原理

用户操作 → 调用 setState() → 框架标记dirty → 框架调用 build() → UI 更新
  1. 你调用 setState()
  2. 框架将当前 State 标记为「dirty」
  3. 在下一帧,框架调用 build() 重新构建 Widget 树
  4. 框架对比新旧 Widget 树,只更新变化的部分

注意事项

不要在 setState 中执行耗时操作

dart
// ❌ 错误
void _loadData() {
  setState(() {
    final data = http.get(Uri.parse(url));  // 耗时操作!
    _items = data;
  });
}

// ✅ 正确
void _loadData() async {
  setState(() => _isLoading = true);   // 先显示 loading
  final data = await http.get(Uri.parse(url));
  setState(() {
    _items = data;
    _isLoading = false;
  });
}

不要在 build 中调用 setState

dart
// ❌ 错误:会导致无限循环
@override
Widget build(BuildContext context) {
  setState(() { _count++; });
  return Text('$_count');
}

// ✅ 正确:使用 WidgetsBinding.addPostFrameCallback
@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    if (mounted) setState(() { _count++; });
  });
}

mounted 检查

在异步操作后调用 setState 前,检查组件是否还在 Widget 树中:

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

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

  @override
  State<MountedCheckPage> createState() => _MountedCheckPageState();
}

class _MountedCheckPageState extends State<MountedCheckPage> {
  String _data = '等待加载';

  void _fetchData() async {
    final result = await Future.delayed(const Duration(seconds: 2));
    // ─── ★ mounted 检查 ──────────────
    if (!mounted) return;
    // ─── ☆ mounted 检查 ──────────────
    setState(() => _data = '结果: $result');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('mounted 检查')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(_data, style: const TextStyle(fontSize: 20)),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: _fetchData,
              child: const Text('加载数据'),
            ),
          ],
        ),
      ),
    );
  }
}

只更新部分 UI

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

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

  @override
  State<PartialUpdatePage> createState() => _PartialUpdatePageState();
}

class _PartialUpdatePageState extends State<PartialUpdatePage> {
  bool _isLoading = false;
  String _data = '';

  void _load() async {
    // ─── ★ 先设 loading ──────────────
    setState(() => _isLoading = true);
    // ─── ☆ 先设 loading ──────────────
    await Future.delayed(const Duration(seconds: 2));
    if (!mounted) return;
    setState(() {
      _data = '加载完成';
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('setState 局部更新')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (_isLoading)
              const CircularProgressIndicator()
            else
              Text(_data.isEmpty ? '点击加载' : _data),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: _load,
              child: const Text('加载'),
            ),
          ],
        ),
      ),
    );
  }
}

setState 的局限性

局限说明
仅限当前 State无法在兄弟/跨层级组件间共享状态
每次全量重建build() 整体重建,大型 Widget 树性能较差
状态传递麻烦深层传递需要层层回调(callback hell)

何时使用 setState

场景推荐方案
单个组件的简单状态setState
状态需要跨组件共享Provider / Riverpod
全局状态(主题、语言、登录状态)Provider / Riverpod / Bloc
复杂业务逻辑Bloc / Riverpod

TIP

对于新手,先用 setState 理解状态管理的基本概念。当项目变大、状态共享需求增多时,再迁移到 Provider 等方案。

下一步

基于 Flutter 官方文档整理