如何在 Flutter 中使用函数创建动态 TabBarView/渲染新 Tab?
Posted
技术标签:
【中文标题】如何在 Flutter 中使用函数创建动态 TabBarView/渲染新 Tab?【英文标题】:How to create a dynamic TabBarView/ Render a new Tab with a function in Flutter? 【发布时间】: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,
)
),
),
),
],
),
);
【问题讨论】:
【参考方案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以上是关于如何在 Flutter 中使用函数创建动态 TabBarView/渲染新 Tab?的主要内容,如果未能解决你的问题,请参考以下文章