0

I am trying to implement Flutter's Tab Bar with 3 tabs and an AnimatedList inside those tabs. I want to use the same list and filter the list according to each tab (past tasks, today's tasks, and future tasks), however during my implementation of the tab bar together with the animatedlist I am getting an error regarding a duplicate global key in the widget tree. https://pastebin.com/iAW6DH9m . What would be the best way to deal with this error? Thank you for any help.

edit: I tried using this method to fix this. Multiple widgets used the same GlobalKey while it did fix my error I was then unable to access "currentstate" method on the key to be able to add more items to the list. I then tried a similar method using using GlobalKey and it resulted in a similar error of duplicate global keys.

This is my tab bar implementation

import 'package:flutter/material.dart';
import 'search_widget.dart';
import 'animatedlist_widget.dart';

class Dashboard extends StatefulWidget {
  @override
  _DashboardState createState() => _DashboardState();
}

class _DashboardState extends State<Dashboard> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
        centerTitle: true,
        actions: <Widget>[
          new IconButton(icon: new Icon(Icons.grid_on), onPressed: null)
        ],
        title: new Text('Dashboard'),
        elevation: 0,
      ),
      floatingActionButton: new FloatingActionButton(
          onPressed: () {
            _onFabPress(context);
          },
          child: new Icon(Icons.add)),
      body: Scaffold(
        appBar: new SearchWidget(
          onPressed: () => print('implement search'),
          icon: Icons.search,
          title: 'Search',
          preferredSize: Size.fromHeight(50.0),
        ),
        body: DefaultTabController(
          length: 3,
          child: Scaffold(
            appBar: PreferredSize(
              preferredSize: Size.fromHeight(kToolbarHeight),
              child: Container(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: new TabBar(
                      unselectedLabelColor: Colors.black45,
                      labelColor: Colors.white,
                      indicator: CustomTabIndicator(),
                      tabs: <Widget>[
                        new Tab(text: "Past"),
                        new Tab(text: "Today"),
                        new Tab(text: "Future")
                      ]),
                ),
              ),
            ),
            body: new TabBarView(
              children: <Widget>[
                AnimatedTaskList(),
                AnimatedTaskList(),
                AnimatedTaskList()
              ],
            )
          ),
        ),
      ),
    );
  }

  void _onFabPress(context) {
    AnimatedTaskList().addUser();
  }
    /*showModalBottomSheet(
        context: context,
        builder: (BuildContext bc) {
          return Container(
              child: new Wrap(children: <Widget>[
            new TextField(
                decoration: InputDecoration(
                    border: OutlineInputBorder(),
                    hintText: 'Enter Task Title')),
            new TextField(
                decoration: InputDecoration(
              border: OutlineInputBorder(),
              hintText: 'Enter Task Details',
            )),
          ]));
        });
  }*/
}

class CustomTabIndicator extends Decoration {
  @override
  BoxPainter createBoxPainter([onChanged]) {
    // TODO: implement createBoxPainter
    return new _CustomPainter(this, onChanged);
  }
}

class _CustomPainter extends BoxPainter {
  final CustomTabIndicator decoration;

  _CustomPainter(this.decoration, VoidCallback onChanged)
      : assert(decoration != null),
        super(onChanged);

  @override
  void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
    // TODO: implement paint
    assert(configuration != null);
    assert(configuration.size != null);

    final indicatorHeight = 30.0;
    final Rect rect = Offset(
            offset.dx, (configuration.size.height / 2) - indicatorHeight / 2) &
        Size(configuration.size.width, indicatorHeight);
    final Paint paint = Paint();
    paint.color = Colors.blueAccent;
    paint.style = PaintingStyle.fill;
    canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(30)), paint);
  }
}

This is my animatedlist class:

import 'package:flutter/material.dart';

final GlobalKey<AnimatedListState> _listKey = GlobalKey();

class AnimatedTaskList extends StatefulWidget {

  void addUser() {
    int index = listData.length;
    listData.add(
      TaskModel(
        taskTitle: "Grocery Shopping",
        taskDetails: "Costco",
      ),
    );
    _listKey.currentState
        .insertItem(index, duration: Duration(milliseconds: 500));
  }

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

class _AnimatedTaskListState extends State<AnimatedTaskList> {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      body: SafeArea(
          child: AnimatedList(
              key: _listKey,
              initialItemCount: listData.length,
              itemBuilder:
                  (BuildContext context, int index, Animation animation) {
                return Card(
                    child: FadeTransition(
                        opacity: animation,
                        child: ListTile(
                          title: Text(listData[index].taskTitle),
                          subtitle: Text(listData[index].taskDetails),
                          onLongPress: () {
                            //todo delete user
                          },
                        )));
              })),
    );
  }
}

class TaskModel {
  TaskModel({this.taskTitle, this.taskDetails});
  String taskTitle;
  String taskDetails;
}

List<TaskModel> listData = [
  TaskModel(
    taskTitle: "Linear Algebra",
    taskDetails: "Chapter 4",
  ),
  TaskModel(
    taskTitle: "Physics",
    taskDetails: "Chapter 9",
  ),
  TaskModel(
    taskTitle: "Software Construction",
    taskDetails: "Architecture",
  ),
];

Screenshot

1 Answers1

0

I fixed my issue by moving

final GlobalKey<AnimatedListState> _listKey = GlobalKey(); 

into my _AnimatedTaskListState class, and adding a constructor and private key to my AnimatedTaskList class

  final GlobalKey<AnimatedListState> _key;
  AnimatedTaskList(this._key);

  @override
  _AnimatedTaskListState createState() => _AnimatedTaskListState(_key);

then in my tab bar implementation I changed it to reflect my new constructor

AnimatedTaskList(GlobalKey<AnimatedListState>(debugLabel: "key 1"));
AnimatedTaskList(GlobalKey<AnimatedListState>(debugLabel: "key 2"));
AnimatedTaskList(GlobalKey<AnimatedListState>(debugLabel: "key 3"));