CupertinoSliverNavigationBar

Flutter Cupertino(iOS风格)设计语言中的一个组件,它提供了一个可滚动区域中的导航栏

CupertinoSliverNavigationBar是Flutter Cupertino(iOS风格)设计语言中的一个组件,它提供了一个可滚动区域中的导航栏。这个导航栏通常作为CustomScrollViewslivers列表中的一个元素,能够随着用户的滚动而 改变其显示行为,例如标题的收缩或扩展。

它的设计灵感来源于iOS的大型标题导航栏,能够在用户向下滚动时显示一个大的可读标题,向上滚动时标题会缩小并固定在顶部。它与SliverAppBar类似,但提供了更接近iOS原生导航栏的视觉和交互体验。

主要用途

  • CustomScrollView中构建具有iOS风格大标题和透明或半透明效果的导航栏。
  • 为应用程序提供一致的iOS视觉体验。
  • 处理滚动时导航栏的动态变化,例如标题大小和按钮位置。

使用场景

  • 列表或网格视图的顶部: 搭配CustomScrollViewSliverListSliverGrid,为内容提供一个动态显示的iOS风格导航栏。
  • 用户个人资料页: 显示用户姓名、头像等信息时,导航栏可以随着滚动展示动态效果。
  • 设置页面: 当设置项较多时,使用大标题能更好地引导用户。

示例

基本用法与大标题

import 'package:flutter/cupertino.dart';

class BasicCupertinoSliverNavBarPage extends StatelessWidget {
  const BasicCupertinoSliverNavBarPage({super.key});

  
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      child: CustomScrollView(
        slivers: <Widget>[
          const CupertinoSliverNavigationBar(
            largeTitle: Text('我的应用'), // 大标题
            // 中间标题,在滚动时显示,默认为 largeTitle 的 Text
            // middle: Text('顶部导航'), 
            // 次要标题,在大标题下方,只在大标题显示时可见
            // trailing: CupertinoButton(
            //   padding: EdgeInsets.zero,
            //   onPressed: () {},
            //   child: Icon(CupertinoIcons.add),
            // ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                return CupertinoListTile(
                  title: Text('列表项 $index'),
                );
              },
              childCount: 50, // 列表项数量,确保可滚动
            ),
          ),
        ],
      ),
    );
  }
}

// 假设有一个 CupertinoListTile 示例,如果不存在你可以替换成 ListTile:
class CupertinoListTile extends StatelessWidget {
  final Widget title;
  const CupertinoListTile({super.key, required this.title});

  
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
      decoration: BoxDecoration(
        color: CupertinoColors.white,
        border: Border(
          bottom: BorderSide(
            color: CupertinoColors.extraLightBackgroundGray,
            width: 0.5,
          ),
        ),
      ),
      child: DefaultTextStyle(
        style: CupertinoTheme.of(context).textTheme.textStyle,
        child: title,
      ),
    );
  }
}

带有操作按钮的导航栏

import 'package:flutter/cupertino.dart';

class ActionsCupertinoSliverNavBarPage extends StatelessWidget {
  const ActionsCupertinoSliverNavBarPage({super.key});

  
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      child: CustomScrollView(
        slivers: <Widget>[
          CupertinoSliverNavigationBar(
            largeTitle: const Text('消息'),
            leading: CupertinoButton(
              padding: EdgeInsets.zero,
              onPressed: () {
                Navigator.of(context).pop(); // 返回上一页
              },
              child: const Icon(CupertinoIcons.back),
            ),
            trailing: CupertinoButton(
              padding: EdgeInsets.zero,
              onPressed: () {
                // 执行添加操作
                print('添加消息');
              },
              child: const Icon(CupertinoIcons.add),
            ),
            backgroundColor: CupertinoColors.systemBackground.withOpacity(0.9), // 轻微透明
            border: Border(
              bottom: BorderSide(
                color: CupertinoColors.systemGrey.withOpacity(0.5),
                width: 0.0, // 无边框
              ),
            ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                return CupertinoListTile(
                  title: Text('你有新消息 $index'),
                );
              },
              childCount: 50,
            ),
          ),
        ],
      ),
    );
  }
}
// CupertinoListTile 参见示例 1

完全自定义中间标题和背景色

import 'package:flutter/cupertino.dart';

class CustomCupertinoSliverNavBarPage extends StatelessWidget {
  const CustomCupertinoSliverNavBarPage({super.key});

  
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      child: CustomScrollView(
        slivers: <Widget>[
          CupertinoSliverNavigationBar(
            largeTitle: const Text('个人设置'),
            middle: const Text('详情', style: TextStyle(color: CupertinoColors.activeBlue)), // 自定义中间小标题样式
            backgroundColor: CupertinoColors.systemGrey5.withOpacity(0.95), // 自定义背景色
            // effect: const NoSplashCupertinoSliverNavigationBarEffect(), // 移除背景模糊效果
            border: Border( // 移除底部分割线
              bottom: BorderSide(
                color: CupertinoColors.systemGrey.withOpacity(0.0),
                width: 0.0,
              ),
            ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                return CupertinoListTile(
                  title: Text('设置项 $index'),
                );
              },
              childCount: 50,
            ),
          ),
        ],
      ),
    );
  }
}
// CupertinoListTile 参见示例 1

注意点

  • 必须在CustomScrollView中使用: CupertinoSliverNavigationBar是一个Sliver,所以它必须作为CustomScrollViewslivers列表中的一个元素,不能直接用在ScaffoldCupertinoPageScaffoldappBar属性中。
  • largeTitle是核心: 要想实现iOS风格的大标题效果,largeTitle属性是必须提供的。如果没有largeTitle,导航栏的行为会更接近常规导航栏。
  • 背景色与透明度: backgroundColor属性可以控制导航栏的背景色。当颜色带有透明度时,CupertinoSliverNavigationBar会自动应用iOS风格的模糊效果(UIVisualEffectView),使得内容在导航栏下方滚动时可以看到模糊的背景。
  • border属性: 默认情况下,导航栏底部会有一条细线。你可以通过border属性来修改或移除它,例如设置BorderSide(width: 0.0, color: Colors.transparent)
  • transitionBetweenRoutes属性: 这个属性控制当路由切换时,导航栏是否显示过渡动画。如果禁用,切换会更生硬。
  • alwaysShowMiddlestretch: 在某些布局中,中标题可能会在滚动时隐藏。通过alwaysShowMiddle可以强制显示。stretch属性可以让导航栏在下拉时有一个弹性拉伸的效果。
  • CupertinoPageScaffold配合: CupertinoPageScaffold通常是CustomScrollView的父级,它的child属性就是CustomScrollView
  • 避免过度嵌套: 在leadingmiddletrailing中放置的widget应该尽量轻量,避免复杂的布局,以保证滚动时的性能。

构造函数

const CupertinoSliverNavigationBar({
  Key? key,
  this.largeTitle,
  this.leading,
  this.automaticallyImplyLeading = true,
  this.automaticallyImplyMiddle = true,
  this.middle,
  this.trailing,
  this.border = _kDefaultNavBarBorder,
  this.backgroundColor,
  this.padding,
  this.previousPageTitle,
  this.transitionBetweenRoutes = true,
  this.heroTag = _defaultHeroTag,
  this.stretch = false,
  this.overlapActions = false,
  this.alwaysShowMiddle = false,
})

属性

属性名称类型说明
largeTitleWidget?(关键) 大标题,当导航栏展开时显示。它在滚动时会缩小并可能被 middle 替换。通常是一个 Text widget。
leadingWidget?导航栏左侧的 widget,例如返回按钮 (CupertinoNavigationBarBackButton) 或其他操作按钮。
automaticallyImplyLeadingbool是否自动推断并显示一个返回按钮。默认为 true。如果 leading 被设置,则此属性无效。
automaticallyImplyMiddlebool如果 middle 未设置,是否自动使用 largeTitle 创建一个中间标题。默认为 true
middleWidget?导航栏中间的标题 widget。当导航栏收缩时显示,或者 alwaysShowMiddle 为 true 时一直显示。如果未设置,通常会使用 largeTitle 的内容。
trailingWidget?导航栏右侧的操作按钮或 widget。
borderBorder?(关键) 导航栏底部的边框。默认为一条细灰色线。可以设置为 null 或自定义 Border 以移除或修改。
backgroundColorColor?(关键) 导航栏的背景颜色。如果设置了透明度,会自动应用 iOS 风格的模糊效果。
paddingEdgeInsetsDirectional?导航栏内容的内边距。
previousPageTitleString?当导航栏用作返回按钮时,返回按钮旁边显示的上一页标题文本。
transitionBetweenRoutesbool(关键) 控制在路由切换时,是否显示导航栏的过渡动画。默认为 true
heroTagObject用于 Hero 动画的标签,确保路由切换时导航栏能平滑过渡。如果页面有多个 CupertinoSliverNavigationBar,则需要为每个设置唯一的 heroTag。默认为一个内部生成的唯一对象。
stretchboolCustomScrollView 超出滚动范围时,导航栏是否可以弹性拉伸。默认为 false
overlapActionsbool是否允许 leadingtrailing widget 与 middle widget 重叠。默认为 false,即会尝试将它们分开。
alwaysShowMiddlebool是否强制 middle widget 始终显示,而不是只在导航栏收缩时显示。默认为 false

关键属性详解

  • largeTitle: 这是实现iOS大标题效果的核心。它决定了导航栏展开时的主要视觉元素。
  • border: 默认的iOS导航栏底部有一个细的分割线,你可以通过此属性自定义或完全移除它,以获得更简洁的界面。
  • backgroundColor: 控制导航栏的颜色。特值得注意的是,当颜色带有透明度时,Flutter会自动模拟iOS的毛玻璃(UIVisualEffectView)效果,使得背景内容模糊可见。
  • transitionBetweenRoutes: 这个属性对于创建流畅的iOS风格页面切换体验至关重要。开启后,当页面进出时,导航栏会有类似iOS原生的动画效果。
  • heroTag: 在使用CupertinoPageRoute进行页面切换时,确保heroTag的唯一性对于CupertinoSliverNavigationBar之间平滑的"push/pop"动画至关重要。如果你的应用中有多个同时存在的CupertinoSliverNavigationBar,或者嵌套了导航器,应该为它们提供明确且唯一的heroTag