How to prevent Flutter widget tree rerendering on state update and performance improvement

1,059 views
Skip to first unread message

Ganesh Rathinavel

unread,
Mar 26, 2020, 1:34:48 PM3/26/20
to Flutter Development (flutter-dev)

I have been trying to build an app using flutter and I am having a hard time figuring out certain concepts in flutter. I am hoping to find some answers here.

Preface: App dependencies: flutter_mobx,auto_route,get_it.


I am building an app where I need to show some alerts. I have created a Flutter page (AlertsScreen) which is subscribed to mobx alerts_store. So whenever I try to push an alert to the alerts_store the snackbar should popup. Something similar to which I have been doing in React web.


App repo link: https://github.com/ganeshrvel/open_cloud_encryptor/tree/new-router-logic


Questions:


1. How do I mount a common widget like AlertsScreen() which will be rendered across the app. Earlier I used to do something like this:

app.dart (Github link)

 
MaterialApp(
  builder
: (BuildContext context, Widget widget) {
    setErrorBuilder
();
   
return Column(
      children
: <Widget>[
       
Expanded(
          child
: widget,
       
),
       
AlertsScreen(),
     
],
   
);
 
},
  title
: 'App name',
  onGenerateRoute
: Router.onGenerateRoute,
  initialRoute
: Router.splashScreen,
  navigatorKey
: Router.navigator.key,
)



But then whenever I try to render Flushbar or any other item inside AlertsScreen which requires context it throws an error:

[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Navigator operation requested with a context that does not include a Navigator. The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.

Flushbar example code:


Flushbar(
  title
: 'title',
  message
: 'body',
  duration
: const Duration(seconds: 5),
).show(context);


How do I mount a common widget like AlertsScreen across the app and which will be always visible for any sort of activity?



2) This answer from stackoverflow suggests to make a CommonWidget stateless widget and use it wherever it has to be rendered. So taking cues from the above stackoverflow answer I modified my code like this:


common_widget.dart (Github link)


class CommonWidget extends StatelessWidget {
 
final Widget child;

 
const CommonWidget({
   
@required this.child,
 
});

 
@override
  Widget build(BuildContext context) {
   
return Column(
      children
: <Widget>[
       
Expanded(
          child
: child,
       
),
       
AlertsScreen(),
     
],
   
);
 
}
}


home.dart (Github link)

class _HomeScreenState extends StatelessWidget {
 
@override
  Widget build(BuildContext context) {
   
return Scaffold(
      appBar
: AppBar(
        title
: const Text('Home'),
     
),
      body
: CommonWidget(
        child
: Center(
          child
: Column(),
       
),
     
),
   
);
 
}
}





Is this the right way to do it?


3) I use flutter_mobx as the store for AlertsScreen. Since I have used CommonWidget in multiple screens for displaying AlertsScreen, whenever I update the store the AlertsScreen gets rebuilt multiple time. How do I prevent this? How do I make sure that AlertsScreen widget is only rerendered in the current screen and prevent it from rebuilding everywhere else while updating the store?


Current the workaround is something like this:


alerts.dart (Github Link)

@override
void didChangeDependencies() {
 
super.didChangeDependencies();
 
_alertsStore ??= Provider.of<AlertsStore>(context);
 
_disposers ??= [
    reaction
(
     
(_) => _alertsStore.alertsList.iterator,
     
(alertsList) {

        // Check whether current screen is rendered
       
if (ModalRoute.of(context).isCurrent) {
          buildSnackbar();
       
}
     
},
   
),
 
];
}




Is there any better way to do this?

Reply all
Reply to author
Forward
0 new messages