我创建了自己的标签实现,因为我需要的功能比当前的标签小部件还多。例如,我在应用程序中有很多向导,其中第2步必须有效,然后才能将第3步导航到第一个。
当TextField位于选项卡中时,它将按预期打开焦点对准的键盘。但是,由于发出了Tab键小部件(或更高级别的小部件)的重新渲染,因此键盘立即关闭。
如果我要全部添加,那么这个问题有很多代码副本-因此,我将首先尝试添加关键部分。希望这足以解释发生了什么。
首先我们有了Tab:这是传递给TabController的模型
typedef OnBuild = Widget Function(BuildContext context);
class Tab {
final IconData icon;
final String label;
final Color highlightedColor;
final OnBuild buildBody;
final Widget body;
Tab({
this.buildBody,
this.icon,
this.label,
this.highlightedColor,
this.highlighted = false,
this.selected = false,
this.body,
});
bool highlighted;
bool selected;
}
这里有TabController,将其作为StatefulWidget读取。尽管我创建了BaseController和BaseView试图将逻辑与渲染分离,但是从理论上讲,它是一个StatefulWidget。
class TabPageController extends BaseController {
final TabSelected tabSelected;
final List<Tab> tabs;
final Map<Tab, GlobalKey> _tabs = {};
Tab selectedTab;
TabPageController({@required this.tabs, this.tabSelected}) {
tabs.forEach((t) {
_tabs.putIfAbsent(t, () => GlobalKey());
});
/// Always select the first tab
selectedTab = tabs[0];
selectedTab.selected = true;
}
void selectTab(Tab tab) {
bool _refreshNeeded = false;
tabs.forEach((t) {
if (t == tab) {
if (!(tab.selected ?? true)) {
_refreshNeeded = true;
/// Set selected as true
/// on the one that is seleted
/// unless its already selected
t.selected = true;
selectedTab = t;
/// Let consumer know of the tab change
if (tabSelected != null) {
tabSelected(tabs.indexOf(t), t);
}
}
} else if (t.selected ?? false) {
/// Set selected as false on
/// the other one(s, theoretically)
/// that are selected
t.selected = false;
}
});
if (_refreshNeeded) {
refresh();
}
}
bool needPaint = false;
/// Highlight the tab
/// from other widget(s)
void highlightTab(Tab tab, bool highlight) {
if (tab.highlighted != highlight) {
tab.highlighted = highlight;
/// Re-draw the tab
_tabs[tab].currentState.setState(() {});
}
}
}
class TabPage extends BaseView {
final TabPageController controller;
TabPage({this.controller});
@override
Widget build(BuildContext context) {
if (!mounted) {
return Container();
}
print('BUILDING: ' + DateTime.now().toIso8601String());
if (controller._tabs == null || controller._tabs.length == 0) {
return Container();
}
if(controller.tabs.length == 1){
return controller.selectedTab.buildBody(context);
}
List<Widget> children = <Widget>[];
controller._tabs.forEach((tab, key) {
children.add(Expanded(
child:
_TabWidget(tab: tab, tabSelected: controller.selectTab, key: key),
));
});
return Column(
children: <Widget>[
/// Tab selector
Container(
height: getMinimizedListItemHeight(context),
child: Row(
children: children,
),
),
/// The tab body
Expanded(
child: controller.selectedTab.buildBody(context),
key: GlobalKey(),
),
],
);
}
}
class _TabWidget extends StatefulWidget {
final Tab tab;
final _InternalTabSelected tabSelected;
_TabWidget(
{@required this.tab, @required this.tabSelected, @required Key key})
: super(key: key);
@override
State<StatefulWidget> createState() => _TabWidgetState();
}
class _TabWidgetState extends State<_TabWidget> {
@override
Widget build(BuildContext context) {
Color color;
if (widget.tab.highlighted) {
color = widget.tab.highlightedColor;
if (!(widget.tab.selected ?? true)) {
color = color.withOpacity(0.5);
}
} else {
if (widget.tab.selected ?? false) {
color = ServiceProvider
.instance.instanceStyleService.appStyle.activeIconColor;
} else {
color = ServiceProvider
.instance.instanceStyleService.appStyle.inactiveIconColor;
}
}
return Column(
children: <Widget>[
Expanded(
child: Container(
child: IconButton(
onPressed: () {
widget.tabSelected(widget.tab);
},
icon: Icon(
widget.tab.icon,
size: getAppBarIconSize(context),
color: color,
),
),
),
),
Container(
height: getDefaultPadding(context),
color: color,
),
],
);
}
}
如果仅将一个Tab传递给控制器并且触发此条件,则它将按预期工作(因为此选项仅将Tab主体呈现为主体,而不是呈现标头部分)。这就是为什么我知道Tab实现会产生此错误的原因。
if(controller.tabs.length == 1){
return controller.selectedTab.buildBody(context);
}
我希望有人可以看到这里出了什么问题,否则我可能只需要在制表符更改上返回一个索引,然后让消费者根据该索引决定要呈现的内容,例如BottomNavigationBar可以执行的操作。但是,我希望将标签页与其主体部件紧密结合。
渲染中的以下代码是在以下事件上触发的:
print('BUILDING: ' + DateTime.now().toIso8601String());