BottomNavigationBar 内的顶部 TabBar

top TabBar inside BottomNavigationBar

我无法让顶部 TabBar 在我的 BottomNavigationBar 中呈现。我只希望顶部 TabBar 显示在首页 BottomNavigationBarItem 内。我知道我可以在主页中设置另一个脚手架,但我显然不希望脚手架内有一个脚手架。使用以下代码,我收到运行时错误:

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

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

最简单的方法 (不是最好的) 是在应用栏参数中使用内联条件,如果所选索引为 = 0 return带标签栏的应用栏,否则 return 普通应用栏。

例子

 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"),
              ],
            ),
          ),

        ],
      ),
    );
  }
}