【问题标题】:How to create a dynamic TabBarView/ Render a new Tab with a function in Flutter?如何在 Flutter 中使用函数创建动态 TabBarView/渲染新 Tab?
【发布时间】:2018-10-06 18:33:12
【问题描述】:

所以我学习了 Flutter 已经有一段时间了,我陷入了困境。对不起,如果这是一个愚蠢的问题。我目前正在尝试构建类似卡片标签的东西。信息和小部件将存储在卡片中。

想象一下 Tinder 之类的东西,它们有多个卡片叠并左右滑动进行导航。

我计划创建它,但我似乎找不到使用按钮添加/渲染新卡片的方法。

这就像向列表中添加一些东西一样,Flutter 将使用一个 ListView 构建器,我们将在其中添加到列表中。但是没有 TabBarView 构建器。这是不可能的事情吗?我尝试在选项卡中放置一个列表,但它仍然不一样。

我在这里创建了一些基本骨架来帮助传达我的意思。所以卡片会左右滑动,appBar中有一个添加卡片的按钮。长度现在是 2,我希望按钮渲染第三张卡片。这可能吗?

提前致谢!

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,
                  )
              ),
            ),
          ),
        ],
      ),
    );
  }
}

【问题讨论】:

    标签: tabs dart widget flutter flutter-layout


    【解决方案1】:

    如果您需要修改数组,就会出现问题。它们在于这样一个事实,即在修改数组时,您没有机会使用相同的控制器。

    对于这种情况,您可以使用下一个自定义小部件:

    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);
          }
        }
      }
    

    【讨论】:

    • 这很有帮助。谢谢(y)
    • @Yuriy Luchaninov,如果我想根据选择为选项卡视图添加“删除页面”选项,则应从 PageBuilder 中删除相应页面。
    • 请找到标签删除图片链接参考。 ibb.co/9NPNkkZ
    • 好朋友
    • 这是黄金。非常感谢。
    【解决方案2】:

    试试这个。

    要制作动态标签,您可以使用列表并在每次按钮单击时继续附加列表。

    技巧:清除列表并重新绘制一个空的小部件,然后根据您的列表再次绘制小部件。

     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(),
          ),
        );
      }
    }
    

    希望这会有所帮助:)

    【讨论】:

    • 我跑了这个例子..我无法获得当前标签的索引..因为您使用地图来显示所有标签,我需要索引来显示某些组件并隐藏一些
    • 此解决方案存在问题,当在 build() 中实例化新的 TabController 时,TabPageSelector 会丢失其状态。我也在努力解决这个问题。请看:webmshare.com/ZQ00d
    猜你喜欢
    • 1970-01-01
    • 2012-10-06
    • 2019-10-20
    • 2015-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-22
    • 2019-11-17
    相关资源
    最近更新 更多