mcpField
S
signals-and-flutter-hooks

signals-and-flutter-hooks

github.com/rodydavis开发6056 次安装2 周前更新
代码仓库

关于 signals-and-flutter-hooks

Signals and Flutter Hooks 通过代码示例介绍 Flutter 中从 setState、ValueNotifier 到 signals 和 hooks 的状态管理方式,适合学习响应式状态与组件生命周期的开发者。内容展示了各方案的用法及衔接方式。

主要功能

  1. 01对比 setState 与 ValueNotifier
  2. 02通过 signals 实现响应式状态
  3. 03使用 Watch 更新组件
  4. 04以 hooks 减少 StatefulWidget 样板代码
  5. 05在 HookWidget 中使用 signals

适用场景

  1. 01比较 Flutter 状态管理方式
  2. 02将状态更新限制在组件子树
  3. 03在 HookWidget 中共享响应式状态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/rodydavis/skills --skill signals-and-flutter-hooks

安装后在 Claude Code 中输入/signals-and-flutter-hooks确认已加载。

详细介绍

Signals 和 Flutter Hooks

在使用 Flutter 处理数据时,最先接触到的内容之一就是 setState。

setState

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}

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

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int count = 0;

  void increment() {
    if (mounted) {
      setState(() {
        count++;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: Center(child: Text('Count: $count')),
      floatingActionButton: FloatingActionButton(
        onPressed: increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

每次调用 setState 时,它只会将 widget 标记为需要重建,但需要你(作为开发者)留意并明确这些更新发生的时机。如果修改数据时忘记调用 setState,widget 树就可能变得过时。

ValueNotifier

我们可以使用 ValueNotifier 来改进这一点,而不是直接存储值。这样,我们就可以在容器中读写值,并使用 ValueListenableBuilder 等辅助 widget,在值发生变化时更新 widget 树的部分区域。

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}

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

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  final count = ValueNotifier(0);

  void increment() {
    count.value++;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: Center(child: ValueListenableBuilder(
        valueListenable: count,
        builder: (context, value, child) {
          return Text('Count: $value');
        }
      )),
      floatingActionButton: FloatingActionButton(
        onPressed: increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

FlutterSignal

使用 signals 包,我们可以将 ValueNotifier 升级为基于 signal 的实现,它采用基于推送 / 拉取架构的响应式图。

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

void main() {
  runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}

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

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  final count = signal(0);

  void increment() {
    count.value++;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: Center(
        child: ValueListenableBuilder(
          valueListenable: count,
          builder: (context, value, child) {
            return Text('Count: $value');
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

6.0.0 之后创建的 Signals 也实现了 ValueNotifier,因此无需更改其他代码即可轻松迁移。

我们可以使用 Watch widget 或 .watch(context) 扩展来替代 ValueListenableBuilder。

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

void main() {
  runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}

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

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  final count = signal(0);

  void increment() {
    count.value++;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: Center(
        child: Text('Count: ${count.watch(context)}'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

flutter_hooks

使用 Flutter Hooks,我们可以改用 HookWidget,减少 StatefulWidget 的样板代码。借助 useState,我们可以直接在 build 方法中定义状态,并轻松地在 widget 之间共享。

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

void main() {
  runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}

class Counter extends HookWidget {
  const Counter({super.key});

  @override
  Widget build(BuildContext context) {
    final count = useState(0);
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: Center(child: Text('Count: ${count.value}')),
      floatingActionButton: FloatingActionButton(
        onPressed: () => count.value++,
        child: const Icon(Icons.add),
      ),
    );
  }
}

useState 会返回一个 ValueNotifier,值发生变化时会自动重建 widget。

signals_hooks

借助新的包 signals_hooks,我们现在可以在 HookWidget 中定义 Signals,并获得响应式图的优势,同时还能在 widget 之间共享生命周期。

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

void main() {
  runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}

class Counter extends HookWidget {
  const Counter({super.key});

  @override
  Widget build(BuildContext context) {
    final count = useSignal(0);
    final countStr = useComputed(() => count.value.toString());
    useSignalEffect(() {
      print('count: $count');
    });
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: Center(child: Text('Count: $countStr')),
      floatingActionButton: FloatingActionButton(
        onPressed: () => count.value++,
        child: const Icon(Icons.add),
      ),
    );
  }
}

评论

支持 Markdown