Is your feature request related to a problem? Please describe.
If two screens both listen to the same bloc (maybe they're multiple steps in an onboarding wizard that are identical but for their actual text fields), both screens continue to fire and respond to actions, even after navigating to the second screen.
See distilled code at bottom.
Describe the solution you'd like
It makes sense that both are still active because they're both in the navigation stack, but it would be nice for the first screen to know whether or not it is the active navigation frame.
Describe alternatives you've considered
For now, I am getting around this by subclassing the OnboardingBloc for each step of the wizard and including an instance of each subclass in the BlocProviderTree. This all makes me extremely suspicious that I'm not thinking about screen deactivation correctly.
Additional context
class WorkflowPage extends StatefulWidget {
OnboardingWizard onboardingWizard;
WorkflowPage(this.onboardingWizard);
@override
State createState() => WorkflowPageState();
}
class WorkflowPageState extends State<StatefulWidget> {
OnboardingBloc getBloc(BuildContext context) {
// Home of the smelly workaround
if (widget.onboardingWizard.values == 'values') {
return BlocProvider.of<OnboardingBloc>(context);
} else if (widget.onboardingWizard.values == 'other values') {
return BlocProvider.of<SubclassedOnboardingBloc>(context);
}
}
@override
Widget build(BuildContext context) {
// Without the workaround, this print twice (once per page) upon navigating to `/onboarding/2`
print('Building $widget');
// In the workaround, this line has to know which OnboardingBloc to get
// based on which Screen this is
final OnboardingBloc bloc = getBloc(context);
return Scaffold(
body: BlocBuilder(
bloc: bloc,
builder: (BuildContext context, OnboardingState state) {
// `state` checks and such
return Text('$widget.onboardingWizard stuff goes here');
}
),
);
}
}
class MyApp extends StatelessWidget {
final OnboardingBloc bloc = OnboardingBloc();
final SubclassedOnboardingBloc bloc2 = SubclassedOnboardingBloc(); // My current workaround is to add something like this
@override
Widget build(BuildContext context) {
return BlocProviderTree(
blocProviders: [
BlocProvider<OnboardingBloc>(bloc: bloc),
BlocProvider<SubclassedOnboardingBloc>(bloc: bloc2), // My current workaround obviously also includes adding this
]
child: MaterialApp(
routes: {
'/onboarding/1': (context) {
return WorkflowPage(OnboardingWizard('values'))
},
'/onboarding/2': (context) {
return WorkflowPage(OnboardingWizard('other values'))
},
},
),
);
}
void main() {
runApp(MyApp());
}
Hi @craiglabenz thanks for opening this issue!
I would recommend having two different bloc instances and providing each instance to the workflow pages separately rather than using a global bloc provider.
class MyApp extends StatelessWidget {
final OnboardingBloc blocA = OnboardingBloc();
final OnboardingBloc blocB = OnboardingBloc();
@override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
'/onboarding/1': (context) {
return WorkflowPage(OnboardingWizard('values'), blocA)
},
'/onboarding/2': (context) {
return WorkflowPage(OnboardingWizard('other values'), blocB)
},
},
),
);
}
Does that help or do you want to have the same bloc state for both pages? I would imagine you'd want each page to have it's own state so that when you navigate, you have a clean state on the new page. If that's the case, then I would just have two instances of the same bloc and inject them individually into the components that need them.
I hope that I understood your question properly. Let me know if that helps 馃憤
In my case I retrieve a json object list at the root page and I am navigating to a 'details' type screen which should update the specific object's details. When I make changes to the details of an object, both the initial screen's BlocBuilder and the second screen's BlocBuilder rebuild (global bloc provider). I am too wondering if there is a way to only fire a rebuild if the current screen's bloc builder has focus. Both screens should be sharing the same data objects so the rebuild makes sense, but since the page isn't exposed I fear this may eventually cause bad performance. Would the solution be to instead have separate bloc instances that read from the same cached data? If so then how would the first page's bloc know its data changed as a result from the second bloc's events.
Sorry if that doesn't make good sense, I actually have a more complicated use case of a bunch of nested screens, each accessing a deeper level of the object's json but I just wanted to articulate the main idea. Thanks! @felangel
@hawkinsjb1 I personally would not worry about optimizing for rebuilds unless you're running into performance issues. Flutter is designed to rebuild widgets many times and if both pages depend on the same bloc state stream then I'd say don't worry (unless, like I said, you're seeing performance issues).
Many external factors can trigger a new widget build, such as:
Hope that helps! 馃憤
Closing this for now but feel free to comment with additional concerns/questions and I'm happy to continue the conversation 馃憤