2
votes

I want to change title in appBar, when when I switch from one tab to another. In my current code do not do that because on change tab build is not called.

Thanks to all!!

  

      import 'package:flutter/material.dart';
        import 'package:provider/provider.dart';
        
        import '../models/task.dart';
        import '../widgets/tasks_list_mob.dart';
        import '../widgets/new_task.dart';
        
        class TaskListName {
          final String shortName;
          final String longName;
          TaskListName(this.shortName, this.longName);
        }
        
        class TaksScreen extends StatefulWidget {
          @override
          _TaksScreenState createState() => _TaksScreenState();
        }
        
        class _TaksScreenState extends State with TickerProviderStateMixin {
          TabController _tabController;
          final Map tasksListLabels = {
            0: TaskListName('Backlog', 'Tasks in backlog'),
            1: TaskListName('Tomorrow', 'Planed tasks for tomorrow'),
            2: TaskListName('Today', 'Your tasks for today'),
            3: TaskListName('Last work day', 'Tasks completed last working day'),
            4: TaskListName('Completed', 'Completed Tasks'),
            5: TaskListName('Backlog', 'Archived not completed tasks'),
          };
        
          @override
          void initState() {
            super.initState();
            _tabController = TabController(length: 6, vsync: this, initialIndex: 2);
          }
        
          @override
          void dispose() {
            _tabController.dispose();
            super.dispose();
          }
        
          Future showAddTaskDialog(BuildContext context) async {
            print(_tabController);
            await showDialog(
              context: context,
              builder: (context) => SimpleDialog(
                children: [NewTask(TaksBucket.backlog)],
              ),
            );
          }
        
          @override
          Widget build(BuildContext context) {
            final tasks = Provider.of>(context);
            return Scaffold(
              appBar: AppBar(
                title: Text(tasksListLabels[_tabController.index].longName),
                bottom: TabBar(
                  controller: _tabController,
                  isScrollable: true,
                  tabs: [
                    Tab(text: tasksListLabels[0].shortName),
                    Tab(text: tasksListLabels[1].shortName),
                    Tab(text: tasksListLabels[2].shortName),
                    Tab(text: tasksListLabels[3].shortName),
                    Tab(text: tasksListLabels[4].shortName),
                    Tab(text: tasksListLabels[5].shortName),
                  ],
                ),
              ),
              body: TabBarView(
                controller: _tabController,
                children: [
                  TasksListMob(tasks, TaksBucket.backlog),
                  TasksListMob(tasks, TaksBucket.tomorrow),
                  TasksListMob(tasks, TaksBucket.today),
                  TasksListMob(tasks, TaksBucket.completed),
                  TasksListMob(tasks, TaksBucket.completed),
                  TasksListMob(tasks, TaksBucket.archived),
                ],
              ),
              floatingActionButton: FloatingActionButton(
                child: Icon(Icons.add),
                onPressed: () => showAddTaskDialog(context),
              ),
              floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
            );
          }
        }
    
    

After @mkobuolys solution implementation - scroll handle is still missinng...


    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    
    import '../models/task.dart';
    import '../widgets/tasks_list_mob.dart';
    import '../widgets/new_task.dart';
    
    class TaskListName {
      final String shortName;
      final String longName;
      TaskListName(this.shortName, this.longName);
    }
    
    class TaksScreen extends StatefulWidget {
      @override
      _TaksScreenState createState() => _TaksScreenState();
    }
    
    class _TaksScreenState extends State with TickerProviderStateMixin {
      TabController _tabController;
      var _tabIndex = ValueNotifier(2);
      final Map tasksListLabels = {
        0: TaskListName('Backlog', 'Tasks in backlog'),
        1: TaskListName('Tomorrow', 'Planed tasks for tomorrow'),
        2: TaskListName('Today', 'Your tasks for today'),
        3: TaskListName('Last work day', 'Tasks completed last working day'),
        4: TaskListName('Completed', 'Completed Tasks'),
        5: TaskListName('Backlog', 'Archived not completed tasks'),
      };
    
      @override
      void initState() {
        super.initState();
        _tabController = TabController(length: 6, vsync: this, initialIndex: 2);
      }
    
      Future showAddTaskDialog(BuildContext context) async {
        await showDialog(
          context: context,
          builder: (context) => SimpleDialog(
            children: [NewTask(TaksBucket.backlog)],
          ),
        );
      }
    
      @override
      Widget build(BuildContext context) {
        final tasks = Provider.of>(context);
        return Scaffold(
          appBar: AppBar(
            title: **ValueListenableBuilder(
              valueListenable: _tabIndex,
              builder: (context, value, child) =>
                  Text(tasksListLabels[value].longName),
            ),**
            bottom: TabBar(
              controller: _tabController,
              isScrollable: true,
              onTap: (value) {
                _tabIndex.value = _tabController.index;
              },
              tabs: [
                Tab(text: tasksListLabels[0].shortName),
                Tab(text: tasksListLabels[1].shortName),
                Tab(text: tasksListLabels[2].shortName),
                Tab(text: tasksListLabels[3].shortName),
                Tab(text: tasksListLabels[4].shortName),
                Tab(text: tasksListLabels[5].shortName),
              ],
            ),
          ),
          body: TabBarView(
            controller: _tabController,
            children: [
              TasksListMob(tasks, TaksBucket.backlog),
              TasksListMob(tasks, TaksBucket.tomorrow),
              TasksListMob(tasks, TaksBucket.today),
              TasksListMob(tasks, TaksBucket.completed),
              TasksListMob(tasks, TaksBucket.completed),
              TasksListMob(tasks, TaksBucket.archived),
            ],
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () => showAddTaskDialog(context),
          ),
        );
      }
    }

At the end it was really easy to make it done!

  • I did create ValueNotifier _tabIndex

  • At init state I addListener on TabControler. Listener set value for _tabIndex(ValueNotifier). I did dispose Listener in dispose...

  • In build method add ValueListenableBuilder to title.

  • Final code:

      import 'package:flutter/material.dart';
      import 'package:provider/provider.dart';
    
      import '../models/task.dart';
      import '../widgets/tasks_list_mob.dart';
      import './tasks_notifications_screen.dart';
      import '../widgets/new_task.dart';
      import '../widgets/count_batch_icon_button.dart';
      import '../consts/consts.dart';
      import '../util/tasks_helper.dart';
    
      class TaskListName {
        final String shortName;
        final String longName;
        final TaksBucket bucket;
        TaskListName(this.shortName, this.longName, this.bucket);
      }
    
      class TaksScreen extends StatefulWidget {
        @override
        _TaksScreenState createState() => _TaksScreenState();
      }
    
      class _TaksScreenState extends State with TickerProviderStateMixin {
        TabController _tabController;
        var _tabIndex = ValueNotifier(2);
    
        final Map tasksListLabels = {
          0: TaskListName('Backlog', 'Tasks in backlog', TaksBucket.backlog),
          1: TaskListName(
              'Tomorrow', 'Planed tasks for tomorrow', TaksBucket.tomorrow),
          2: TaskListName('Today', 'Your tasks for today', TaksBucket.today),
          3: TaskListName('Last work day', 'Tasks completed last working day',
              TaksBucket.completed),
          4: TaskListName('Completed', 'Completed Tasks', TaksBucket.completed),
          5: TaskListName(
              'Archived', 'Archived not completed tasks', TaksBucket.archived),
        };
    
        @override
        void initState() {
          super.initState();
          _tabController = TabController(length: 6, vsync: this, initialIndex: 2);
          _tabController.addListener(() {
            _tabIndex.value = _tabController.index;
          });
        }
    
        @override
        void dispose() {
          super.dispose();
          _tabController.removeListener(() {
            _tabIndex.value = _tabController.index;
          });
        }
    
        Future showAddTaskDialog(BuildContext context) async {
          await showDialog(
            context: context,
            builder: (context) => SimpleDialog(
              children: [NewTask(tasksListLabels[_tabController.index].bucket)],
            ),
          );
        }
    
        @override
        Widget build(BuildContext context) {
          final tasks = Provider.of>(context);
          return Scaffold(
            appBar: AppBar(
              title: ValueListenableBuilder(
                valueListenable: _tabIndex,
                builder: (context, value, child) =>
                    Text(tasksListLabels[value].longName),
              ),
              actions: [
                CountBatchIconButton(
                  TasksHelper.numberOfTasksForNotification(tasks),
                  Icon(Icons.notifications_none),
                  () => Navigator.of(context).pushNamed(TasksNotifications.routeName),
                ),
              ],
              bottom: TabBar(
                controller: _tabController,
                isScrollable: true,
                indicatorColor: TaskConsts.mainColor,
                tabs: [
                  Tab(text: tasksListLabels[0].shortName),
                  Tab(text: tasksListLabels[1].shortName),
                  Tab(text: tasksListLabels[2].shortName),
                  Tab(text: tasksListLabels[3].shortName),
                  Tab(text: tasksListLabels[4].shortName),
                  Tab(text: tasksListLabels[5].shortName),
                ],
              ),
            ),
            body: TabBarView(
              controller: _tabController,
              children: [
                TasksListMob(tasks, TaksBucket.backlog),
                TasksListMob(tasks, TaksBucket.tomorrow),
                TasksListMob(tasks, TaksBucket.today),
                TasksListMob(
                  tasks,
                  TaksBucket.completed,
                  isLastWorkingDay: true,
                ),
                TasksListMob(tasks, TaksBucket.completed),
                TasksListMob(tasks, TaksBucket.archived),
              ],
            ),
            floatingActionButton: FloatingActionButton(
              backgroundColor: TaskConsts.mainColor,
              child: Icon(Icons.add),
              onPressed: () => showAddTaskDialog(context),
            ),
          );
        }
      }
      
2

2 Answers

2
votes

Inside the TabBar widget, add onTap callback with the setState method to trigger rebuild, hence updating the name:

...
appBar: AppBar(
  title: Text(tasksListLabels[_tabController.index].longName),
  bottom: TabBar(
    controller: _tabController,
    isScrollable: true,
    tabs: [
      Tab(text: tasksListLabels[0].shortName),
      Tab(text: tasksListLabels[1].shortName),
      Tab(text: tasksListLabels[2].shortName),
      Tab(text: tasksListLabels[3].shortName),
      Tab(text: tasksListLabels[4].shortName),
      Tab(text: tasksListLabels[5].shortName),
    ],
    onTap: (_) {
      setState((){});
    },
  ),
),
...
0
votes

You can also use the same text of your tab where you switching with the help of the list