【问题标题】:How to add Grids into TabbarView in Flutter?如何在 Flutter 中将网格添加到 TabbarView?
【发布时间】:2022-01-22 13:05:24
【问题描述】:

所以基本上我有这个小部件:

class MyWidget extends StatelessWidget {
  const MyWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: [
          // ...
          SliverToBoxAdapter(
            child: TabBar(
              controller: this._controller,
              indicator: UnderlineTabIndicator(
                borderSide: BorderSide(width: 1.0, color: Colors.red),
              ),
              tabs: [
                Tab(icon: Icon(CupertinoIcons.camera)),
                Tab(icon: Icon(CupertinoIcons.photo)),
                Tab(icon: Icon(CupertinoIcons.video_camera)),
              ],
            ),
          ),
          SliverFillRemaining(
            child: TabBarView(
              controller: this._controller,
              children: [
                // Want Scrollable Grid here
                // Want Scrollable Grid here
                Center(
                  child: Text("Hello Reader????"),
                ),
              ],
            ),
          ),
          // ...
        ],
      ),
    );
  }
}

我想在TabBarView 中添加一个2 个可滚动网格作为子项,但是当我使用GridView.builder(...) 时,网格顶部有一个恼人的间隙,滚动并不是全部即使使用shrinkWrap: true physics: NeverScrollableScrollPhysics() 也不会太好。

但是当我使用SliverGrid(...) 时,会出现这个错误

RenderObject 需要特定类型的子对象,因为它们在布局和绘制过程中会与子对象协调。例如,RenderSliv​​er 不能是 RenderBox 的子级,因为 RenderSliv​​er 不理解 RenderBox 布局协议。

这显然是有道理的,因为TabBarView 不是 sliver 小部件。我已经看过this 的帖子,但它并没有什么帮助。

我该如何实现呢?有没有一种方法可以让我创建自己的小部件构建器来构建自定义布局?

谢谢!

【问题讨论】:

    标签: flutter dart


    【解决方案1】:

    您需要使用 SliverOverlapAbsorber/SliverOverlapInjector,以下代码适用于我 (working full code on dart pad):

    这里我使用了SliverFixedExtentList,但你可以将它替换为SliverGrid

      @override
      State<StatefulWidget> createState() => _NewsScreenState();
    }
    
    class _NewsScreenState extends State<NewsScreen> {
      final List<String> listItems = [];
    
      final List<String> _tabs = <String>[
        "Featured",
        "Popular",
        "Latest",
      ];
    
      @override
      Widget build(BuildContext context) {
        return Material(
          child: Scaffold(
            body: DefaultTabController(
              length: _tabs.length, // This is the number of tabs.
              child: NestedScrollView(
                headerSliverBuilder:
                    (BuildContext context, bool innerBoxIsScrolled) {
                  // These are the slivers that show up in the "outer" scroll view.
                  return <Widget>[
                    SliverOverlapAbsorber(
                      // This widget takes the overlapping behavior of the SliverAppBar,
                      // and redirects it to the SliverOverlapInjector below. If it is
                      // missing, then it is possible for the nested "inner" scroll view
                      // below to end up under the SliverAppBar even when the inner
                      // scroll view thinks it has not been scrolled.
                      // This is not necessary if the "headerSliverBuilder" only builds
                      // widgets that do not overlap the next sliver.
                      handle:
                          NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                      sliver: SliverSafeArea(
                        top: false,
                        sliver: SliverAppBar(
                          title: const Text('Books'),
                          floating: true,
                          pinned: true,
                          snap: false,
                          primary: true,
                          forceElevated: innerBoxIsScrolled,
                          bottom: TabBar(
                            // These are the widgets to put in each tab in the tab bar.
                            tabs: _tabs
                                .map((String name) => Tab(text: name))
                                .toList(),
                          ),
                        ),
                      ),
                    ),
                  ];
                },
                body: TabBarView(
                  // These are the contents of the tab views, below the tabs.
                  children: _tabs.map((String name) {
                    return SafeArea(
                      top: false,
                      bottom: false,
                      child: Builder(
                        // This Builder is needed to provide a BuildContext that is "inside"
                        // the NestedScrollView, so that sliverOverlapAbsorberHandleFor() can
                        // find the NestedScrollView.
                        builder: (BuildContext context) {
                          return CustomScrollView(
                            // The "controller" and "primary" members should be left
                            // unset, so that the NestedScrollView can control this
                            // inner scroll view.
                            // If the "controller" property is set, then this scroll
                            // view will not be associated with the NestedScrollView.
                            // The PageStorageKey should be unique to this ScrollView;
                            // it allows the list to remember its scroll position when
                            // the tab view is not on the screen.
                            key: PageStorageKey<String>(name),
                            slivers: <Widget>[
                              SliverOverlapInjector(
                                // This is the flip side of the SliverOverlapAbsorber above.
                                handle:
                                    NestedScrollView.sliverOverlapAbsorberHandleFor(
                                        context),
                              ),
                              SliverPadding(
                                padding: const EdgeInsets.all(8.0),
                                // In this example, the inner scroll view has
                                // fixed-height list items, hence the use of
                                // SliverFixedExtentList. However, one could use any
                                // sliver widget here, e.g. SliverList or SliverGrid.
                                sliver: SliverFixedExtentList(
                                  // The items in this example are fixed to 48 pixels
                                  // high. This matches the Material Design spec for
                                  // ListTile widgets.
                                  itemExtent: 60.0,
                                  delegate: SliverChildBuilderDelegate(
                                    (BuildContext context, int index) {
                                      // This builder is called for each child.
                                      // In this example, we just number each list item.
                                      return Container(
                                          color: Color((math.Random().nextDouble() *
                                                          0xFFFFFF)
                                                      .toInt() <<
                                                  0)
                                              .withOpacity(1.0));
                                    },
                                    // The childCount of the SliverChildBuilderDelegate
                                    // specifies how many children this inner list
                                    // has. In this example, each tab has a list of
                                    // exactly 30 items, but this is arbitrary.
                                    childCount: 30,
                                  ),
                                ),
                              ),
                            ],
                          );
                        },
                      ),
                    );
                  }).toList(),
                ),
              ),
            ),
          ),
        );
      }
    }
    

    【讨论】:

    • NestedScrollView 小部件删除了我当前在小部件上拥有的功能。我需要使用CustomScrollView 来实现它
    • 没关系。我做了一些挖掘并找到了解决方案。
    • 好的,谢谢分享
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-05
    • 2020-08-10
    • 1970-01-01
    相关资源
    最近更新 更多