【问题标题】:Instagram Profile Header Layout In FlutterFlutter 中的 Instagram 个人资料页眉布局
【发布时间】:2020-05-11 06:27:55
【问题描述】:

我一直在研究 SliverAppBar、CustomScrollView、NestedScrollView、SliverPersistentHeader 等。我找不到一种方法来构建像 Instagram 用户个人资料屏幕的标题这样的东西,其中只有标签栏被固定。屏幕的主体是一个 TabBarView,每个窗格都有一个可滚动的列表。

使用SliverAppBar,可以很方便的在底部参数中添加TabBar。但我想在该 TabBar 上方有一个未知/可变高度的额外小部件。当页面滚动时,额外的小部件应该滚动到外面,然后 TabBar 是固定在屏幕顶部的。

我只能管理标签栏之前的固定内容和固定标签栏。我无法让标题向上滚动并将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')),
                    ],
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

【问题讨论】:

    标签: flutter dart flutter-layout customscrollview flutter-sliverappbar


    【解决方案1】:

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

    由于我们需要动态构建 AppBarTabBar 之间的小部件并滚动直到 TabBar 到达 AppBar,因此使用 ScaffoldappBar 属性来构建您的 AppBar 和使用headerSliverBuilder 构建其他未知高度的小部件。使用NestedScrollViewbody 属性来构建您的标签视图。

    这样headerSliverBuilder 的元素会滚动到body 到达AppBar 的底部。

    单凭文字可能会让人有些困惑,这里举个例子。

    代码:

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

    输出:

    希望这会有所帮助!

    【讨论】:

    • 这是一个不错的解决方案。为什么“A”和“B”选项卡文本在我的应用中显示为白底白字,而在您的应用中显示为白底黑字?
    • 嘿,对不起。我将Theme 的原色设置为Colors.white。它基本上取决于您的 . Theme
    • 嘿! @HemanthRaj 我试过这个方法,但是如果你可以检查一下底部溢出,我会遇到这个问题,因为底部导航栏和标签栏stackoverflow.com/q/65429750/13406343?sem=2
    • 如何实现顶部 NestedScrollView 的滑动刷新??
    【解决方案2】:

    另一种解决方案是您可以在DefaultTabController 中使用固定SliverAppBarFlexibleSpaceBar。示例代码:

      Scaffold(
        body: DefaultTabController(
          length: 2,
          child: NestedScrollView(
            headerSliverBuilder: (context, value) {
              return [
                SliverAppBar(
                  floating: true,
                  pinned: true,
                  bottom: TabBar(
                    tabs: [
                      Tab(text: "Posts"),
                      Tab(text: "Likes"),
                    ],
                  ),
                  expandedHeight: 450,
                  flexibleSpace: FlexibleSpaceBar(
                    collapseMode: CollapseMode.pin,
                    background: Profile(), // This is where you build the profile part
                  ),
                ),
              ];
            },
            body: TabBarView(
              children: [
                Container(
                  child: ListView.builder(
                    itemCount: 100,
                    itemBuilder: (context, index) {
                      return Container(
                        height: 40,
                        alignment: Alignment.center,
                        color: Colors.lightBlue[100 * (index % 9)],
                        child: Text('List Item $index'),
                      );
                    },
                  ),
                ),
                Container(
                  child: ListView.builder(
                    itemCount: 100,
                    itemBuilder: (context, index) {
                      return Container(
                        height: 40,
                        alignment: Alignment.center,
                        color: Colors.lightBlue[100 * (index % 9)],
                        child: Text('List Item $index'),
                      );
                    },
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    

    滚动前:

    滚动后:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多