You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

我是否错误使用了Flutter的ChangeNotifierProxyProvider?

问题分析与修正

你想要实现「Counter2递增时重置Counter1」的功能,当前用ChangeNotifierProxyProvider的写法存在用法偏差,虽然能实现功能,但不符合ProxyProvider的设计初衷,且有冗余操作。

原始代码问题点

  • ChangeNotifierProxyProvider的核心作用是让被代理的Model依赖另一个Model的状态来更新自身,而不是用来添加/移除监听事件。你在update方法里反复对Counter2绑定/解绑Counter1的reset监听,属于误用。
  • 每次Counter2触发notifyListeners()时,update方法都会执行,反复执行removeListener和addListener完全冗余,还可能引发监听重复或丢失的潜在问题。

修正方案

方案1:直接绑定监听(无需ProxyProvider)

这种方式最简单,直接在Counter2初始化时绑定Counter1的reset方法作为监听,不需要用到ProxyProvider:

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

void main() {
  runApp(const MyApp());
}

class Counter1 extends ChangeNotifier {
  int _counter = 0;
  int get counter => _counter;

  void increment() {
    _counter++;
    notifyListeners();
  }

  void reset() {
    _counter = 0;
    notifyListeners();
  }
}

class Counter2 extends ChangeNotifier {
  int _counter = 0;
  int get counter => _counter;

  void increment() {
    _counter++;
    notifyListeners();
  }
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: MultiProvider(providers: [
        // 先提供Counter1,这样创建Counter2时能获取到实例
        ChangeNotifierProvider<Counter1>(create: (_) => Counter1()),
        ChangeNotifierProvider<Counter2>(
          create: (context) {
            final counter2 = Counter2();
            // 获取Counter1实例并添加监听,listen设为false避免不必要的重建
            final counter1 = Provider.of<Counter1>(context, listen: false);
            counter2.addListener(counter1.reset);
            return counter2;
          },
        ),
      ], child: const MyHomePage()),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    var counter1 = Provider.of<Counter1>(context);
    var counter2 = Provider.of<Counter2>(context);
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text("Counter2递增时重置Counter1"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Counter 1:'),
            Text('${counter1.counter}', style: Theme.of(context).textTheme.headlineMedium),
            ElevatedButton(onPressed: counter1.increment, child: const Text("递增Counter1")),
            const SizedBox(height: 20),
            const Text('Counter 2:'),
            Text('${counter2.counter}', style: Theme.of(context).textTheme.headlineMedium),
            ElevatedButton(onPressed: counter2.increment, child: const Text("递增Counter2")),
          ],
        ),
      ),
    );
  }
}

方案2:正确使用ChangeNotifierProxyProvider

让Counter1依赖Counter2的状态,通过ProxyProvider的update方法触发Counter1的重置,符合Provider的状态管理设计:

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

void main() {
  runApp(const MyApp());
}

class Counter1 extends ChangeNotifier {
  int _counter = 0;
  int get counter => _counter;

  void increment() {
    _counter++;
    notifyListeners();
  }

  void reset() {
    _counter = 0;
    notifyListeners();
  }

  // 根据Counter2的状态更新自身
  void updateFromCounter2(Counter2 counter2) {
    // 这里可以根据Counter2的具体逻辑判断是否重置,比如每次counter变化就重置
    reset();
  }
}

class Counter2 extends ChangeNotifier {
  int _counter = 0;
  int get counter => _counter;

  void increment() {
    _counter++;
    notifyListeners();
  }
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: MultiProvider(providers: [
        ChangeNotifierProvider<Counter2>(create: (_) => Counter2()),
        ChangeNotifierProxyProvider<Counter2, Counter1>(
          create: (_) => Counter1(),
          // 当Counter2变化时,调用Counter1的更新方法
          update: (_, counter2, counter1) {
            counter1?.updateFromCounter2(counter2);
            return counter1 ?? Counter1();
          },
        ),
      ], child: const MyHomePage()),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    var counter1 = Provider.of<Counter1>(context);
    var counter2 = Provider.of<Counter2>(context);
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text("Counter2递增时重置Counter1"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Counter 1:'),
            Text('${counter1.counter}', style: Theme.of(context).textTheme.headlineMedium),
            ElevatedButton(onPressed: counter1.increment, child: const Text("递增Counter1")),
            const SizedBox(height: 20),
            const Text('Counter 2:'),
            Text('${counter2.counter}', style: Theme.of(context).textTheme.headlineMedium),
            ElevatedButton(onPressed: counter2.increment, child: const Text("递增Counter2")),
          ],
        ),
      ),
    );
  }
}

总结

两种方案都能实现需求:方案1简单直接,适合逻辑简单的场景;方案2更符合Provider的状态管理思想,适合后续需要扩展依赖逻辑的场景。你的原始代码虽然能运行,但属于对ChangeNotifierProxyProvider的误用,建议替换为上述方案。

内容的提问来源于stack exchange,提问作者Fractale

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 04:37:04