我一直在研究SliverAppBar、CustomScrollView、NestedScrollView、SliverPersistentHeader等.我找不到一种方法来构建Instagram用户配置文件屏幕的标题,其中只固定选项卡栏.屏幕的主体是TabBarView,每个窗格都有一个可滚动列表.

使用SliverAppBar,可以很容易地在底部参数中添加TabBar.但我想在这个选项卡栏上有一个额外的未知/可变高度的小部件.当页面被滚动时,额外的小部件应该滚动到一边,然后标签栏被固定在屏幕顶部.

enter image description here

我只能管理选项卡栏和固定选项卡栏之前的固定内容.我不能让标题向上滚动,并将TabBar放在顶部恰好在AppBar的正下方.

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(home: MyApp()));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 2,
      child: Scaffold(
        appBar: AppBar(
          title: Text("pabloaleko"),
        ),
        body: CustomScrollView(
          physics: const BouncingScrollPhysics(),
          slivers: <Widget>[
            SliverToBoxAdapter(
              child: SafeArea(
                child: Text("an unknown\namount of content\n goes here in the header"),
              ),
            ),
            SliverToBoxAdapter(
              child: TabBar(
                tabs: [
                  Tab(child: Text('Days', style: TextStyle(color: Colors.black))),
                  Tab(child: Text('Months', style: TextStyle(color: Colors.black))),
                ],
              ),
            ),
            SliverFillRemaining(
              child: TabBarView(
                children: [
                  ListView(
                    children: <Widget>[
                      ListTile(title: Text('Sunday 1')),
                      ListTile(title: Text('Monday 2')),
                      ListTile(title: Text('Tuesday 3')),
                      ListTile(title: Text('Wednesday 4')),
                      ListTile(title: Text('Thursday 5')),
                      ListTile(title: Text('Friday 6')),
                      ListTile(title: Text('Saturday 7')),
                      ListTile(title: Text('Sunday 8')),
                      ListTile(title: Text('Monday 9')),
                      ListTile(title: Text('Tuesday 10')),
                      ListTile(title: Text('Wednesday 11')),
                      ListTile(title: Text('Thursday 12')),
                      ListTile(title: Text('Friday 13')),
                      ListTile(title: Text('Saturday 14')),
                    ],
                  ),
                  ListView(
                    children: <Widget>[
                      ListTile(title: Text('January')),
                      ListTile(title: Text('February')),
                      ListTile(title: Text('March')),
                      ListTile(title: Text('April')),
                      ListTile(title: Text('May')),
                      ListTile(title: Text('June')),
                      ListTile(title: Text('July')),
                      ListTile(title: Text('August')),
                      ListTile(title: Text('September')),
                      ListTile(title: Text('October')),
                      ListTile(title: Text('November')),
                      ListTile(title: Text('December')),
                    ],
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

enter image description here

推荐答案

您可以使用NestedScrollViewScaffold来实现此行为.

因为我们需要动态构建AppBarTabBar之间的小部件,并滚动到TabBar达到AppBar,所以使用ScaffoldappBar属性来构建AppBar,使用headerSliverBuilder来构建其他未知高度的小部件.使用NestedScrollViewbody属性来构建选项卡视图.

这样,headerSliverBuilder的元素就会滚动开,直到body到达AppBar的底部.

仅仅用语言理解可能会有点困惑,下面是一个例子.

Code:

// InstaProfilePage
class InstaProfilePage extends StatefulWidget {
  @override
  _InstaProfilePageState createState() => _InstaProfilePageState();
}

class _InstaProfilePageState extends State<InstaProfilePage> {
  double get randHeight => Random().nextInt(100).toDouble();

  List<Widget> _randomChildren;

  // Children with random heights - You can build your widgets of unknown heights here
  // I'm just passing the context in case if any widgets built here needs  access to context based data like Theme or MediaQuery
  List<Widget> _randomHeightWidgets(BuildContext context) {
    _randomChildren ??= List.generate(3, (index) {
      final height = randHeight.clamp(
        50.0,
        MediaQuery.of(context).size.width, // simply using MediaQuery to demonstrate usage of context
      );
      return Container(
        color: Colors.primaries[index],
        height: height,
        child: Text('Random Height Child ${index + 1}'),
      );
    });

    return _randomChildren;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // Persistent AppBar that never scrolls
      appBar: AppBar(
        title: Text('AppBar'),
        elevation: 0.0,
      ),
      body: DefaultTabController(
        length: 2,
        child: NestedScrollView(
          // allows you to build a list of elements that would be scrolled away till the body reached the top
          headerSliverBuilder: (context, _) {
            return [
              SliverList(
                delegate: SliverChildListDelegate(
                  _randomHeightWidgets(context),
                ),
              ),
            ];
          },
          // You tab view goes here
          body: Column(
            children: <Widget>[
              TabBar(
                tabs: [
                  Tab(text: 'A'),
                  Tab(text: 'B'),
                ],
              ),
              Expanded(
                child: TabBarView(
                  children: [
                    GridView.count(
                      padding: EdgeInsets.zero,
                      crossAxisCount: 3,
                      children: Colors.primaries.map((color) {
                        return Container(color: color, height: 150.0);
                      }).toList(),
                    ),
                    ListView(
                      padding: EdgeInsets.zero,
                      children: Colors.primaries.map((color) {
                        return Container(color: color, height: 150.0);
                      }).toList(),
                    )
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Output:

Output Image

希望这能有所帮助!

Flutter相关问答推荐

Flutter:如何从Bottom Navigator中删除此内容

如何使一个悬浮的appbar在flutter?

Flutter API请求BadState响应

有没有什么方法可以加快在Flutter 中加载Quill HTML编辑器的速度?

Supabase Auth:重定向到 Google.com 而不是我的应用程序

如何在flutter中将所有单词的第一个字母大写

Flutter中如何实现带有变化图标的浮动操作按钮?

#Flutter/Riverpod - 使用 AsyncNotifier 时是否可以仅调用错误?

如何从 Flutter 中的 App2 远程点击 App1 中的按钮

更改 flutter listview 中的布局

使用堆栈的自定义按钮

启动画面中的淡入淡出图像

如何在 Flutter (iOS) 中解决这个问题

如何防止键盘将内容向上推?

是否有可能减少代码的编写,例如:ref.read(countProvider.notifier) - 在构建方法中?

如何为文本主题设置多种 colored颜色 ?

对话框打开时如何使背景模糊?

Listview 如何转到下一行/新行?

如何在手势检测器中获得涟漪效应

Flutter Web App 未启动:脚本的 MIME 类型不受支持