0

I have a TabBar working ok with a FutureBuilder. This executes just once and when I switch between tabs it doesn't need to load again. When I perform some changes to the elements inside this tab I reload it. Until here all good.

The problem is that this is creating some complexity since I have to do now more and more updates to the inner elements.

So having a StreamBuilder fixes the issue but it triggers again when switching tabs. First, the UX is not that good showing the loader every time and second, this is getting documents from Firebase incrementing costs.

Is there a better way to show tabs from Firebase documents?

class BottomBarState extends State<BottomBar>
    with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {

@override
  bool get wantKeepAlive => true;

@override
  Widget build(BuildContext context) {
    super.build(context);

    return Container(
      child: tabs(),
    );
  }

return StreamBuilder<QuerySnapshot>(
      stream: FirebaseProfile().getPortfolios(),
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
        if (snapshot.hasError) return Text('Error: ${snapshot.error}');
        if (!snapshot.hasData) return const Loader();
        if (snapshot.data == null) return const Loader();

        portfolios = snapshot.data!.docs;

        return scaffold(); // the rest of it it's not really important since this Stream should execute only the first time and when I perform changes in the DB
      },
    );
  }
Dani
  • 3,128
  • 2
  • 43
  • 91

2 Answers2

1

You can use a StateFulWidget for each page and in the state add the AutomaticKeepAliveClientMixin, this would keep alive each tab and prevent the reload. Hope is useful

class Page extends StatefulWidget {
  Page({Key? key}) : super(key: key);

  @override
  State<Page> createState() => _PageState();
}

class _PageState extends State<Page> with AutomaticKeepAliveClientMixin {
  @override
  Widget build(BuildContext context) {
    return Container();
  }

  @override
  bool get wantKeepAlive => true;
}
Nicolás López
  • 430
  • 2
  • 8
  • Tested and it only loads the first time and still listening the changes also. Thanks! – Dani Aug 18 '22 at 08:07
  • Despite this works fine for the tab content, I tried to apply it also to the tabs (that I also get from Firebase with a StreamBuilder) and for some reasons it doesn't work there. When I switch tab this StreamBuilder is triggered again. Only difference is that I also use this class with TickerProviderStateMixin. Would it create any kind of conflict? I see no errors – Dani Aug 18 '22 at 10:51
  • The streambuilder would be always listening on any changes after it createds – Nicolás López Aug 18 '22 at 16:08
  • But I'm not talking about any DB change, just selecting another tab – Dani Aug 18 '22 at 17:19
  • I see some problems using AutomaticKeepAliveClientMixin and BottomNavigationBar. Not sure why it worked the first time. I need some workaround https://stackoverflow.com/questions/53011686/flutter-automatickeepaliveclientmixin-is-not-working-with-bottomnavigationbar – Dani Aug 18 '22 at 21:25
  • Would not work in that case, if you want to keep the state in that case use IndexedStack – Nicolás López Aug 19 '22 at 15:49
  • and remove the automatixkeepalive mixin since it is no longer necesary – Nicolás López Aug 19 '22 at 15:50
0

The problem comes with the way I declare the stream. This video explains it very well:

https://youtu.be/sqE-J8YJnpg

So the change would be something like this:

class BottomBarState extends State<BottomBar>
    with TickerProviderStateMixin {
  var portfoliosStream;

@override
  void initState() {
    super.initState();
    portfoliosStream = FirebaseProfile().getPortfolios();
  }

@override
  Widget build(BuildContext context) {
    super.build(context);

    return Container(
      child: tabs(),
    );
  }

return StreamBuilder<QuerySnapshot>(
      stream: portfoliosStream,
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
        if (snapshot.hasError) return Text('Error: ${snapshot.error}');
        if (!snapshot.hasData) return const Loader();
        if (snapshot.data == null) return const Loader();

        portfolios = snapshot.data!.docs;

        return scaffold(); // the rest of it it's not really important since this Stream should execute only the first time and when I perform changes in the DB
      },
    );
  }
Dani
  • 3,128
  • 2
  • 43
  • 91