使用自定义ChangeNotifier初始化riverpod Provider

维纳亚克

我只是在尝试新的river_pod(颤振状态管理库)。我的目标很简单。GestureDetector主页中的侦听垂直拖动并相应地更新动画控制器。我想在其他地方听这个动画。我编写了以下代码,并且按预期方式工作。但是我不觉得我在以正确的方式初始化提供程序。

// a custom notifier class
class AnimationNotifier extends ChangeNotifier {
  final AnimationController _animationController;

  AnimationNotifier(this._animationController) {
    _animationController.addListener(_onAnimationControllerChanged);
  }

  void forward() => _animationController.forward();
  void reverse() => _animationController.reverse();

  void _onAnimationControllerChanged() {
    notifyListeners();
  }

  @override
  void dispose() {
    _animationController.removeListener(_onAnimationControllerChanged);
    super.dispose();
  }

  double get value => _animationController.value;
}

// provider variable, (not initialized here)
var animationProvider;

// main Widget
class GestureControlledAnimationDemo extends StatefulWidget {
  @override
  _GestureControlledAnimationDemoState createState() =>
      _GestureControlledAnimationDemoState();
}

class _GestureControlledAnimationDemoState
    extends State<GestureControlledAnimationDemo>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;

  double get maxHeight => 420.0;

   @override
  void initState() {
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 1),
    );
    // provider is initialized here
    animationProvider = ChangeNotifierProvider((_) {
      return AnimationNotifier(_controller);
    });
    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return CustomScaffold(
      title: 'GestureControlled',
      body: GestureDetector(
        onVerticalDragUpdate: _handleDragUpdate,
        onVerticalDragEnd: _handleDragEnd,
        child: Container(
          color: Colors.red,
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  'Yo',
                  style: TextStyle(color: Colors.white),
                ),
                NotifierTest(),
              ],
            ),
          ),
        ),
      ),
    );
  }

  void _handleDragUpdate(DragUpdateDetails details) {
    _controller.value -= details.primaryDelta / maxHeight;
  }

  void _handleDragEnd(DragEndDetails details) {
    if (_controller.isAnimating ||
        _controller.status == AnimationStatus.completed) return;

    final double flingVelocity =
        details.velocity.pixelsPerSecond.dy / maxHeight;
    if (flingVelocity < 0.0) {
      _controller.fling(velocity: max(2.0, -flingVelocity));
    } else if (flingVelocity > 0.0) {
      _controller.fling(velocity: min(-2.0, -flingVelocity));
    } else {
      _controller.fling(velocity: _controller.value < 0.5 ? -2.0 : 2.0);
    }
  }
}

// Widget which uses the provider
class NotifierTest extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final animationNotifier = useProvider(animationProvider);
    double count = animationNotifier.value * 1000.0;
    return Container(
      child: Text(
        '${count.floor()}',
        style: TextStyle(color: Colors.white),
      ),
    );
  }
}

由于需要动画控制器实例来创建的实例AnimationNotifier,因此只能在_controller初始化后才能完成因此,在中initState(),我同时初始化了_controlleranimationProvider这是使用Riverpod的正确方法Provider吗?如果没有,可以进行哪些修改?

亚历克斯·哈特福德

首先,我强烈建议您使用钩子-这样会大大减少代码的样板,例如,您的类声明将变为:


class GestureControlledAnimationDemo extends HookWidget {
  double get maxHeight => 420.0;

  @override
  Widget build(BuildContext context) {
    final _controller = useAnimationController(duration: Duration(seconds: 1));
    ...
  }

这消除了对initState,dispose等的需要。

其次,您不一定要在类内部创建非静态提供程序。相反,您可以在全局范围内创建它,或者在这种情况下,有必要在自定义通知程序上添加为静态成员。

class AnimationNotifier extends ChangeNotifier {
  ...
  static final provider = ChangeNotifierProvider((_) {
    return AnimationNotifier(controller);
  });
}

但是,等等,我们controller在这个作用域中没有任何变量,那么我们如何获得访问权限?我们可以为AnimationController创建提供程序,也可以将您的提供程序变成一个系列,以便我们可以接受AnimationController作为参数。我将与家人一起演示这种方法:

class AnimationNotifier extends ChangeNotifier {
  ...
  static final provider = ChangeNotifierProvider.autoDispose.family<AnimationNotifier, AnimationController>((_, AnimationController controller) {
    return AnimationNotifier(controller);
  });
}

我添加了autoDispose,因为您可能希望在不再需要它们时丢弃它们。现在,我们使用提供程序:


class GestureControlledAnimationDemo extends HookWidget {
  double get maxHeight => 420.0;

  @override
  Widget build(BuildContext context) {
    final controller = useAnimationController(duration: Duration(seconds: 1));
    final provider = useProvider(AnimationNotifier.provider(controller));
    ...
  }

如果确实使用钩子,请确保将riverpod依赖项更改为hooks_riverpod

编辑:

在您的用例中,您似乎可以将当前控制器存储在StateProvider中,然后从ChangeNotifierProvider中读取它,而不是使用系列。

final controllerProvider = StateProvider<AnimationController>((_) => null);

class AnimationNotifier extends ChangeNotifier {
  ...
  static final provider = ChangeNotifierProvider.autoDispose<AnimationNotifier>((ref) {
    final controller = ref.read(controllerProvider)?.state;
    return AnimationNotifier(controller);
  });
}

class GestureControlledAnimationDemo extends HookWidget {
  double get maxHeight => 420.0;

  @override
  Widget build(BuildContext context) {
    final controller = useAnimationController(duration: Duration(seconds: 1));

    final currentController = useProvider(controllerProvider);
    currentController.state = controller;
    
    final notifier = useProvider(AnimationNotifier.provider);
    ...
  }

这应该工作。请注意,在发布Riverpod 0.6.0时,您还可以自动处理StateProvider。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Flutter Provider 重新初始化模型

在 Symfony User Provider 中使用自定义注解

使用 azure secret provider 类自定义

初始化后如何完成SunPKCS11 Provider?

UILabel子类使用自定义颜色初始化

如何使用自定义属性初始化 SparkContext?

使用@Namespace进行.matchedGeometryEffect的自定义初始化

初始化前使用的自定义变量“层”

正确使用Flutter Provider

Provider 被废弃后使用

NgDocs Provider如何使用?

NestJS。自定义 provider,inject 无法解析 useFactory 的依赖

自定义InfowindowAdapter以添加来自Content provider的图像

如何在 Flutter 中正确扩展自定义 Provider

自定义绑定初始化

重载>>运算符,并使用自定义类型的初始化列表初始化

使用自定义注释注释的bean的自定义初始化

在 Content Provider 中定义并在 onCreate() 中初始化的全局变量在 query() 中为 null

使用WCF服务创建自定义WS-Federation Identity Provider

使用哪个初始化参数:jersey.config.server.provider.packages或javax.ws.rs.Application?

使用AWS Cookbook时出现“未初始化的常量Chef :: Provider :: AwsS3File :: RightAws”错误

将 Provider 与 TickerProviderStateMixin 一起使用:我可以在哪里初始化我的控制器?

Riverpod,在 BuildContext 和 Provider 之外读取状态

自定义UIView是使用init(coder)初始化的,如何使用init(frame)对其进行初始化

自定义类型需要使用auto进行初始化的初始化程序吗?

使用自定义初始化程序的Swift枚举会丢失rawValue初始化程序

为什么我的状态没有在 Flutter Consumer/Provider 中初始化?

Flutter Provider 包在 VSCode 中保存时重新初始化

如何在keras自定义正则化程序中使用初始化的图层权重?