Flutter 顶部选项卡栏内部底部导航栏

Flutter 顶部选项卡栏内部底部导航栏,flutter,dart,flutter-navigation,Flutter,Dart,Flutter Navigation,我无法在我的BottomNavigationBar中获得要渲染的顶部TabBar。我只希望顶部选项卡栏显示在主页底部导航栏中。我知道我可以在主页上设置另一个脚手架,但我显然不想在脚手架内设置脚手架。通过以下代码,我得到了运行时错误: Another exception was thrown: RenderBox was not laid out: RenderViewport#d0e83 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE

我无法在我的
BottomNavigationBar
中获得要渲染的顶部
TabBar
。我只希望顶部
选项卡栏
显示在主页
底部导航栏
中。我知道我可以在主页上设置另一个脚手架,但我显然不想在脚手架内设置脚手架。通过以下代码,我得到了运行时错误:

Another exception was thrown: RenderBox was not laid out: RenderViewport#d0e83 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
home.dart:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class Home extends StatelessWidget {
  static const routeName = 'home';

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 2,
      child: Column(
        children: <Widget>[
          TabBar(
            tabs: <Widget>[
              Tab(
                text: '1',
                icon: Icon(Icons.add_a_photo),
              ),
              Tab(
                text: '2',
                icon: Icon(Icons.adb),
              ),
            ],
          ),
          TabBarView(
            children: <Widget>[
              Center(
                child: Text('1'),
              ),
              Center(
                child: Text('2'),
              )
            ],
          ),
        ],
      ),
    );
  }
}
import'包装:flift/cupertino.dart';
进口“包装:颤振/材料.省道”;
类Home扩展了无状态小部件{
静态常量routeName='home';
@凌驾
小部件构建(构建上下文){
返回DefaultTabController(
长度:2,
子:列(
儿童:[
塔巴(
选项卡:[
标签(
正文:“1”,
图标:图标(图标。添加照片),
),
标签(
正文:“2”,
图标:图标(Icons.adb),
),
],
),
塔巴视图(
儿童:[
居中(
子项:文本('1'),
),
居中(
子项:文本('2'),
)
],
),
],
),
);
}
}
main.dart:

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: ...,
      theme: ThemeData(primaryColor: Colors.red, accentColor: Colors.white),
      // home: Categories(),
      initialRoute: '/',
      routes: {
        '/': (ctx) => MyHomePage(0),
        ...,
      },
      // onGenerateRoute: (settings){
      //incase you're creating routes on the fly that arent named routes/generating route names during the app lifecycle
      // print(settings.arguments)
      // if(settings.name=='mealdetail'){
      //   return ... materialpageroute...
      // }
      // else
      //   return MaterialPageRoute(builder: (context) => Categories());
      // },
      onUnknownRoute: (settings) {
        //useful for catching routes as a last resort/couldnt find the page etc.../ 404 fallback page like web
        return MaterialPageRoute(builder: (context) => Home());
      },
    );
  }
}

class MyHomePage extends StatefulWidget {
  final int selectedScreenIndex;

  MyHomePage(this.selectedScreenIndex);

  @override
  _MyHomePageState createState() => _MyHomePageState(selectedScreenIndex);
}

class _MyHomePageState extends State<MyHomePage> {
  final List<Map<String, Object>> screens = [
    {'title': 'Home', 'screen': Home()},
    {...},
  ];

  _MyHomePageState(this.selectedScreenIndex);

  int selectedScreenIndex = 0;

  void selectScreen(int index) {
    setState(() {
      selectedScreenIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        // resizeToAvoidBottomInset: true,

        appBar: AppBar(
          title: Text(screens[selectedScreenIndex]['title']),
        ),
        drawer: MainDrawer(),
        body: screens[selectedScreenIndex]['screen'],
        bottomNavigationBar: BottomNavigationBar(
          onTap: selectScreen,
          backgroundColor: Theme.of(context).primaryColor,
          unselectedItemColor: Colors.white,
          selectedItemColor: Theme.of(context).accentColor,
          currentIndex: selectedScreenIndex,
          type: BottomNavigationBarType.fixed,
          items: [
            BottomNavigationBarItem(
                icon: Icon(Icons.home),
                title: Text('Home'),
                backgroundColor: Theme.of(context).primaryColor),
            ...,
          ],
        ));
  }
}
void main()=>runApp(MyApp());
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
标题:。。。,
主题:主题数据(原色:Colors.red,重音颜色:Colors.white),
//主页:类别(),
initialRoute:“/”,
路线:{
“/”:(ctx)=>MyHomePage(0),
...,
},
//onGenerateRoute:(设置){
//如果您正在动态创建未命名路由的路由/在应用程序生命周期中生成路由名称
//打印(设置.参数)
//if(settings.name='mealdeail'){
//返回…物料管理器路径。。。
// }
//否则
//返回MaterialPackageRoute(生成器:(上下文)=>Categories());
// },
onUnknownRoute:(设置){
//作为最后手段捕捉路线很有用/找不到页面等./404类似web的后备页面
返回MaterialPackageRoute(生成器:(上下文)=>Home());
},
);
}
}
类MyHomePage扩展StatefulWidget{
最终整数选择屏幕索引;
MyHomePage(此.selectedScreenIndex);
@凌驾
_MyHomePageState createState()=>\u MyHomePageState(selectedScreenIndex);
}
类_MyHomePageState扩展状态{
最终列表屏幕=[
{'title':'Home','screen':Home()},
{...},
];
_MyHomePageState(此.selectedScreenIndex);
int selectedScreenIndex=0;
无效选择屏幕(整数索引){
设置状态(){
selectedScreenIndex=索引;
});
}
@凌驾
小部件构建(构建上下文){
返回脚手架(
//resizeToAvoidBottomInset:true,
appBar:appBar(
标题:文本(屏幕[selectedScreenIndex]['title']),
),
抽屉:main抽屉(),
正文:屏幕[selectedScreenIndex][“屏幕”],
底部导航栏:底部导航栏(
onTap:选择屏幕,
背景色:主题。背景色,
unselectedItemColor:Colors.white,
selectedItemColor:Theme.of(context).accentColor,
currentIndex:selectedScreenIndex,
类型:BottomNavigationBarType.fixed,
项目:[
底部导航气压计(
图标:图标(Icons.home),
标题:文本(“主页”),
背景色:主题(上下文)。原色),
...,
],
));
}
}
最简单的方法(不是最好的)是在应用程序栏参数中使用内联条件,如果选定的索引为=0,则返回应用程序栏和选项卡栏,否则返回正常的应用程序栏

示例

 appBar:selectedSreenIndex==0? AppBar(title:Text(screens[selectedScreenIndex['title'], 
     bottom: TabBar())
     : AppBar(
          title: Text(screens[selectedScreenIndex]['title']),
        ),

我刚刚弄明白了。下面的代码是一个简单的示例

class LearningScreen extends StatefulWidget {

  @override
  State<StatefulWidget> createState() {
    return _LearningScreenState();
  }
}

class _LearningScreenState extends State<LearningScreen>
    with SingleTickerProviderStateMixin {
  TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(vsync: this, length: 3);
  }

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

  @override
  Widget build(BuildContext context) {
    final _tabBar = TabBar(
      controller: _tabController,
      tabs: [
        Tab(
          text: "TAB1",
        ),
        Tab(
          text: "TAB2",
        ),
        Tab(
          text: "TAB3",
        ),
      ],
    );

    return Container(
      height: double.infinity,
      width: double.infinity,
      alignment: Alignment.topCenter,
      child: Column(
        children: <Widget>[
          _tabBar,
          Expanded( // needed for TabBar View to show correctly
            child: TabBarView(
              controller: _tabController,
              children: [
                Text("TAB_CONTENT_1"),
                Text("TAB_CONTENT_2"),
                Text("TAB_CONTENT_3"),
              ],
            ),
          ),

        ],
      ),
    );
  }
}
class LearningScreen扩展了StatefulWidget{
@凌驾
状态createState(){
返回_LearningScreenState();
}
}
类_LearningScreenState扩展状态
使用SingleTickerProviderStateMixin{
TabController\u TabController;
@凌驾
void initState(){
super.initState();
_tabController=tabController(vsync:this,长度:3);
}
@凌驾
无效处置(){
_tabController.dispose();
super.dispose();
}
@凌驾
小部件构建(构建上下文){
最终_tabBar=tabBar(
控制器:\ tab控制器,
选项卡:[
标签(
正文:“表1”,
),
标签(
正文:“表2”,
),
标签(
正文:“表3”,
),
],
);
返回容器(
高度:双无限,
宽度:double.infinity,
对齐:alignment.topCenter,
子:列(
儿童:[
_塔巴,
展开(//需要选项卡栏视图才能正确显示
子项:选项卡视图(
控制器:\ tab控制器,
儿童:[
文本(“选项卡内容1”),
文本(“选项卡内容2”),
文本(“选项卡内容3”),
],
),
),
],
),
);
}
}

您可以在
Home
小部件中安全地使用scaffold,因为它属于回退路由,
MyHomePage
属于根路由。没有筑巢脚手架-所有路线都有自己的。你能详细说明一下吗?我尝试在家中添加一个脚手架,结果是一个双脚手架,因为主脚手架位于我的主页中,而家作为底部导航栏位于我的主页中。谢谢!这就是我现在正在做的,我希望其他人能有一个更清晰的答案。我还没有任何想法,但这是一个非常好的问题,如果找到其他方法,我会编辑我的答案