簡體   English   中英

如何在 Flutter 中將 Grids 添加到 TabbarView 中?

[英]How to add Grids into TabbarView in Flutter?

所以基本上我有這個小部件:

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: truephysics: NeverScrollableScrollPhysics()

但是,當我使用SliverGrid(...)時,會出現此錯誤

RenderObjects 期望特定類型的子對象,因為它們在布局和繪制過程中與子對象協調。 例如,RenderSliver 不能是 RenderBox 的子級,因為 RenderSliver 不理解 RenderBox 布局協議。

這顯然是有道理的,因為TabBarView不是 sliver 小部件。 我已經看過這篇文章,但它並沒有真正的幫助。

我怎么能實現這個? 有沒有一種方法可以創建自己的小部件構建器來構建自定義布局?

謝謝你!

您需要使用 SliverOverlapAbsorber/SliverOverlapInjector,以下代碼適用於我(在 dart 墊上工作的完整代碼):

在這里,我使用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(),
            ),
          ),
        ),
      ),
    );
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM