Flutter 实现导航栏吸顶功能

3,263 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情

前言

在Flutter开发过程中经常会遇到导航栏吸顶的需求,我们可以使用Sliver系列组件实现吸顶导航栏功能。本文探讨的主题就是使用SliverPersistentHeader组件实现导航栏吸顶功能。

SliverPersistentHeader组件简介

SliverPersistentHeader是一个滚动到视窗边缘时大小会发生变化的组件,主要包含delegatepinnedfloating三个参数:

 const SliverPersistentHeader({
   Key? key,
   required this.delegate,
   this.pinned = false,
   this.floating = false,
 }) : assert(delegate != null),
      assert(pinned != null),
      assert(floating != null),
      super(key: key);
1. delegate

delegate参数需要重新实现SliverPersistentHeaderDelegate方法,并对buildminExtentmaxExtentshouldRebuild四种方法进行复写:

build:放置在SliverPersistentHeader内的小部件;

minExtent:允许组件达到的最小尺寸;

maxExtent:允许组件达到的最大尺寸,也是组件在顶部未缩小时的尺寸。

shouldRebuild:当oldDelegate对象返回的数据改变时,是否重新构建SliverPersistentHeader组件。

2. pinned

pinned参数表示当组件达到其最小高度 (即滑动到顶部) 时是否固定在顶部不动,当pinned值为true时表示滑动到顶部后固定不动,pinned值为false则表示滑动到顶部后直接滑出页面。pinned参数值默认为false;

3. floating

floating参数表示用户反转滚动方向 (即向上滑动) ,组件是否立即恢复最大高度,当floating值为true时表示立即恢复最大高度,floating值为false则表示滑动到顶部时恢复最大高度。floating参数值默认为false。

功能代码

可以在实现SliverPersistentHeaderDelegate方法时对该方法进行封装,通过传入参数实现导航栏吸顶的功能,完整代码如下:

 import 'package:flutter/material.dart';
 ​
 class SliverDemo extends StatefulWidget {
   const SliverDemo({Key? key}) : super(key: key);
 ​
   @override
   State<SliverDemo> createState() => _SliverDemoState();
 }
 ​
 class _SliverDemoState extends State<SliverDemo> with SingleTickerProviderStateMixin {
   final List _tab = ['第一', '第二', '第三', '第四'];
   late TabController _tabController;
 ​
   @override
   void initState() {
     // TODO: implement initState
     super.initState();
     _tabController = TabController(length: _tab.length, vsync: this);
   }
 ​
   @override
   Widget build(BuildContext context) {
     return Scaffold(
       appBar: AppBar(),
       body: CustomScrollView(
         slivers: [
           SliverPersistentHeader(
             delegate: SliverTabBar(
               tabController: _tabController,
               tab: _tab,
               isScrollable: false,
               minHeight: 40,
               maxHeight: 50,
               onTap: (i) {
                 print(i);
               },
               borderRadius: const BorderRadius.only(
                 topLeft: Radius.circular(8),
                 topRight: Radius.circular(8),
               ),
               backgroundColor: const Color(0xFFF6F6F6),
               tabColor: const Color(0xFFF6F6F6),
             ),
             pinned: true,
             floating: true,
           ),
         ],
       ),
     );
   }
 }
 // 实现SliverPersistentHeaderDelegate方法
 class SliverTabBar extends SliverPersistentHeaderDelegate {
   TabController tabController; //必传参数,控制器
   List tab; //必传参数,导航项列表
   double maxHeight; //导航栏最大高度
   double minHeight; //导航栏最小高度
   bool isScrollable; //是否可以滚动,默认false
   Function(int)? onTap; //点击导航项事件
   BorderRadiusGeometry? borderRadius; //导航栏圆角
   Color? backgroundColor; //背景颜色
   Color? tabColor; //导航栏颜色
   SliverTabBar({
     required this.tabController,
     required this.tab,
     this.isScrollable = false,
     this.maxHeight = 44,
     this.minHeight = 44,
     this.onTap,
     this.borderRadius,
     this.backgroundColor,
     this.tabColor,
   });
   @override
   Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
     return Container(
       color: tabColor ?? const Color(0xFFF6F6F6),
       child: Container(
         height: maxHeight,
         decoration: BoxDecoration(
           color: backgroundColor ?? const Color(0xFFF6F6F6),
           borderRadius: borderRadius ?? BorderRadius.zero,
         ),
         child: TabBar(
           isScrollable: isScrollable,
           onTap: onTap,
           labelStyle: const TextStyle(color: Color(0xFFFF6720), fontSize: 14),
           labelColor: const Color(0xFFFF6720),
           indicatorColor: const Color(0xFFFF6720),
           indicatorSize: TabBarIndicatorSize.label,
           unselectedLabelColor: const Color(0xFF76777B),
           unselectedLabelStyle: const TextStyle(color: Color(0xFF76777B), fontSize: 14),
           controller: tabController,
           tabs: tab.map((e) => Tab(child: Text(e))).toList(),
         ),
       ),
     );
   }
 ​
   @override
   // TODO: implement maxExtent
   double get maxExtent => maxHeight;
 ​
   @override
   // TODO: implement minExtent
   double get minExtent => minHeight;
 ​
   @override
   bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
     return false;
   }
 }

效果图如下:

运行效果图

总结

Sliver家族有着许多强大的功能组件,本文讲述了SliverPersistentHeader组件实现导航栏吸顶的功能。