Flutter riverpod widget is calling twice

1.6k Views Asked by At

I'm trying to learn Flutter and Riverpod and try to have clean code. I just noticed that my widgets are called twice. How to avoid doing unnecessary operations ?

My project is just a navigation bar that loads a view depending on the button we click

My NavigationBarScreen :

class NavigationBarScreen extends HookWidget
{

  @override
  Widget build(BuildContext context) {

    print('build navigationScreen');
    final _pageModel = useProvider(navigationProvider.state);

    return SafeArea(
      child: Scaffold(
        body : context.read(navigationProvider).buildScreen(_pageModel.pageState),
        bottomNavigationBar: Container(
              margin: EdgeInsets.only(left : 8, right : 8, bottom: 8),
              decoration: BoxDecoration(
                borderRadius: BorderRadius.only(
                    topRight: Radius.circular(20), topLeft: Radius.circular(20)),
                boxShadow: [
                  BoxShadow(color: AppColors.colorShadowLight, spreadRadius: 0, blurRadius: 10),
                ],
              ),
              child: ClipRRect(
                borderRadius: BorderRadius.circular(50.0),
                child: BottomNavigationBar(
                  type: BottomNavigationBarType.fixed,
                  backgroundColor: AppColors.colorBgDark,
                  fixedColor: AppColors.colorContrastOrange,
                  unselectedItemColor: AppColors.colorFontLight2,
                  currentIndex: _pageModel.navigationIndexItem,
                  showSelectedLabels: false,
                  showUnselectedLabels: false,
                  onTap: context.read(navigationProvider).selectPage,
                  items: [
                    BottomNavigationBarItem(
                      icon: Icon(Icons.home),
                      title: Text('Home'),
                    ),
                    BottomNavigationBarItem(
                      icon: Icon(Icons.settings),
                      title: Text('Settings'),
                    ),
                  ],
                ),
              ),
            ),
      ),
    );
  }

}

My navigationNotifier :

enum NavigationBarState
{
  HOME, PROFIL
}

class NavigationNotifier extends StateNotifier<NavigationBarModel>
{
  NavigationNotifier() : super(_initialPage);

  static const int _initialIndex = 0;
  static const NavigationBarState _initialState = NavigationBarState.HOME;
  static const _initialPage = NavigationBarModel(pageState : _initialState, navigationIndexItem : _initialIndex);

  void selectPage(int i)
  {
    switch (i)
    {
      case 0:
        state = NavigationBarModel(pageState : NavigationBarState.HOME, navigationIndexItem : i);
        break;
      case 1:
        state = NavigationBarModel(pageState : NavigationBarState.PROFIL, navigationIndexItem : i);
        break;
    }
  }

  Widget buildScreen(NavigationBarState page)
  {
    switch (page)
    {
      case NavigationBarState.HOME:
        return HomeScreen();
        break;
      case NavigationBarState.PROFIL:
        return Text("Page under construction");
        break;
    }
    return null;
  }

}

My navigationBarModel :

class NavigationBarModel {
  const NavigationBarModel({this.pageState, this.navigationIndexItem});
  final NavigationBarState pageState;
  final int navigationIndexItem;
}

In the run inspector I have this :

I/flutter (32738): build navigationScreen
I/flutter (32738): build homeScreen
I/flutter (32738): build navigationScreen
I/flutter (32738): build homeScreen

EDIT : In my navigationBarScreen I have changed this :

body : context.read(navigationProvider).buildScreen(_pageModel.pageState),

by this :

body : useProvider(navigationProvider).buildScreen(_pageModel.pageState),

The context.read() must be used in onPressed, onTap, ... but I have the same result, my navigationBarScreen is called twice ...

1

There are 1 best solutions below

1
On BEST ANSWER

I cannot run your code as there are parts missing but my best guess is that because are you watching both the provider itself, and the providers state, that would be why it's getting called twice.

@override
  Widget build(BuildContext context) {

    print('build navigationScreen');
    // Here you are listening to the state
    final _pageModel = useProvider(navigationProvider.state);

    return SafeArea(
      child: Scaffold(
        // Here you are listening to the provider
        body : context.read(navigationProvider).buildScreen(_pageModel.pageState),

Instead of getting the pageModel from the navigationProvider state and passing it back into the navigationProvider, I'd recommend refactoring the StateNotifier.

class NavigationNotifier extends StateNotifier<NavigationBarModel>
{
  NavigationNotifier() : super(_initialPage);

  static const int _initialIndex = 0;
  static const NavigationBarState _initialState = NavigationBarState.HOME;
  static const _initialPage = NavigationBarModel(pageState : _initialState, navigationIndexItem : _initialIndex);

  void selectPage(int i)
  {
    switch (i)
    {
      case 0:
        state = NavigationBarModel(pageState : NavigationBarState.HOME, navigationIndexItem : i);
        break;
      case 1:
        state = NavigationBarModel(pageState : NavigationBarState.PROFIL, navigationIndexItem : i);
        break;
    }
  }

  Widget buildScreen()
  {
    switch (state.pageState)
    {
      case NavigationBarState.HOME:
        return HomeScreen();
        break;
      case NavigationBarState.PROFIL:
        return Text("Page under construction");
        break;
    }
    return null;
  }

}

Then remove final _pageModel = useProvider(navigationProvider.state); from the top of your build method.