动态标签栏颤动

时间:2019-09-24 10:35:43

标签: flutter flutter-layout tabbar

如果我希望用户在标签栏上添加/删除标签,该如何动态地进行操作?

现在我可以创建一个新标签,但创建时不能跳到正确的页面。

class _Page {
  _Page({ this.label, this.colors, this.iconup,this.iconfloatingbutton });

  final String label;
  final MaterialColor colors;
  final IconData iconfloatingbutton;
  final IconData iconup;

  Color get labelColor => colors != null ? colors.shade300 :     Colors.grey.shade300;
  bool get fabDefined => colors != null && iconfloatingbutton != null;
  Color get fabColor => colors.shade400;
  Icon get fabIcon => Icon(iconfloatingbutton);
  Key get fabKey => ValueKey<Color>(fabColor);
}

final List<_Page> _allPages = <_Page>[
  _Page(label: 'Blue', colors: Colors.indigo,iconup:Icons.text_fields,     iconfloatingbutton: Icons.add),
  _Page(label: 'Eco', colors: Colors.green,iconup:Icons.text_fields, iconfloatingbutton: Icons.create),
  _Page(label: 'No',iconup:Icons.text_fields,),
  _Page(label: 'Teal', colors: Colors.teal,iconup:Icons.text_fields, iconfloatingbutton: Icons.add),

];

class TabsFabDemo extends StatefulWidget {

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

class _TabsFabDemoState extends State<TabsFabDemo> with     SingleTickerProviderStateMixin {


  int initPosition = 1;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        color: Colors.green,
        child: SafeArea(
          child: CustomTabView(
            initPosition: initPosition,
            itemCount: _allPages.length,
            tabBuilder: (context, index) => Tab(text: _allPages[index].label),
            pageBuilder: (context, index) => Container(color:Colors.white,child: Text(_allPages[index].label)),
            onPositionChange: (index){
              print('current position: $index');
            },
            onScroll: (position) => print("POS : "+'$position'),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          addtab("a",null);
        },
        child: Icon(Icons.add),
      ),
    );
  }


  addtab(label,icon){
    setState(() {
      _allPages.add(new _Page(label: 'Blue', colors: Colors.indigo,iconup:Icons.satellite, iconfloatingbutton: Icons.add));
    });
  }


}

// ---------------------实施自定义标签------------------

class CustomTabView extends StatefulWidget {
  final int itemCount;
  final IndexedWidgetBuilder tabBuilder;
  final IndexedWidgetBuilder pageBuilder;
  final Widget stub;
  final ValueChanged<int> onPositionChange;
  final ValueChanged<double> onScroll;
  final int initPosition;

  CustomTabView({
    @required this.itemCount,
    @required this.tabBuilder,
    @required this.pageBuilder,
    this.stub,
    this.onPositionChange,
    this.onScroll,
    this.initPosition,
  });

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

class _CustomTabsState extends State<CustomTabView> with         TickerProviderStateMixin {
  TabController controller;
  int _currentCount;
  int _currentPosition;

  @override
  void initState() {
    _currentPosition = widget.initPosition ?? 0;
    print("INIT POSITION : "+widget.initPosition.toString());
    print("INIT POSITION : "+_currentPosition.toString());

    controller = TabController(
      length: widget.itemCount,
      vsync: this,
      initialIndex: _currentPosition,
    );
    controller.addListener(onPositionChange);
    controller.animation.addListener(onScroll);
    _currentCount = widget.itemCount;
    super.initState();
  }

  @override
  void didUpdateWidget(CustomTabView oldWidget) {

    if (_currentCount != widget.itemCount) {
      print("DID UPDATE POSITION : 0");

      controller.animation.removeListener(onScroll);
      controller.removeListener(onPositionChange);
      controller.dispose();

      if (widget.initPosition != null) {
        print("DID UPDATE POSITION : 1");

        _currentPosition = widget.initPosition;
      }

      if (_currentPosition > widget.itemCount - 1) {
        print("DID UPDATE POSITION : 2");

        _currentPosition = widget.itemCount - 1;
        _currentPosition = _currentPosition < 0 ? 0 :
        _currentPosition;    
        if (widget.onPositionChange is ValueChanged<int>) {
          print("DID UPDATE POSITION : 3");

          WidgetsBinding.instance.addPostFrameCallback((_){
            if(mounted) {
              print("DID UPDATE POSITION : 4");

              widget.onPositionChange(_currentPosition);
            }
          });
        }
      }


      _currentCount = widget.itemCount;
      setState(() {
        controller = TabController(
          length: widget.itemCount,
          vsync: this,    
          initialIndex: _currentPosition,
        );
        controller.addListener(onPositionChange);
        controller.animation.addListener(onScroll);
      });
    } else if (widget.initPosition != null) {
      controller.animateTo(widget.initPosition);
    }

    super.didUpdateWidget(oldWidget);
  }

  @override
  void dispose() {
    controller.animation.removeListener(onScroll);
    controller.removeListener(onPositionChange);
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (widget.itemCount < 1) return widget.stub ?? Container();

    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        Container(
          alignment: Alignment.center,
          child: TabBar(
            isScrollable: true,
            controller: controller,
            labelColor: Theme.of(context).primaryColor,
            unselectedLabelColor: Theme.of(context).hintColor,
            indicator: BoxDecoration(
              border: Border(
                bottom: BorderSide(
                  color: Theme.of(context).primaryColor,
                  width: 2,
                ),
              ),
            ),
            tabs: List.generate(
          widget.itemCount,
              (index) => widget.tabBuilder(context, index),
            ),
          ),
        ),
        Expanded(
          child: TabBarView(
            controller: controller,
            children: List.generate(
              widget.itemCount,
                  (index) => widget.pageBuilder(context, index),
            ),
          ),
        ),
      ],
    );
  }

  onPositionChange() {
    if (!controller.indexIsChanging) {
      _currentPosition = controller.index;
      if (widget.onPositionChange is ValueChanged<int>) {
        widget.onPositionChange(_currentPosition);
      }
    }
  }

  onScroll() {
    if (widget.onScroll is ValueChanged<double>) {
      widget.onScroll(controller.animation.value);
    }
  }
}

创建标签时,滚动条必须指向新生成的项目

删除标签时,滚动条必须指向上一个元素

1 个答案:

答案 0 :(得分:0)

为了动态添加或删除选项卡,您需要在有状态Widget的setState()中调用添加或删除方法。

要将控制器移至新添加的选项卡,只需将initPosition更新为新添加的选项卡索引,如下所示。我刚刚更改了TabsFabDemo类,因为其余所有代码都没问题(CustomTabBar和_Page类的实现)。

class TabsFabDemo extends StatefulWidget {
   @override
  _TabsFabDemoState createState() => _TabsFabDemoState();
}

class _TabsFabDemoState extends State<TabsFabDemo>
    with SingleTickerProviderStateMixin {
  int initPosition = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: CustomTabView(
          initPosition: initPosition,
          itemCount: data.length,
          tabBuilder: (context, index) => Tab(text: data[index].label),
          pageBuilder: (context, index) =>
              Center(child: Text(data[index].label)),
          onPositionChange: (index) {
            initPosition = index;
          },
          onScroll: (position) => print('$position'),
        ),
      ),
      floatingActionButton: Container(
        height: 130.0,
        width: 80.0,
        child: FloatingActionButton(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              IconButton(
                icon: Icon(Icons.add),
                onPressed: () {
                  setState(() {
                    data.add(_Page(
                        label: 'Blue',
                        colors: Colors.indigo,
                        iconup: Icons.satellite,
                        iconfloatingbutton: Icons.add));

                    int lastIndex = data.length;
                    initPosition = lastIndex;
                  });
                },
                padding: const EdgeInsets.all(0.0),
              ),
              IconButton(
                icon: Icon(Icons.remove),
                onPressed: () {
                  setState(() {
                    data.removeLast();
                  });
                },
                padding: const EdgeInsets.all(0.0),
              ),
            ],
          ),
        ),
      ),
    );
  }
}