简体   繁体   English

如何创建动态 TabBarView/在 Flutter 中使用函数渲染新选项卡?

[英]How to create a dynamic TabBarView/ Render a new Tab with a function in Flutter?

So I have been learning flutter in a while and I am stuck in this.所以我已经学习了一段时间的颤振,但我陷入了困境。 Sorry if it is a noobish question.对不起,如果这是一个noobish问题。 I am currently trying to build something like a Card Tab.我目前正在尝试构建类似 Card Tab 的东西。 The information and widget will be stored in a card.信息和小部件将存储在卡片中。

Imagine something like Tinder, where they have multiple card stack and swipe left and right to navigate.想象一下像 Tinder 这样的东西,他们有多个卡片堆,可以左右滑动来导航。

I plan to create that but I cannot seems to find a way to add/render a new card with a button.我打算创建它,但我似乎无法找到一种方法来添加/呈现带有按钮的新卡片。

It's like adding something to the list, Flutter will use a ListView builder where we add to the list.这就像在列表中添加一些东西,Flutter 将使用 ListView 构建器,我们将在其中添加到列表中。 But there is no TabBarView builder.但是没有 TabBarView 构建器。 Is this something that is not possible to do?这是不可能做到的事情吗? I try putting a list inside a tab but it's still wont be the same.我尝试在选项卡中放置一个列表,但它仍然不会相同。

I created some basic skeleton here to help convey my meaning.我在这里创建了一些基本的骨架来帮助传达我的意思。 So the card will be swipe left and right and there is a button in the appBar to add card.所以卡片会左右滑动,appBar中有一个按钮可以添加卡片。 Lenght is 2 now and I wanted the button to render the 3rd card. Lenght 现在是 2,我想要按钮来渲染第三张卡片。 Is this possible?这可能吗?

Thanks in advance!提前致谢!

 import 'package:flutter/material.dart'; void main() { runApp(new MaterialApp( home: new CardStack(), )); } class CardStack extends StatefulWidget { @override _MainState createState() => new _MainState(); } class _MainState extends State<CardStack> with SingleTickerProviderStateMixin { TabController _cardController; @override void initState() { super.initState(); _cardController = new TabController(vsync: this, length: 2); } @override void dispose() { _cardController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return new Scaffold( backgroundColor: Colors.grey[300], appBar: new AppBar( actions: <Widget>[ new IconButton( icon: const Icon(Icons.add), tooltip: 'Add Tabs', onPressed: null, ), ], title: new Text("Title Here"), bottom: new PreferredSize( preferredSize: const Size.fromHeight(20.0), child: new Theme( data: Theme.of(context).copyWith(accentColor: Colors.grey), child: new Container( height: 50.0, alignment: Alignment.center, child: new TabPageSelector(controller: _cardController), ), ) ) ), body: new TabBarView( controller: _cardController, children: <Widget>[ new Center( child: new Card( child: new Container( height: 450.0, width: 300.0, child: new IconButton( icon: new Icon(Icons.favorite, size: 100.0), tooltip: 'Favorited', onPressed: null, ) ), ), ), new Center( child: new Card( child: new Container( height: 450.0, width: 300.0, child: new IconButton( icon: new Icon(Icons.local_pizza, size: 50.0,), tooltip: 'Pizza', onPressed: null, ) ), ), ), ], ), ); } }

Problems arise if you need to modify the arrays.如果您需要修改数组,就会出现问题。 They consist in the fact that when modifying an array you do not have the opportunity to use the same controller.它们包括在修改数组时您没有机会使用相同的控制器。

在此处输入图片说明

You can use the next custom widget for this case:对于这种情况,您可以使用下一个自定义小部件:

import 'package:flutter/material.dart';

  void main() => runApp(MyApp());

  class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Flutter Demo',
        home: MyHomePage(),
      );
    }
  }

  class MyHomePage extends StatefulWidget {
    @override
    _MyHomePageState createState() => _MyHomePageState();
  }

  class _MyHomePageState extends State<MyHomePage> {
    List<String> data = ['Page 0', 'Page 1', 'Page 2'];
    int initPosition = 1;

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        body: SafeArea(
          child: CustomTabView(
            initPosition: initPosition,
            itemCount: data.length,
            tabBuilder: (context, index) => Tab(text: data[index]),
            pageBuilder: (context, index) => Center(child: Text(data[index])),
            onPositionChange: (index){
              print('current position: $index');
              initPosition = index;
            },
            onScroll: (position) => print('$position'),
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            setState(() {
              data.add('Page ${data.length}');
            });
          },
          child: Icon(Icons.add),
        ),
      );
    }
  }

  /// Implementation

  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;
      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) {
        controller.animation.removeListener(onScroll);
        controller.removeListener(onPositionChange);
        controller.dispose();

        if (widget.initPosition != null) {
          _currentPosition = widget.initPosition;
        }

        if (_currentPosition > widget.itemCount - 1) {
            _currentPosition = widget.itemCount - 1;
            _currentPosition = _currentPosition < 0 ? 0 : 
            _currentPosition;
            if (widget.onPositionChange is ValueChanged<int>) {
               WidgetsBinding.instance.addPostFrameCallback((_){
                if(mounted) {
                  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);
      }
    }
  }

Try this.尝试这个。 演示

To make dynamic tab you can use a List and keep appending the list on every button click.要制作动态选项卡,您可以使用列表并在每次单击按钮时继续附加列表。

Trick: Clear List and redraw an empty widget and again draw the widgets as per your list.技巧:清除列表并重新绘制一个空的小部件,然后根据您的列表再次绘制小部件。

 import 'package:flutter/material.dart';
void main() {
  runApp(new MaterialApp(
    home: new CardStack(),
  ));
}

class DynamicTabContent {
  IconData icon;
  String tooTip;

  DynamicTabContent.name(this.icon, this.tooTip);
}

class CardStack extends StatefulWidget {
  @override
  _MainState createState() => new _MainState();
}

class _MainState extends State<CardStack> with TickerProviderStateMixin {
  List<DynamicTabContent> myList = new List();

  TabController _cardController;

  TabPageSelector _tabPageSelector;

  @override
  void initState() {
    super.initState();

    myList.add(new DynamicTabContent.name(Icons.favorite, "Favorited"));
    myList.add(new DynamicTabContent.name(Icons.local_pizza, "local pizza"));

    _cardController = new TabController(vsync: this, length: myList.length);
    _tabPageSelector = new TabPageSelector(controller: _cardController);
  }

  @override
  void dispose() {
    _cardController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      backgroundColor: Colors.grey[300],
      appBar: new AppBar(
          actions: <Widget>[
            new Padding(
              padding: const EdgeInsets.all(1.0),
              child: new IconButton(
                icon: const Icon(
                  Icons.add,
                  size: 30.0,
                  color: Colors.white,
                ),
                tooltip: 'Add Tabs',
                onPressed: () {
                  List<DynamicTabContent> tempList = new List();

                  myList.forEach((dynamicContent) {
                    tempList.add(dynamicContent);
                  });

                  setState(() {
                    myList.clear();
                  });

                  if (tempList.length % 2 == 0) {
                    myList.add(new DynamicTabContent.name(Icons.shopping_cart, "shopping cart"));
                  } else {
                    myList.add(new DynamicTabContent.name(Icons.camera, "camera"));
                  }

                  tempList.forEach((dynamicContent) {
                    myList.add(dynamicContent);
                  });

                  setState(() {
                    _cardController = new TabController(vsync: this, length: myList.length);
                    _tabPageSelector = new TabPageSelector(controller: _cardController);
                  });
                },
              ),
            ),
          ],
          title: new Text("Title Here"),
          bottom: new PreferredSize(
              preferredSize: const Size.fromHeight(10.0),
              child: new Theme(
                data: Theme.of(context).copyWith(accentColor: Colors.grey),
                child: myList.isEmpty
                    ? new Container(
                        height: 30.0,
                      )
                    : new Container(
                        height: 30.0,
                        alignment: Alignment.center,
                        child: _tabPageSelector,
                      ),
              ))),
      body: new TabBarView(
        controller: _cardController,
        children: myList.isEmpty
            ? <Widget>[]
            : myList.map((dynamicContent) {
                return new Card(
                  child: new Container(
                      height: 450.0,
                      width: 300.0,
                      child: new IconButton(
                        icon: new Icon(dynamicContent.icon, size: 100.0),
                        tooltip: dynamicContent.tooTip,
                        onPressed: null,
                      )),
                );
              }).toList(),
      ),
    );
  }
}

Hope this helps :)希望这可以帮助 :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM