skip to Main Content

I created a StateProvider:

final tabsStateProvider = StateNotifierProvider<TabsProvider, List<TabClass>>(
    (ref) => TabsProvider());

and I’m listening to it with ref.listen:

Inside ref.listen, when the state changes, I’m calling tabController.animateTo(1); but it’s not having any effect on the tabs. I’m not sure why.
I added a print statement in the ref.listen which gets called but tabController.animateTo(1); in particular is not getting called for some reason.

My class code:

class HomePage extends ConsumerStatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  HomePageState createState() => HomePageState();
}

class HomePageState extends ConsumerState<HomePage>
    with TickerProviderStateMixin {
  late TabController tabController;

  @override
  Widget build(BuildContext context) {
    List<TabClass> tabClassList = ref.watch(tabsStateProvider);
    TabsProvider tabsProvider = ref.watch(tabsStateProvider.notifier);
    tabController =
        TabController(length: ref.watch(tabsStateProvider).length, vsync: this);

    ref.listen(tabsStateProvider, (previousState, latestState) {
      debugPrint('TAB STATE -> $latestState');
      tabController.animateTo(1);
    });

    return Column(
      children: [
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: DottedBorder(
            color: Colors.black,
            strokeWidth: 1,
            dashPattern: const [5, 5],
            child: Row(
              children: [
                const Flexible(
                  child: Text(
                    'Create',
                    style: TextStyle(fontSize: 20),
                  ),
                ),
              ],
            ),
          ),
        ),
        Expanded(
          child: DefaultTabController(
            length: tabClassList.length,
            child: Column(
              children: [
                SizedBox(
                  height: 60,
                  child: AppBar(
                    bottom: TabBar(
                      controller: tabController,
                      isScrollable: true,
                      tabs: List.generate(
                        tabClassList.length,
                        (index) {
                          TabClass tabClass = tabClassList[index];
                          return Row(
                            mainAxisSize: MainAxisSize.min,
                            children: [
                              TitleWidget(title: tabClass.tabsTitle),
                              index != 0
                                  ? InkWell(
                                      onTap: () {
                                        tabsProvider.decrement(index);
                                      },
                                      child:
                                          Icon(Icons.close, color: Colors.red),
                                    )
                                  : SizedBox.shrink(),
                            ],
                          );
                        },
                      ),
                    ),
                  ),
                ),
                Expanded(
                  child: TabBarView(
                      controller: tabController,
                      children:
                          tabClassList.map((e) => e.tabBarViewWidget).toList()),
                ),
              ],
            ),
          ),
        ),
      ],
    );
  }
}

I expected the tab to animate to the second tab which is position 1.

This is my state class:

final tabsStateProvider = StateNotifierProvider<TabsProvider, List<TabClass>>(
    (ref) => TabsProvider());

class TabsProvider extends StateNotifier<List<TabClass>> {
  TabsProvider()
      : super([
          TabClass(tabsTitle: 'Tab 1', tabBarViewWidget: TabBarViewWidget())
        ]);

  void increment() => state = [
        ...state,
        TabClass(
            tabsTitle: 'Tab ${state.length + 1}',
            tabBarViewWidget: TabBarViewWidget())
      ];

  void decrement(int index) => state = [
        for (final tab in state)
          if (tab.tabsTitle != state[index].tabsTitle) tab
      ];
}

class TabClass {
  final String tabsTitle;
  final TabBarViewWidget tabBarViewWidget;

  TabClass({required this.tabsTitle, required this.tabBarViewWidget});
}

2

Answers


  1. Your TabClass objects must be immutable and have all final fields. Moreover, your List<TabClass> must be modified using the List.of(...) or state = [...state] method to change your state in TabsProvider.

    Login or Signup to reply.
  2. tabController.animateTo(1) : This is calling but widget is not updating . There is difference between ref.watch and ref.listen. Due to this the controller’s tab is not changing its positions .

    ref.listen:-

    The main difference between them is that, rather than rebuilding the widget/provider if the listened to provider changes, using ref.listen will instead call a custom function.

    That can be useful for performing actions when a certain change happens, such as showing a snackbar when an error happens.

    The ref.listen method needs 2 positional arguments, the first one is the Provider and the second one is the callback function that we want to execute when the state changes. The callback function when called will be passed 2 values, the value of the previous State and the value of the new State.

    The ref.listen method can be used inside the body of a provider.
    It is better to use for showing error message, Navigation,print , Scaffold etc.

    Check this link for preference .
    ref.listen() and ref.listen.

    I have updated your code and it’s working now.

    class HomePage extends ConsumerStatefulWidget {
      const HomePage({Key key}) : super(key: key);
    
      @override
      HomePageState createState() => HomePageState();
    }
    
    class HomePageState extends ConsumerState<HomePage>
        with TickerProviderStateMixin {
      TabController tabController;
    
      @override
      Widget build(BuildContext context) {
        final tabClassList = ref.watch(tabsStateProvider);
    
        tabController = TabController(length: tabClassList.length, vsync: this);
    
        tabController.animateTo(tabClassList.length - 1);
        ref.listen(tabsStateProvider,
            (List<TabClass> previousState, List<TabClass> latestState) {
          print('sdfsd');
    
    
            });
    
        return Column(
          children: [
            Expanded(
              child: DefaultTabController(
                length: tabClassList.length,
                child: Column(
                  children: [
                    SizedBox(
                      height: 100,
                      child: AppBar(
                        leading: InkWell(
                            onTap: () {
                              ref.read(tabsStateProvider.notifier).increment();
                            },
                            child: Icon(Icons.add)),
                        bottom: TabBar(
                          controller: tabController,
                          isScrollable: true,
                          tabs: List.generate(
                            tabClassList.length,
                            (index) {
                              TabClass tabClass = tabClassList[index];
                              return Row(
                                mainAxisSize: MainAxisSize.min,
                                children: [
                                  Text(tabClass.tabsTitle),
                                  index != 0
                                      ? InkWell(
                                          onTap: () {
                                            ref
                                                .read(tabsStateProvider.notifier)
                                                .decrement(index);
                                          },
                                          child:
                                              Icon(Icons.close, color: Colors.red),
                                        )
                                      : SizedBox.shrink(),
                                ],
                              );
                            },
                          ),
                        ),
                      ),
                    ),
                    Expanded(
                      child: TabBarView(
                          controller: tabController,
                          children:
                              tabClassList.map((e) => e.tabBarViewWidget).toList()),
                    ),
                  ],
                ),
              ),
            ),
          ],
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search