使用自定义 ChangeNotifier 初始化 riverpod Provider
Initializing riverpod Provider with a custom ChangeNotifier
我只是在试用新的 river_pod,flutter 状态管理库。我的目标很简单。主页中的 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()
中,我已经初始化了 _controller
和 animationProvider
。这是使用 riverpod Provider
的正确方法吗?
如果不行,可以做哪些修改?
首先,我强烈建议使用挂钩 - 它会显着减少代码的样板文件,例如,您的 class 声明将变成:
class GestureControlledAnimationDemo extends HookWidget {
double get maxHeight => 420.0;
@override
Widget build(BuildContext context) {
final _controller = useAnimationController(duration: Duration(seconds: 1));
...
}
这消除了对 initState、dispose 等的需要
其次,您不一定要在 class 内创建 non-static 提供程序。相反,您可以在全局范围内创建它,或者在这种情况下,将其添加为自定义通知程序上的静态成员是有意义的。
class AnimationNotifier extends ChangeNotifier {
...
static final provider = ChangeNotifierProvider((_) {
return AnimationNotifier(controller);
});
}
但是等等,我们在此范围内没有任何名为 controller
的变量,那么我们如何获得访问权限?我们可以为 AnimationController 创建一个提供者,或者我们可以将您的提供者变成一个 family 这样我们就可以接受一个 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。
我只是在试用新的 river_pod,flutter 状态管理库。我的目标很简单。主页中的 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()
中,我已经初始化了 _controller
和 animationProvider
。这是使用 riverpod Provider
的正确方法吗?
如果不行,可以做哪些修改?
首先,我强烈建议使用挂钩 - 它会显着减少代码的样板文件,例如,您的 class 声明将变成:
class GestureControlledAnimationDemo extends HookWidget {
double get maxHeight => 420.0;
@override
Widget build(BuildContext context) {
final _controller = useAnimationController(duration: Duration(seconds: 1));
...
}
这消除了对 initState、dispose 等的需要
其次,您不一定要在 class 内创建 non-static 提供程序。相反,您可以在全局范围内创建它,或者在这种情况下,将其添加为自定义通知程序上的静态成员是有意义的。
class AnimationNotifier extends ChangeNotifier {
...
static final provider = ChangeNotifierProvider((_) {
return AnimationNotifier(controller);
});
}
但是等等,我们在此范围内没有任何名为 controller
的变量,那么我们如何获得访问权限?我们可以为 AnimationController 创建一个提供者,或者我们可以将您的提供者变成一个 family 这样我们就可以接受一个 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。