Flutter(九)StatelessWidget StatefullWidget

StatelessWidget

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

void main(List<String> args) {
  runApp(HomePage());
}
class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "首页",
      theme: ThemeData(scaffoldBackgroundColor: Colors.blue),
      home: Scaffold(
        appBar: AppBar(title: Text("购物车"),),
        body: ListView(
          children: [
            Text("零食"),
            Text("电脑")
          ],
        ),
        bottomNavigationBar: Container(
          height: 60,
          color: Colors.green,
          child: Center(
            child: Text("结算"),
          ),
        ),
      ),
    );
  }
}

StatelessWidget进阶

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

void main(List<String> args) {
  runApp(const MyApp());
}

// ─────────────────────────────────────────────
// 知识点 1: 无状态组件的基本结构
// ─────────────────────────────────────────────
// 继承 StatelessWidget,重写 build 方法
// 无状态组件 = 不可变(final)+ 无状态(无 setState)
// 适用于 UI 不依赖可变数据的场景

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '无状态组件知识点',
      home: const HomePage(),
      theme: ThemeData(primarySwatch: Colors.blue),
    );
  }
}

// ─────────────────────────────────────────────
// 知识点 2: 从父组件接收参数
// ─────────────────────────────────────────────
// 通过构造函数接收数据,所有字段必须声明为 final
// 这是 StatelessWidget 传递数据的唯一方式(单向数据流)

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('StatelessWidget 详解')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          // 知识点 3: 复用组件 ------ 无状态组件可自由复用
          // 因为没有状态,同一个组件类可以创建多个实例
          Text('无状态组件的核心知识点', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
          SizedBox(height: 16),
          KnowledgeCard(
            title: '1. 不可变性',
            description: '所有属性必须是 final,运行时无法修改',
            icon: Icons.lock,
            color: Colors.red,
          ),
          SizedBox(height: 8),
          KnowledgeCard(
            title: '2. 单向数据流',
            description: '数据只能从父组件通过构造函数传入,子组件无法修改',
            icon: Icons.arrow_downward,
            color: Colors.blue,
          ),
          SizedBox(height: 8),
          KnowledgeCard(
            title: '3. build 方法',
            description: '必须重写,返回一个 Widget,每次重建都会重新调用',
            icon: Icons.build,
            color: Colors.green,
          ),
          SizedBox(height: 8),
          KnowledgeCard(
            title: '4. 性能优势',
            description: '无状态组件重建开销小,Flutter 会跳过不必要的重建',
            icon: Icons.speed,
            color: Colors.orange,
          ),
          SizedBox(height: 8),
          KnowledgeCard(
            title: '5. Context 作用',
            description: 'BuildContext 是组件树的引用,用于查找父级数据(Theme、MediaQuery 等)',
            icon: Icons.account_tree,
            color: Colors.purple,
          ),
          SizedBox(height: 24),
          ComparisonTitle(),
          SizedBox(height: 8),
          _ComparisonTable(),
          SizedBox(height: 24),
          _UsageTips(),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────
// 知识点 3: 自定义可复用的无状态组件
// ─────────────────────────────────────────────
// 提取独立组件,便于复用和维护
// 所有传入参数必须声明为 final

class KnowledgeCard extends StatelessWidget {
  final String title;
  final String description;
  final IconData icon;
  final Color color;

  const KnowledgeCard({
    super.key,
    required this.title,
    required this.description,
    required this.icon,
    this.color = Colors.blue,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 3,
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            CircleAvatar(
              backgroundColor: color.withValues(alpha: 0.15),
              child: Icon(icon, color: color),
            ),
            const SizedBox(width: 12),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
                  const SizedBox(height: 4),
                  Text(description, style: TextStyle(fontSize: 14, color: Colors.grey[700])),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// ─────────────────────────────────────────────
// 知识点 4: 通过 Theme.of(context) 查找父级数据
// ─────────────────────────────────────────────
// StatelessWidget 通过 context 向上查找组件树中的数据
// 这是 InheritedWidget 的机制

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

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context); // 通过 context 获取 Theme
    return Text(
      '对比:StatelessWidget vs StatefulWidget',
      style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
    );
  }
}

// ─────────────────────────────────────────────
// 知识点 5: 组件树的概念
// ─────────────────────────────────────────────
// 无状态组件是组件树的叶子节点
// 父组件通过构造函数将数据向下传递

class _ComparisonTable extends StatelessWidget {
  const _ComparisonTable();

  @override
  Widget build(BuildContext context) {
    return Table(
      border: TableBorder.all(color: Colors.grey.shade300),
      columnWidths: const {
        0: FlexColumnWidth(1),
        1: FlexColumnWidth(2),
        2: FlexColumnWidth(2),
      },
      children: [
        TableRow(
          decoration: BoxDecoration(color: Colors.grey),
          children: [
            _Cell('对比项', isHeader: true),
            _Cell('StatelessWidget', isHeader: true),
            _Cell('StatefulWidget', isHeader: true),
          ],
        ),
        TableRow(children: [
          _Cell('状态'),
          _Cell('无状态'),
          _Cell('有状态'),
        ]),
        TableRow(children: [
          _Cell('修改数据'),
          _Cell('无法修改(final)'),
          _Cell('通过 setState 修改'),
        ]),
        TableRow(children: [
          _Cell('生命周期'),
          _Cell('只有 build'),
          _Cell('initState / build / dispose 等'),
        ]),
        TableRow(children: [
          _Cell('性能'),
          _Cell('更轻量'),
          _Cell('相对较重'),
        ]),
        TableRow(children: [
          _Cell('使用场景'),
          _Cell('静态 UI、展示型页面'),
          _Cell('交互型、动态数据页面'),
        ]),
      ],
    );
  }
}

class _Cell extends StatelessWidget {
  final String text;
  final bool isHeader;

  const _Cell(this.text, {this.isHeader = false});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 10),
      child: Text(
        text,
        style: TextStyle(
          fontWeight: isHeader ? FontWeight.bold : FontWeight.normal,
          color: isHeader ? Colors.white : Colors.black87,
        ),
      ),
    );
  }
}

// ─────────────────────────────────────────────
// 知识点 6: 何时使用 StatelessWidget
// ─────────────────────────────────────────────

class _UsageTips extends StatelessWidget {
  const _UsageTips();

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.blue.shade50,
        borderRadius: BorderRadius.circular(8),
        border: Border.all(color: Colors.blue.shade200),
      ),
      child: const Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('💡 何时使用 StatelessWidget?', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
          SizedBox(height: 8),
          Text('✅ 页面内容不依赖于运行时可变数据'),
          Text('✅ 仅展示从父组件传入的数据'),
          Text('✅ 不需要修改自身状态'),
          Text('✅ 纯 UI 组件(如卡片、列表项、图标等)'),
          SizedBox(height: 8),
          Text('❌ 需要交互响应(如点击改变状态)时,应使用 StatefulWidget'),
        ],
      ),
    );
  }
}

StatefullWidget

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

void main(List<String> args) {
  runApp(HomePage());
}

class HomePage extends StatefulWidget {
  HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Container(
       child: MaterialApp(
        title: "title",
        theme: ThemeData(scaffoldBackgroundColor: Colors.green),
        home: Scaffold(
          appBar: AppBar(title: Text("首页"),),
          body: Center(
            child: ListView(
              children: [
                Text("电脑"),
                Text("显卡"),
              ],
            ),
          ),
          bottomNavigationBar: Container(
            height: 60,
            color: Colors.blue,
            child: Text("购买"),
          ),
        ),
       ),
    );
  }
}

StatefullWidget

setState里面写需要修改的数据。推荐写法,虽然写外面也能刷新。

甚至写在setState后面也能更新。因为这个方法只是标记下一帧需要刷新。不是立刻执行。

scss 复制代码
// Flutter 框架内部 setState 的简化逻辑
void setState(VoidCallback fn) {
  fn();                          // 1. 先执行你传入的回调(修改状态)
  _element!.markNeedsBuild();   // 2. 再把当前 Element 标记为「脏」
}
less 复制代码
import 'package:flutter/material.dart';

void main(List<String> args) {
  runApp(
    MaterialApp(
      title: "title",
      theme: ThemeData(scaffoldBackgroundColor: Colors.greenAccent),
      home: HomePage(),
    ),
  );
}

class HomePage extends StatefulWidget {
  HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int count = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("首页")),
      bottomNavigationBar: Container(
        height: 60,
        color: Colors.blue,
        child: Center(child: Text("购买")),
      ),
      body: Center(
        child: Row(
          mainAxisAlignment: .center,
          children: [
            GestureDetector(
              child: Text("-2"),
              onDoubleTap: () {
                setState(() {
                  count -= 2;
                });
              },
            ),
            TextButton(
              onPressed: () {
                setState(() {
                  count -= 1;
                });
              },
              child: Text("-1"),
            ),
            Text("100"),
            TextButton(
              onPressed: () {
                setState(() {
                  count += 1;
                });
              },
              child: Text("+1"),
            ),
            GestureDetector(
              child: Text("+2"),
              onDoubleTap: () {
                setState(() {
                  count += 2;
                });
              },
            ),
          ],
        ),
      ),
    );
  }
}

StatefullWidget进阶

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

void main(List<String> args) {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'StatefulWidget 全方位知识点',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const KnowledgeHome(),
    );
  }
}

// ═══════════════════════════════════════════════════════════
// 知识点导航页
// ═══════════════════════════════════════════════════════════
class KnowledgeHome extends StatelessWidget {
  const KnowledgeHome({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('StatefulWidget 知识体系')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: const [
          Text('核心知识点', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
          SizedBox(height: 16),
          _KnowledgeItem(
            title: '1. 基本结构',
            subtitle: 'Widget + State 两段式',
            page: StructureDemo(),
          ),
          _KnowledgeItem(
            title: '2. setState 机制',
            subtitle: '修改状态 + 标记重建',
            page: SetStateDemo(),
          ),
          _KnowledgeItem(
            title: '3. 完整生命周期',
            subtitle: 'createState → initState → build → dispose',
            page: LifecycleDemo(),
          ),
          _KnowledgeItem(
            title: '4. mounted 检查',
            subtitle: '异步回调后判断是否还挂载',
            page: MountedDemo(),
          ),
          _KnowledgeItem(
            title: '5. Key 与状态复用',
            subtitle: '控制 State 是否被复用',
            page: KeyDemo(),
          ),
          _KnowledgeItem(
            title: '6. App 生命周期',
            subtitle: '前后台切换监听',
            page: AppLifecycleDemo(),
          ),
        ],
      ),
    );
  }
}

class _KnowledgeItem extends StatelessWidget {
  final String title;
  final String subtitle;
  final Widget page;

  const _KnowledgeItem({
    required this.title,
    required this.subtitle,
    required this.page,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.only(bottom: 8),
      child: ListTile(
        title: Text(title, style: const TextStyle(fontWeight: FontWeight.w600)),
        subtitle: Text(subtitle),
        trailing: const Icon(Icons.chevron_right),
        onTap: () => Navigator.push(
          context,
          MaterialPageRoute(builder: (_) => page),
        ),
      ),
    );
  }
}

// ═══════════════════════════════════════════════════════════
// 知识点 1:基本结构
// ─────────────────────────────────────────────────────────
// StatefulWidget 由两部分组成:
//   ① Widget 类:不可变,描述配置(props、key)
//   ② State 类:可变,持有状态 + build 方法
// 分离的原因:Widget 频繁重建(每帧都可能 new),但 State 需要持久保留
// ═══════════════════════════════════════════════════════════
class StructureDemo extends StatelessWidget {
  const StructureDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('1. 基本结构')),
      body: const Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('两段式结构', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
            SizedBox(height: 12),
            Text('''
class MyWidget extends StatefulWidget {
  const MyWidget({super.key, required this.title});
  final String title;          // ① 配置:不可变,final

  @override
  State<MyWidget> createState() => _MyWidgetState();  // ② 创建 State
}

class _MyWidgetState extends State<MyWidget> {
  int _count = 0;              // ③ 状态:可变,非 final

  @override
  Widget build(BuildContext context) {
    return Text('\${widget.title}: \$_count');  // ④ 通过 widget.xxx 访问配置
  }
}''', style: TextStyle(fontFamily: 'monospace', fontSize: 13)),
            SizedBox(height: 16),
            Text('🔑 关键点', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
            SizedBox(height: 8),
            Text('• Widget 不可变 → 每次父组件 rebuild 都会 new 一个新 Widget'),
            Text('• State 可变且持久 → 不会随 Widget 重建而丢失'),
            Text('• 通过 widget.属性 访问 Widget 的配置'),
            Text('• State 的泛型 <MyWidget> 绑定了它服务的 Widget 类型'),
          ],
        ),
      ),
    );
  }
}

// ═══════════════════════════════════════════════════════════
// 知识点 2:setState 机制
// ─────────────────────────────────────────────────────────
// setState 做两件事:① 执行回调(修改状态)② markNeedsBuild(标记重建)
// rebuild 是延迟的(下一帧),不是立即执行
// ═══════════════════════════════════════════════════════════
class SetStateDemo extends StatefulWidget {
  const SetStateDemo({super.key});

  @override
  State<SetStateDemo> createState() => _SetStateDemoState();
}

class _SetStateDemoState extends State<SetStateDemo> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('2. setState 机制')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('$_count', style: const TextStyle(fontSize: 48)),
            const SizedBox(height: 20),
            // ✅ 正确:修改写在 setState 回调内
            ElevatedButton(
              onPressed: () {
                setState(() {
                  _count += 1;  // 修改和通知绑定在一起
                });
              },
              child: const Text('+1(正确写法)'),
            ),
            const SizedBox(height: 20),
            const Text(
              'setState 内部逻辑:\nfn() 执行回调 → markNeedsBuild() 标记脏\n→ 下一帧才真正 rebuild',
              textAlign: TextAlign.center,
              style: TextStyle(color: Colors.grey),
            ),
          ],
        ),
      ),
    );
  }
}

// ═══════════════════════════════════════════════════════════
// 知识点 3:完整生命周期
// ─────────────────────────────────────────────────────────
// createState → initState → didChangeDependencies → build
// → (didUpdateWidget + build 循环) → deactivate → dispose
// ═══════════════════════════════════════════════════════════
class LifecycleDemo extends StatefulWidget {
  const LifecycleDemo({super.key});

  @override
  State<LifecycleDemo> createState() => _LifecycleDemoState();
}

class _LifecycleDemoState extends State<LifecycleDemo> {
  final List<String> _logs = [];
  int _tick = 0;

  void _log(String msg) {
    // 用 addPostFrameCallback 避免在 build 阶段调用 setState
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (mounted) {
        setState(() => _logs.insert(0, msg));
      }
    });
  }

  @override
  void initState() {
    super.initState();
    _log('① initState ------ 初始化(仅一次,适合订阅、请求)');
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _log('② didChangeDependencies ------ 依赖变化(InheritedWidget)');
  }

  @override
  void didUpdateWidget(covariant LifecycleDemo oldWidget) {
    super.didUpdateWidget(oldWidget);
    _log('③ didUpdateWidget ------ 父组件 rebuild');
  }

  @override
  Widget build(BuildContext context) {
    _log('④ build ------ 构建 UI(第 ${_tick + 1} 次)');
    return Scaffold(
      appBar: AppBar(title: const Text('3. 生命周期')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(12),
            child: ElevatedButton(
              onPressed: () => setState(() => _tick++),
              child: Text('触发 rebuild ($_tick)'),
            ),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: _logs.length,
              itemBuilder: (context, i) => ListTile(
                dense: true,
                title: Text(_logs[i], style: const TextStyle(fontSize: 13)),
              ),
            ),
          ),
        ],
      ),
    );
  }

  @override
  void deactivate() {
    _log('⑤ deactivate ------ 从树中移除');
    super.deactivate();
  }

  @override
  void dispose() {
    _log('⑥ dispose ------ 销毁,释放资源');
    super.dispose();
  }
}

// ═══════════════════════════════════════════════════════════
// 知识点 4:mounted 检查
// ─────────────────────────────────────────────────────────
// 异步操作(网络请求、定时器)完成后,组件可能已被销毁
// 此时调用 setState 会报错,必须先检查 mounted
// ═══════════════════════════════════════════════════════════
class MountedDemo extends StatefulWidget {
  const MountedDemo({super.key});

  @override
  State<MountedDemo> createState() => _MountedDemoState();
}

class _MountedDemoState extends State<MountedDemo> {
  String _status = '等待中...';
  bool _loading = false;

  Future<void> _fetchData() async {
    setState(() {
      _loading = true;
      _status = '请求中...';
    });

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

    // ⚠️ 关键:异步回调前必须检查 mounted
    // 如果用户在请求期间返回了上一页,组件已被 dispose
    // 此时 setState 会抛 "setState() called after dispose()"
    if (!mounted) return;  // ← 没有这行会崩溃

    setState(() {
      _loading = false;
      _status = '请求完成 ✅';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('4. mounted 检查')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(_status, style: const TextStyle(fontSize: 20)),
            const SizedBox(height: 8),
            const Text(
              '试试点下面按钮后立刻返回上一页\n(不检查 mounted 会崩溃)',
              textAlign: TextAlign.center,
              style: TextStyle(color: Colors.grey, fontSize: 13),
            ),
            const SizedBox(height: 20),
            _loading
                ? const CircularProgressIndicator()
                : ElevatedButton(
                    onPressed: _fetchData,
                    child: const Text('发起请求(3秒)'),
                  ),
          ],
        ),
      ),
    );
  }
}

// ═══════════════════════════════════════════════════════════
// 知识点 5:Key 与状态复用
// ─────────────────────────────────────────────────────────
// 默认情况下,同类型 Widget 在同位置会复用 State
// 用 Key 可以强制 Flutter 创建新 State(而非复用旧的)
// ═══════════════════════════════════════════════════════════
class KeyDemo extends StatefulWidget {
  const KeyDemo({super.key});

  @override
  State<KeyDemo> createState() => _KeyDemoState();
}

class _KeyDemoState extends State<KeyDemo> {
  // 两个开关:控制是否给子组件加 Key
  bool _useKey = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('5. Key 与状态复用')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('当前:${_useKey ? "使用 ValueKey" : "不使用 Key"}',
                style: const TextStyle(fontSize: 16)),
            const SizedBox(height: 8),
            const Text(
              '下方两个方块颜色会交换\n观察它们的数字是否跟着颜色走',
              textAlign: TextAlign.center,
              style: TextStyle(color: Colors.grey, fontSize: 13),
            ),
            const SizedBox(height: 20),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                _ColorBox(
                  key: _useKey ? const ValueKey('red') : null,
                  color: Colors.red,
                  label: '红',
                ),
                const SizedBox(width: 16),
                _ColorBox(
                  key: _useKey ? const ValueKey('blue') : null,
                  color: Colors.blue,
                  label: '蓝',
                ),
              ],
            ),
            const SizedBox(height: 30),
            ElevatedButton(
              onPressed: () => setState(() => _useKey = !_useKey),
              child: Text(_useKey ? '切换为:无 Key' : '切换为:有 Key'),
            ),
            const SizedBox(height: 12),
            Text(
              _useKey
                  ? '有 Key:State 跟着 Key 走(数字跟随颜色)'
                  : '无 Key:State 按位置复用(数字留在原位)',
              style: const TextStyle(fontStyle: FontStyle.italic, color: Colors.green),
            ),
          ],
        ),
      ),
    );
  }
}

// 带内部状态的色块:点击数字 +1
class _ColorBox extends StatefulWidget {
  final Color color;
  final String label;

  const _ColorBox({super.key, required this.color, required this.label});

  @override
  State<_ColorBox> createState() => _ColorBoxState();
}

class _ColorBoxState extends State<_ColorBox> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => setState(() => _count++),
      child: Container(
        width: 80,
        height: 80,
        color: widget.color,
        alignment: Alignment.center,
        child: Text(
          '${widget.label}\n$_count',
          textAlign: TextAlign.center,
          style: const TextStyle(color: Colors.white, fontSize: 16),
        ),
      ),
    );
  }
}

// ═══════════════════════════════════════════════════════════
// 知识点 6:App 生命周期
// ─────────────────────────────────────────────────────────
// 通过 WidgetsBindingObserver 监听 App 前后台切换
// 适合:暂停/恢复定时器、保存草稿、上报埋点
// ═══════════════════════════════════════════════════════════
class AppLifecycleDemo extends StatefulWidget {
  const AppLifecycleDemo({super.key});

  @override
  State<AppLifecycleDemo> createState() => _AppLifecycleDemoState();
}

class _AppLifecycleDemoState extends State<AppLifecycleDemo>
    with WidgetsBindingObserver {
  AppLifecycleState? _lastState;
  final List<String> _logs = [];

  @override
  void initState() {
    super.initState();
    // 注册监听
    WidgetsBinding.instance.addObserver(this);
    _logs.add('已注册 App 生命周期监听');
  }

  @override
  void dispose() {
    // 必须取消监听,否则内存泄漏
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  // App 生命周期变化回调
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    setState(() {
      _lastState = state;
      _logs.insert(0, '${DateTime.now().toIso8601String().substring(11, 19)}  ${state.name}');
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('6. App 生命周期')),
      body: Column(
        children: [
          Container(
            width: double.infinity,
            padding: const EdgeInsets.all(20),
            color: Colors.blue.shade50,
            child: Column(
              children: [
                const Text('当前状态', style: TextStyle(color: Colors.grey)),
                Text(
                  _lastState?.name ?? '未知',
                  style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
                ),
              ],
            ),
          ),
          const Padding(
            padding: EdgeInsets.all(12),
            child: Text(
              '切到后台再切回来,观察状态变化\n• resumed: 前台可见\n• inactive: 失去焦点(如来电)\n• paused: 进入后台\n• detached: 仍在运行但未渲染',
              textAlign: TextAlign.center,
              style: TextStyle(fontSize: 13, color: Colors.grey),
            ),
          ),
          Expanded(
            child: ListView.builder(
              padding: const EdgeInsets.symmetric(horizontal: 16),
              itemCount: _logs.length,
              itemBuilder: (context, i) => ListTile(
                dense: true,
                title: Text(_logs[i], style: const TextStyle(fontSize: 13)),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
相关推荐
paopaokaka_luck1 小时前
基于springboot3+vue3的乡村医生诊疗管理系统(AI助手、协同过滤算法、webSocket实时聊天、Echarts图形化分析)
前端·网络·人工智能·spring boot·websocket·网络协议·echarts
编程风暴1 小时前
3类人群的数据分析实习类型选择+4条选错红线
java·前端·数据分析
其美杰布-富贵-李1 小时前
Vue 3 模块导入教程:import、export 与文件组织
前端·javascript·vue.js
Bigger1 小时前
堆了 20 套主题、10+ 组件,用户还是用不出来?我给设计 Skill 加了「自动驾驶」
前端·ai编程·视觉设计
贾天佑忆月 迷失的昵2 小时前
.NET Web开发技术简单整理
前端·.net
Cobyte2 小时前
Vite & Rollup 插件开发实践
前端·javascript·vue.js
朝阳392 小时前
react19【系列实用教程】实用组件封装
开发语言·前端·javascript
IT_陈寒2 小时前
React的useEffect依赖数组把我坑惨了,原来这样写才靠谱
前端·人工智能·后端
恋猫de小郭2 小时前
Flutter hit,一个可以灵活控制溢出点击的第三方包
android·前端·flutter