mightymatth
mightymatth

Reputation: 93

Flutter Drawer lazy load when extracted to custom widget

I have a drawer on my main page which became bloated with content and logic. For example, I need to fetch an image, get some data from Internet and so on. After extracting Drawer in new MyCustomDrawerWidget which extends StatefulWidget, its state has build function which looks like this:

@override
Widget build(BuildContext context) {
  return Drawer(
    child: ...
  );
}

My HomePageState has build function which looks like this:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Home page'),
    ),
    drawer: MyCustomDrawerWidget(),
    body: ...
  );
}

After refactoring the code like this, I noticed how drawer does not get initialized when page loads. It would initialize just after I open the drawer, which is bad because user needs to wait for data to be loaded.

Is there a way to set eager loading on drawer to get initialized along with the page?

Upvotes: 1

Views: 2380

Answers (2)

Dinesh Balasubramanian
Dinesh Balasubramanian

Reputation: 21728

Problem with having Drawer in MainPage:

  • Content, more logic, fetch an image, get some data from the Internet and so on.

Problem with having Drawer in MyCustomDrawerWidget:

  • Loading time, bad user experience

I would suggest getting the hybrid approach to solve the problem.

  • Trigger the network calls in MainPage
  • Send future to the MyCustomDrawerWidget (If the user opens drawer before network call completes, we can show loader based on future value)
  • Move the logic and contents related to drawer into MyCustomDrawerWidget.

    class MyCustomDrawerWidget extends StatelessWidget {
      var _future;
    
      MyCustomDrawerWidget(this._future);
    
      @override
      Widget build(BuildContext context) {
        return new Drawer(
          child: FutureBuilder(
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                var data = snapshot.data;
                return new Text(data);
              } else {
                return new Text("loading");
              }
            },
            future: _future,
          ),
        );
      }
    }
    

And in MainPage

@override
Widget build(BuildContext context) {
  val future = Future.delayed(Duration(seconds: 10), () => "new value") //network call
  return Scaffold(
      appBar: AppBar(
        title: Text('Home page'),
      ),
      drawer: MyCustomDrawerWidget(future),
      body: ...
  );
}

Hope this helps.

Upvotes: 2

boformer
boformer

Reputation: 30103

I had the same problem. The Scaffold only attaches the drawer when necessary.

The solution: Move the data loading logic into the parent widget. You can still encapsulate it in a separate model class (e.g. DrawerModel), but this class must be instantiated when the parent widget is initialized. Keep it in the State of the parent. Pass DrawerModel to your drawer widget.

You have to be a bit careful when using streams and futures, because those usually can not be resubscribed. I would recommend you to use a StreamBuilder in the drawer widget that is connected to a BehaviorSubject in your DrawerModel.

That's very much the BLoC pattern. Do you need a code example?

Upvotes: 1

Related Questions