-
Notifications
You must be signed in to change notification settings - Fork 215
onInitialBuild called multiple time on child container when parent container changes #59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Sounds like a bug! Hrm, I'm curious as to why this could be happening... I'll have to take a closer look and see what's causing the double-callback. Thanks for reporting :) |
Thanks YOU for building all this :) |
Hey there -- do you happen to have a code sample that exhibits this behavior? I've written some tests and tried a few ways myself, but the Are you perhaps creating a new StoreBuilder / StoreConnector with a Unique key on each build? Thanks for any help reproducing this issue! |
Hey Brian, Thanks for spending time on this. I cannot share the code as I'm not the legal owner of it but I can describe more precisely the problem. I have a main_shell.dart that is a container. It looks like that: class MainShell extends StatelessWidget {
final Widget page;
MainShell({@required this.page})
: super(key: DemoKeys.mainShell(page.key));
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: <Widget>[
this.page, ///<- This is one of the widget for which the InitialBuild is called multiple time. Those are actually other containers...
StoreConnector<AppState, _ViewModel>(
distinct: true,
converter: _ViewModel.fromStore,
onWillChange: (vm) {
if (vm.loginRequested) Navigator.pushNamed(context, 'login');
},
builder: (BuildContext context, vm) {
if (vm.lastMessageKey != "") {
Timer(Duration(milliseconds: 1), () {
final snackBar = SnackMessage(
message: SweetNestLocalizations
.of(context)
.localize(vm.lastMessageKey),
type:vm.lastMessageType);
Scaffold.of(context).showSnackBar(snackBar);
vm.onErrorDisplayed();
});
}
return _loading(context, vm.isLoading);//<- This display a transparent overlay on top of the 'page' widget...
},
),
])
);
}
Widget _loading(BuildContext context, bool isLoading) {
if (isLoading)
return Container(
color: Color(0x55000000),
child:Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
],
)
],)
);
return Container();
}
} As you can see this mainshell has its own view model whit a isLoading flag. The layer is displayed when isloading is true... Then you have a page widget which is also a container. The isloading flag is only part of the main_shell view model. Meaning the page diget should not trigger an initial build as they are on the screen and ti is only the isLoading overlay that is drawn on top of them... this is done using a the stack on the main_shell. Then on the 'Page' widget I perform and action and on a middleware I do something like: Middleware<AppState> _propertyHandler(
AuthenticationRepository authRepo,
LocationRepository locationRepo,
UserRepository userRepository,
PropertyRepository propertyRepository) {
return (Store<AppState> store, action, NextDispatcher next) async {
//We get the current user information
//var user = await authRepo.getSignedInUser();
//TODO:Region selection should we removed...
if (action is HomeSearchPropertiesQuery) {
store.dispatch(SetIsLoading(true)); //<-Trigger a change on the isLoading of the shell
propertyRepository.searchProperty('FR', action.query).then((properties) {
store.dispatch(
HomeSearchPropertiesQueryCompleted(action.query, properties));//<-//update the view model of the 'page' widget
store.dispatch(SetIsLoading(false));//<-Change back the isLoading flag
});
} else if (action is PropertyDetailsGetProperty) {
//store.dispatch(SetIsLoading(true));
propertyRepository.getProperty('FR', action.propertyId).then((property) {
store.dispatch(PropertyDetailsGetPropertyCompleted(property));
//store.dispatch(SetIsLoading(false));
});
}
};
Then on a page widget I have added the following code on the store connector: class HomeTabSearch extends StatelessWidget {
HomeTabSearch() : super(key: SweetNestKeys.homeTab(HomeTab.Search));
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, _ViewModel>(
distinct: true,
onInitialBuild: (vm)=>print('Initial Build Home Search'),
onWillChange: (vm)=>print('on will change'),
onDidChange: (vm)=>print('On did change'),
converter: _ViewModel.fromStore,
builder: (context, vm) {
Timer(Duration(milliseconds: 600), () {
if (!vm.firstQueryExecuted) vm.onQuery();
});
if (vm.viewMode == HomeSearchViewMode.Map) {
return PropertiesMap(
query: vm.query,
center: vm.mapCenter,
zoom: vm.mapZoom,
properties: vm.properties,
selectedPropertyId: vm.selectedPropertyId,
previousSelectedPropertyId: vm.previousSelectedPropertyId,
onPropertySelected: vm.onPropertySelected,
onViewChanged: vm.onViewChanged,
onViewModeChanged: vm.onViewModeChanged,
onPropertyBlocked: vm.onPropertyBlocked,
onPropertyFavorited: vm.onPropertyFavorited,
onPropertyUnfavorited: vm.onPropertyUnfavorited,
favoriteProperties: vm.favoritePropertyIds,
queryShouldBeRecomputed: vm.queryShouldBeRecomputed,
onRegionChangeRequested: vm.onRegionChangeRequested,
onPropertyDetailsViewRequested: vm.onPropertyDetailsViewRequested,
onSearchFilterClicked: vm.onSearchFilterClicked,
onApplyQueryChange: vm.onApplyQueryChange,
canBeQueried: vm.canBeQueried,
chooseNewRegion: vm.chooseNewRegion,
newQueryRegionType: vm.newQueryRegionType,
onchangeQueryRegionType: vm.onChangeQueryRegionType,
handSelectionComplete: vm.handSelectionComplete,
onHandSelectionComplete: vm.onHandSelectionComplete,
onAddHandSelectionPoint: vm.onAddHandSelectionPoint,
handSelectionPoints: vm.handSelectionPoints,
queryCanBeSaved: vm.queryCanBeSaved,
onSaveQuery: vm.onSaveQuery,
isCenterByCode: vm.isCenterByCode,
onCenterByCodeSet: vm.onCenterByCodeSet,
);
} else {
return PropertiesList(
properties: vm.properties,
selectedPropertyId: vm.selectedPropertyId,
favoriteProperties: vm.favoritePropertyIds,
onPropertySelected: vm.onPropertySelected,
onPropertyFavorited: vm.onPropertyFavorited,
onPropertyUnfavorited: vm.onPropertyUnfavorited,
onPropertyBlocked: vm.onPropertyBlocked,
onViewModeChanged: vm.onViewModeChanged,
onPropertyDetailsViewRequested: vm.onPropertyDetailsViewRequested,
onSearchFilterClicked: vm.onSearchFilterClicked,
);
}
});
}
} You can see there is no isLoading on the viewmodel This is the log I have flutter: [INFO] LoggingMiddleware: {Action: InitializationCompleted{uid: <…> //<-This action display the widget so it is normal that an initial build is called
flutter: Initial Build Home Search //<-Expected
flutter: [INFO] LoggingMiddleware: {Action: SetIsLoading{isLoading:true}, <…>//<- This update the loading overlay it should not trigger an initial build of the page again... :(
flutter: [INFO] LoggingMiddleware: {Action: HomeSearchPropertiesQuery{query:Query{id:, <…>
flutter: Initial Build Home Search //<- This should not be there.... :(
flutter: [INFO] LoggingMiddleware: {Action: HomeSearchPropertiesQueryCompleted{properties:<…> //<- This change the viewmodel of page widget
flutter: [INFO] LoggingMiddleware: {Action: SetIsLoading{isLoading:false}, State: <…>
flutter: on will change //<- This is expected as the viewmodel of the page has changed....
flutter: On did change If I comment out the SetLoading action dispatch on the middleware then I end up with a log that looks like: flutter: [INFO] LoggingMiddleware: {Action: InitializationCompleted{uid: <…>
flutter: Initial Build Home Search
flutter: [INFO] LoggingMiddleware: {Action: HomeSearchPropertiesQuery{query:<…>
flutter: [INFO] LoggingMiddleware: {Action: HomeSearchPropertiesQueryCompleted{<…>
flutter: on will change
flutter: On did change This is from my understanding the kind of behavior I should expect... Does the fact that something else is rendered on top of the page pigdet (overlay) causing an initial Build? This is where my expertize reach is limitation :) Let me know If I can help you more. Alex. |
Hrm, this is interesting -- since the Could you check if |
Hi Brian, I’m AFK for the next 5h. I’ll check ASAP when driving back.
Le 24 juin 2018 à 16:03 +0200, Brian Egan <[email protected]>, a écrit :
… Hrm, this is interesting -- since the StoreConnector in MainShell does not wrap this.page, when the ViewModel changes it should not cause the Stack with this.page to be rebuilt, only the code inside the builder function.
Could you check if onInit and onDispose on HomeTabSearch are also called multiple times? If so, this indicates that the this.page Widget's corresponding State class is somehow being initialized then disposed over and over. Generally, that will only happen when the StatefulWidget it's associated with is removed from the tree and added again, or if the key associated with this.page changes on rebuild. Do you know if either of these are a possibility?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or mute the thread.
|
No worries! Whenever ya get a chance :) |
Hey hey :) Any update on this one @AlexandreRoba? |
Hi, sorry I’m on vacation will be back next week. I’ll get back to you ASAP.
Le 4 juil. 2018 à 13:00 +0200, Brian Egan <[email protected]>, a écrit :
… Hey hey :) Any update on this one @AlexandreRoba?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
Oh dang, sorry to interrupt! No problem at all, please take your time and enjoy the vacation :) |
Hi Brian, I've refactored significantly the app since and can seems to reproduce this. :( |
Sounds good, will do! Definitely let me know if ya need more help :) |
Hi all, I have a question regarding container that have other container. Basically I have a container that can display loading status and error message. It is a sort of "shell" container. On this container I have a child container with its own viewmodel and nothing shared. I can see the onInitialBuild method on the child container is called every time the mainshell onInitialBuild is changed. Is this an expected behavior?
The text was updated successfully, but these errors were encountered: