【问题标题】:how to implement a sliverAppBar with a tabBar如何使用 tabBar 实现 sliverAppBar
【发布时间】:2018-11-17 09:34:01
【问题描述】:

flutter 文档显示一个demo for SliverAppBar + TabBar + TabBarView with ListView 使用NestedScrollView,而且有点复杂,不知道有没有简单明了的实现方式。我试过这个:

CustomScrollView
  slivers:
    SliverAPPBar
      bottom: TabBar
    TabBarView
      children: MyWidget(list or plain widget)

出现错误:

flutter:在构建 Scrollable(axisDirection: right, physical:
时抛出了以下断言 颤动:RenderViewport 期望 RenderSliv​​er 类型的子级,但收到 _RenderExcludableScrollSemantics 类型的子级。
Flutter:RenderObjects 期望特定类型的子对象,因为它们在布局和绘制过程中与子对象协调。例如,RenderSliv​​er 不能是 RenderBox 的子级,因为 RenderSliv​​er 不理解 RenderBox 布局协议。

flutter:引发了另一个异常:'package:flutter/src/widgets/framework.dart':断言失败:第 3497 行 pos 14:'owner._debugCurrentBuildTarget == this':不正确。

这是我的代码:

import 'package:flutter/material.dart';

main(List<String> args) {
  runApp(MyScrollTabListApp());
}

class MyScrollTabListApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(title: "aa", home: MyScrollTabListHomePage());
  }
}

class MyScrollTabListHomePage extends StatefulWidget {
  @override
  MyScrollTabListHomePageState createState() {
    return new MyScrollTabListHomePageState();
  }
}

class MyScrollTabListHomePageState extends State<MyScrollTabListHomePage>
    with SingleTickerProviderStateMixin {
  final int _listItemCount = 300;
  final int _tabCount = 8;
  TabController _tabController;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
            expandedHeight: 240.0,
            title: Text("Title"),
            pinned: true,
            bottom: TabBar(
              controller: _tabController,
              isScrollable: true,
              tabs: List<Tab>.generate(_tabCount, (int i) {
                return Tab(text: "TAB$i");
              }),
            ),
          ),
          TabBarView(
            controller: _tabController,
            children: List<Widget>.generate(_tabCount, (int i) {
              return Text('line $i');
            }),
          ),
        ],
      ),
    );
  }
}

对于官方演示,它使用这样的结构

DefaultTabController
    NestedScrollView
      headerSliverBuilder
        SliverOverlapAbsorber
          handle
          SliverAppBar
        TabBarView
          CustomScrollView
            SliverOverlapInjector
              handle
              SliverPadding

【问题讨论】:

标签: flutter nestedscrollview flutter-sliver


【解决方案1】:

使用NestedScrollView,这是工作代码。

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: DefaultTabController(
      length: 2,
      child: NestedScrollView(
        headerSliverBuilder: (context, value) {
          return [
            SliverAppBar(
              bottom: TabBar(
                tabs: [
                  Tab(icon: Icon(Icons.call), text: "Call"),
                  Tab(icon: Icon(Icons.message), text: "Message"),
                ],
              ),
            ),
          ];
        },
        body: TabBarView(
          children: [
            CallPage(),
            MessagePage(),
          ],
        ),
      ),
    ),
  );
}

【讨论】:

  • 像魅力一样工作......谢谢。
【解决方案2】:

这是一个带有 SilverAppBar 的 TabView 示例

class SilverAppBarWithTabBarScreen extends StatefulWidget {
  @override
  _SilverAppBarWithTabBarState createState() => _SilverAppBarWithTabBarState();
}

class _SilverAppBarWithTabBarState extends State<SilverAppBarWithTabBarScreen>
    with SingleTickerProviderStateMixin {
  TabController controller;

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

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new CustomScrollView(
        slivers: <Widget>[
          new SliverAppBar(
            title: Text("Silver AppBar With ToolBar"),
            pinned: true,
            expandedHeight: 160.0,
            bottom: new TabBar(
              tabs: [
                new Tab(text: 'Tab 1'),
                new Tab(text: 'Tab 2'),
                new Tab(text: 'Tab 3'),
              ],
              controller: controller,
            ),
          ),
          new SliverList(
          new SliverFillRemaining(
        child: TabBarView(
          controller: controller,
          children: <Widget>[
               Text("Tab 1"),
               Text("Tab 2"),
               Text("Tab 3"),
             ],
            ),
          ),
        ],
      ),
    );
  }
}

【讨论】:

  • 是的,正如@Negora 所说,没有TabBarView,并且当标题底部的标签发生变化时,页面正文不会改变
  • @DhirajSharma 作为代码new SliverList( new SliverFillRemaining(...),似乎SliverList 没有这样的构造函数,我们可以直接将SliverFillRemaining 作为参数传递
  • @DhirajSharma 我的问题是每个TabView 都包含一个长条列表(这意味着一个可滚动的小部件),而不是一个简单的小部件。
【解决方案3】:

是的。您可以使用 NestedScrollView 来实现选项卡。这是一些附加代码。

class AppView extends StatelessWidget {
final double _minValue = 8.0;

@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;

return Scaffold(
  appBar: MyAppBar(),
  drawer: DrawerDialog(),
  body: DefaultTabController(
    length: 3,
    child: SafeArea(
      child: NestedScrollView(
        body: TabBarView(
          children: [Text("Page 1"), Text("Page 2"), Text("Page 3")],
        ),
        headerSliverBuilder:
            (BuildContext context, bool innerBoxIsScrolled) => [
          SliverPadding(
            padding: EdgeInsets.all(_minValue * 2.5),
            sliver: SliverToBoxAdapter(
              child: Text(
                "Hiding Header",
                style: textTheme.headline6,
                textAlign: TextAlign.center,
              ),
            ),
          ),
          SliverAppBar(
            backgroundColor: Colors.grey[100],
            pinned: true,
            elevation: 12.0,
            leading: Container(),
            titleSpacing: 0.0,
            toolbarHeight: 10,
            bottom: TabBar(tabs: [
              Tab(
                child: Text(
                  "All",
                  style: textTheme.subtitle2,
                ),
              ),
              Tab(
                child: Text(
                  "Categories",
                  style: textTheme.subtitle2,
                ),
              ),
              Tab(
                child: Text(
                  "Upcoming",
                  style: textTheme.subtitle2,
                ),
              ),
            ]),
          ),
        ],
      ),
    ),
  ),
);

} }

【讨论】:

    【解决方案4】:

    您也可以通过将 _tabController 提供给 TabBar() 和 TabBarView 来实现它,这样它就会绑定。 而对于 TabBarView 的孩子,如果您使用 ListView 则给它物理:NeverScrollablePhysics() 所以它不会移动,请不要您必须为 ListView 的容器提供动态高度所以它会加载所有孩子的。

    class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
      TabController _tabController;
      @override
      void initState() {
        _tabController = TabController(length: 2, vsync: this);
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            body: CustomScrollView(
          slivers: [
            SliverAppBar(
              floating: true,
              expandedHeight: 50,
              title: Column(
                children: [
                  Row(
                    children: [
                      Text('Hello, User'),
                      Spacer(),
                      InkWell(
                        child: Icon(Icons.map_rounded),
                      ),
                    
                    ],
                  ),
                ],
              ),
            ),
            SliverList(
                delegate: SliverChildListDelegate([
              _tabSection(context),
            ])),
          ],
        ));
      }
    
      Widget _tabSection(BuildContext context) {
        final height = MediaQuery.of(context).size.height;
        final width = MediaQuery.of(context).size.width;
        double mainAxisHeight = height > width ? height : width;
        return DefaultTabController(
            length: 2,
            child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
              Container(
                height: 48,
                decoration: BoxDecoration(
                    color: Colors.green,
                    borderRadius: BorderRadius.only(
                        bottomRight: Radius.circular(10),
                        bottomLeft: Radius.circular(10))),
                child: TabBar(
                    indicatorColor: Colors.white,
                    indicator: UnderlineTabIndicator(
                      borderSide: BorderSide(color: Colors.white, width: 5.0),
                      insets: EdgeInsets.symmetric(horizontal: 40),
                    ),
                    labelColor: Colors.white,
                    unselectedLabelColor: Colors.grey[300],
                    tabs: [
                      Tab(
                        iconMargin: EdgeInsets.only(top: 5),
                        text: "Tab Bar 1",
                      ),
                      Tab(
                        iconMargin: EdgeInsets.only(top: 5),
                        text: "Tab bar 2",
                      ),
                    ]),
              ),
              
              Container(
                    height: 200 * 6 // 200 will be Card Size and 6 is number of Cards
                  child: TabBarView(controller: _tabController, children: [
                    tabDetails(),
                    tabDetails(),
                  ]))
            ]));
      }
    
      tabDetails() {
        final height = MediaQuery.of(context).size.height;
        final width = MediaQuery.of(context).size.width;
        double mainAxisHeight = height > width ? height : width;
        return Container(
        
          padding: EdgeInsets.symmetric(horizontal: 15),
          decoration: BoxDecoration(
              gradient: LinearGradient(
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter,
                  colors: [
         
                Colors.red[100],
                Colors.red[200],
              ])),
          child: ListView(
            physics: NeverScrollableScrollPhysics(),  // This will disable LitView'Scroll so only Scroll is possible by TabBarView Scroll.
            children: [
              SizedBox(height: 10),        
              Container(
              height:140,
                width: width,
                child: ListView.builder(
                  scrollDirection: Axis.vertical,
                  itemCount: 6,
                  itemBuilder: (BuildContext context, int indexChild) {
                    return Row(
                      children: [
                        MyListTile(
                          name: "Name",
                       
                        ),
                        SizedBox(width: 5),
                      ],
                    );
                  },
                ),
              ),
            
           
              SizedBox(height: 1000),
            ],
          ),
        );
      }
    }
    

    【讨论】:

      【解决方案5】:

      如果你想使用CustomScrollView,你可以使用SliverToBoxAdapter

      Widget build(BuildContext context) {
        return Scaffold(
          body: DefaultTabController(
            length: 2,
            child: CustomScrollView(
              slivers: [
                SliverAppBar(
                  title: Text('SliverAppBar'),
                  bottom: TabBar(
                    tabs: [
                      Tab(icon: Icon(Icons.call), text: "Call"),
                      Tab(icon: Icon(Icons.message), text: "Message"),
                    ],
                  ),
                ),
                SliverToBoxAdapter(
                  child: SizedBox(
                    height: MediaQuery.of(context).size.height,
                    child: TabBarView(
                      children: [
                        Container(color: Colors.red),
                        Container(color: Colors.blue),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          ),
        );
      }
      

      【讨论】:

        猜你喜欢
        • 2018-10-30
        • 2021-01-14
        • 2018-12-19
        • 1970-01-01
        • 2019-09-08
        • 2021-05-14
        • 2020-04-17
        • 2022-12-18
        • 2019-10-25
        相关资源
        最近更新 更多