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

2024-05-01

所以我学习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,
                  )
              ),
            ),
          ),
        ],
      ),
    );
  }
}

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

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

import 'package:flutter/material.dart';
  
void main() => runApp(MyApp());
 

class MyApp extends StatelessWidget {
  const MyApp({super.key});

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

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @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: const Icon(Icons.add),
      ),
    );
  }
}

class CustomTabView extends StatefulWidget {
  const CustomTabView({
    super.key,
    required this.itemCount,
    required this.tabBuilder,
    required this.pageBuilder,
    this.stub,
    this.onPositionChange,
    this.onScroll,
    this.initPosition,
  });

  final int itemCount;
  final IndexedWidgetBuilder tabBuilder;
  final IndexedWidgetBuilder pageBuilder;
  final Widget? stub;
  final ValueChanged<int>? onPositionChange;
  final ValueChanged<double>? onScroll;
  final int? initPosition;

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

class CustomTabsState extends State<CustomTabView>
    with TickerProviderStateMixin {
  late TabController controller;
  late int _currentCount;
  late 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 != null) {
              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),
            ),
          ),
        ),
      ],
    );
  }

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

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


  
  
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Flutter 中使用函数创建动态 TabBarView/ 渲染新 Tab? 的相关文章

随机推荐

  • 仅在一个 JTable 单元格中的复选框

    我想创建一个JTable有 2 列 看起来像一个调查 所以左边是问题 右边是用户可以给出他的答案 但在一行的右侧应该有一个复选框 以便用户只能回答是或否 这可以用JTable 我怎样才能做到这一点 regards 您在评论中指出 我用一列
  • 如何从 CloudFormation 中的 Elastic Beanstalk 环境中提取负载均衡器名称

    我使用以下代码片段在 CloudFormation 中创建了 Elastic Beanstalk 和 CloudWatch 警报 ElasticBeanstalkEnvironment Type AWS ElasticBeanstalk E
  • 使用 Mapstruct 将对象列表转换为长 ID 列表

    我在用MapStruct将实体转换为 DTO 我有一个实体 A 和实体 B 的列表 public class A List b bs 我想要 ADto 类中的 B id 列表 public class ADto List b
  • 如何禁用将包上传到 PyPi 除非将 --public 传递给上传命令

    我正在开发包并将包的开发 测试 等版本上传到本地 devpi 服务器 为了防止意外上传到PyPi 我采用了以下常见做法 setup classifiers Programming Language Python Programming La
  • 是否可以在选择器中进行修剪?

    我想计算表单中所有为空的输入 对于空 我的意思是它的值在修剪其值后为空 如果用户插入空格也为空 这个 jquery 对它们进行计数 但不包括修剪 text filter value length 有一些 jquery 可以用来在选择器中修剪
  • 如何继续使用适用于 AWS Cognito 的 AD FS SAML?

    我正在设置 AD FS 来生成 SAML 元数据以连接到 AWS Cognito 用户池 我已经生成了 xml 元数据并将其上传到用户池 我应该在 AD FS 站点上创建信任中继吗 是否还有其他步骤可以让我的 AD 用户可用于 Web 应用
  • 如何集成Django和Cygwin?

    我有一个安装了 cygwin python 和 django 的 Windows 盒子 现在我想运行 django admin 但是当我这样做时 我收到错误 django admin py c Python26 python exe can
  • 无法获得 S.M.A.R.T.外部驱动器的信息

    我正在尝试获取外部 USB 驱动器的 SMART 信息 我使用以下查询来获取驱动器的温度 但是该查询始终返回集合中的单个对象 即我的内部 HDD ManagementObjectSearcher searcher new Managemen
  • 使用 python 中 pandas 的 read_excel 函数将日期保留为字符串

    Python 2 7 10 尝试过 pandas 0 17 1 函数 read excel 尝试过 pyexcel 0 1 7 pyexcel xlsx 0 0 7 函数 get records 在Python中使用pandas时可以读取e
  • 基于范围的 for 带大括号初始化器而不是非常量值?

    我正在尝试迭代一些std lists 对它们中的每一个进行排序 这是天真的方法 include
  • Box2d 自定义多边形和精灵不匹配

    我正在使用物理编辑器在 Box2d 中创建多边形 它生成多边形并在非视网膜显示器中工作正常 但在视网膜显示器中不起作用 我已附上两个显示器的屏幕截图 现在 当谈到视网膜显示器时 未设置多边形在汽车上方 这是该图像 这是我在项目中使用的代码
  • 如何使用包含点/句点的 MVC5 基于属性的路由?

    我基本上有一个开箱即用的 MVC 5 2 应用程序 启用了基于属性的路由 调用routes MapMvcAttributeRoutes 从提供的RouteConfig RegisterRoutes RouteCollection route
  • 在应用程序中搜索对象的设计模式

    需要一些有关设计模式的帮助 我正在创建一个应用程序 该应用程序在存储在单独表中的数据库中的对象上具有不同类型 例如 我有 5 种对象 A B C D E 我在数据库中有 5 个不同的表来存储每个对象 现在 我想在我的应用程序中实现搜索功能
  • 如何使用 INSTEAD OF 触发器获取插入表中的新记录的标识

    我在表上使用 INSTEAD OF 插入触发器来设置该行上递增的版本号 并将该行复制到第二个历史记录 审核表 这些行插入到两个表中都没有问题 但是 我无法将第一个表中的新身份返回给用户 Schema CREATE TABLE Table1
  • “pow”的使用含糊不清

    大家好 我尝试过 当有人给出一秒钟时 我会得到十亿秒的实时数据 所以我就写了这样的代码 我必须将第二次乘以 10 9 但出现错误 pow 的使用含糊不清 func gigaSecond second Int gt Int var gigas
  • 在 Swift 中获取 UIPickerView 控件的选定值

    如何在 Swift 中获取 UIPickerView 控件的选定值 我尝试过这样的事情 labelTest text Spinner1 selectedRowInComponent 0 description 但这仅返回选定的索引 我需要这
  • 二维向量的迭代器

    如何为 2d 向量 向量的向量 创建迭代器 虽然你的问题是not非常清楚 我假设您的意思是 2D 向量表示向量的向量 vector lt vector
  • 哪种编写回调的方法更好?

    只要看看我现在写的东西 我就可以看到一个小得多 所以就代码高尔夫 http en wikipedia org wiki Code golf Option 2是更好的选择 但就哪个更干净而言 我更喜欢Option 1 我真的很喜欢社区对此的意
  • d3.select 不适用于特殊字符

    我正在使用 d3 js 处理简单的图表 假设下面的 div 是我计划放置 d3 的 svg 容器的地方 div 但是当我使用 d3 select my div chart 我无法选择特定的 div 但是通过使用 java 脚本选择器 它可以
  • 如何在 Flutter 中使用函数创建动态 TabBarView/ 渲染新 Tab?

    所以我学习flutter有一段时间了 就卡在这个了 抱歉 如果这是一个新手问题 我目前正在尝试构建类似卡片选项卡的东西 信息和小部件将存储在卡中 想象一下像 Tinder 这样的东西 他们有多个卡片堆 并且左右滑动即可导航 我计划创建它 但