admin管理员组文章数量:1355564
I'm using Flutter Provider, and I'm trying to figure out how to provide my dependencies to a dialog's context while maintaining the lazy initialization.
See my example below where dependencies are provided to the dialogs context, but .read() initializes the dependencies, thus they are no longer lazily initialized in the dialog itself at the moment they are first used within the dialog itself.
showDialog(
context: context,
builder: (_) {
return MultiProvider(
providers: <SingleChildWidget>[
Provider<MyViewModel>.value(
value: context.read<MyViewModel>(),
),
BlocProvider<UploadImageCubit>.value(
value: context.read<UploadImageCubit>(),
),
],
child: InvalidImageSizeDialogWidget(
assetPath: path,
bytes: file.bytes!,
originalWidth: _originalWidth,
originalHeight: _originalHeight,
),
);
},
);
I am aware that I can solve this problem lifting up the state to app scope by wrapping the Material app, but this is not what I want/need. As these dependencies are scoped by Route instead and they should not be available for the entire app.
What I need to know is how I provide dependencies that are declared higher up the widget tree to the context of a dialog with Provider while maintaining lazy initialization?
I'm using Flutter Provider, and I'm trying to figure out how to provide my dependencies to a dialog's context while maintaining the lazy initialization.
See my example below where dependencies are provided to the dialogs context, but .read() initializes the dependencies, thus they are no longer lazily initialized in the dialog itself at the moment they are first used within the dialog itself.
showDialog(
context: context,
builder: (_) {
return MultiProvider(
providers: <SingleChildWidget>[
Provider<MyViewModel>.value(
value: context.read<MyViewModel>(),
),
BlocProvider<UploadImageCubit>.value(
value: context.read<UploadImageCubit>(),
),
],
child: InvalidImageSizeDialogWidget(
assetPath: path,
bytes: file.bytes!,
originalWidth: _originalWidth,
originalHeight: _originalHeight,
),
);
},
);
I am aware that I can solve this problem lifting up the state to app scope by wrapping the Material app, but this is not what I want/need. As these dependencies are scoped by Route instead and they should not be available for the entire app.
What I need to know is how I provide dependencies that are declared higher up the widget tree to the context of a dialog with Provider while maintaining lazy initialization?
Share Improve this question edited Mar 31 at 15:10 anonymous-dev asked Mar 28 at 14:14 anonymous-devanonymous-dev 3,52112 gold badges66 silver badges138 bronze badges1 Answer
Reset to default 5 +50Reproviding what we passed with Provider
is as easy as just using the Provider
default constructor again. We don't set the dispose
property, because we don't want this to clean-up. It's up to the parent (previous context's) provider.
The BlocProvider
is trickier. The default constructor with the create
param has no way of disabling the disposal mechanism. But we can take a look inside its implementation.
We see that it's using just Provider.of
inside of its of
method, so we're safe providing it down the tree with the Provider
.
static T of<T extends StateStreamableSource<Object?>>(
BuildContext context, {
bool listen = false,
}) {
try {
return Provider.of<T>(context, listen: listen);
// ...
But the trick is to make it also resemble its behavior, and BlocProvider
will make widgets depending on it rebuild when the bloc/cubit emits.
We also see that it provides the bloc/cubit using an InheritedProvider
, where the only unordinary thing is the startListening
parameter.
@override
Widget buildWithChild(BuildContext context, Widget? child) {
// ...
return InheritedProvider<T>(
create: _create,
dispose: (_, bloc) => bloc.close(),
startListening: _startListening,
lazy: lazy,
child: child,
);
}
We can make a custom class that will pass the correct callback there and use it from there on.
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
class BlocReprovider<T extends StateStreamableSource<Object?>>
extends SingleChildStatelessWidget {
const BlocReprovider({
required T Function(BuildContext context) read,
super.key,
super.child,
}) : _read = read;
final T Function(BuildContext context) _read;
@override
Widget buildWithChild(BuildContext context, Widget? child) {
assert(
child != null,
'$runtimeType used outside of MultiBlocProvider must specify a child',
);
return InheritedProvider<T>(
create: _read,
// don't set dispose
startListening: _startListening,
lazy: true,
child: child,
);
}
static VoidCallback _startListening(
InheritedContext<StateStreamable<dynamic>?> e,
StateStreamable<dynamic> value,
) {
final subscription = value.stream.listen(
(dynamic _) => e.markNeedsNotifyDependents(),
);
return subscription.cancel;
}
}
// ...
showDialog(
context: context,
builder: (_) {
return MultiProvider(
providers: <SingleChildWidget>[
Provider<MyViewModel>(
create: (context) => context.read<MyViewModel>(),
),
BlocReprovider<UploadImageCubit>(
read: (context) => context.read<UploadImageCubit>(),
),
],
child: InvalidImageSizeDialogWidget(
assetPath: path,
bytes: file.bytes!,
originalWidth: _originalWidth,
originalHeight: _originalHeight,
),
);
},
);
Below is the full example ready to be run on https://dartpad.dev
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
class ACubit extends Cubit<bool> {
ACubit() : super(true) {
print('Instantiating ACubit');
}
}
class AModel {
AModel() {
print('Instantiating AModel');
}
}
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: MultiProvider(
providers: [
BlocProvider<ACubit>(create: (context) => ACubit()),
Provider<AModel>(create: (_) => AModel()),
],
child: Builder(
builder:
(context) => ElevatedButton(
child: Text('Show dialog'),
onPressed: () {
final outerContext = context;
showDialog(
context: context,
builder:
(context) => MultiProvider(
providers: [
Provider<AModel>(
create: (_) => outerContext.read<AModel>(),
),
BlocReprovider<ACubit>(
read: (_) => outerContext.read<ACubit>(),
),
],
child: Builder(
builder:
(context) => ElevatedButton(
child: Text('Print cubit and model value'),
onPressed: () {
print(context.read<ACubit>().state);
print(context.read<AModel>());
},
),
),
),
);
},
),
),
),
),
),
);
}
class BlocReprovider<T extends StateStreamableSource<Object?>>
extends SingleChildStatelessWidget {
const BlocReprovider({
required T Function(BuildContext context) read,
super.key,
super.child,
}) : _read = read;
final T Function(BuildContext context) _read;
@override
Widget buildWithChild(BuildContext context, Widget? child) {
assert(
child != null,
'$runtimeType used outside of MultiBlocProvider must specify a child',
);
return InheritedProvider<T>(
create: _read,
// don't set dispose
startListening: _startListening,
lazy: true,
child: child,
);
}
static VoidCallback _startListening(
InheritedContext<StateStreamable<dynamic>?> e,
StateStreamable<dynamic> value,
) {
final subscription = value.stream.listen(
(dynamic _) => e.markNeedsNotifyDependents(),
);
return subscription.cancel;
}
}
本文标签: flutterHow to lazily reprovide dependencies to dialogsStack Overflow
版权声明:本文标题:flutter - How to lazily reprovide dependencies to dialogs - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744032152a2579108.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论