Flutter NestedScrollView 、SliverAppBar全解析,悬浮菜单的应用

在我们开发过程中经常会使用到悬浮菜单的使用,当我们滑动到指定位置后,菜单会自动悬浮。

实现效果如下(左为滑动前、右为滑动后):

上述便是通过NestedScrollView 、SliverAppBar实现的效果,通过两个控件我们便可以实现上述的效果。

废话不多说直接上代码,代码的实现原理会以注释的形式实现:

Dart 复制代码
import 'package:aboxmini/view/home/room/room_device_page.dart';
import 'package:flutter/material.dart';
import '../../model/app_model.dart';

class HomeTabBar extends StatefulWidget {

  const HomeTabBar({super.key});

  @override
  State<HomeTabBar> createState() => _HomeTabBarState();
}

class _HomeTabBarState extends State<HomeTabBar> with TickerProviderStateMixin {
  /// 自定义的一个类,此类可获取屏幕宽度等
  final AppModel _appModel = AppModel.share();
  /// 设置 中间展示区域的高度
  final double _topHeight = 180 + AppModel.share().safeTop + kToolbarHeight;
  /// 分栏控制器
  late TabController tabController = TabController(length: 3, vsync: this);
  /// 分栏控制器每一个标题
  final _tabs = <String>["Tab 1", "Tab 2", "Tab 3"];

  @override
  void dispose() {
    tabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: _tabs.length,
      child: NestedScrollView(
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return <Widget>[
            SliverOverlapAbsorber(
              handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
              sliver: SliverAppBar(
                /// 取消系统导航左边按钮
                leading: Container(),
                /// 设置背景色
                backgroundColor: Colors.white,
                /// 设置左边按钮宽度
                leadingWidth: _appModel.width,
                centerTitle: false,
                pinned: true,
                floating: false,
                snap: false,
                primary: true,
                /// 设置分栏区域上面的高度
                expandedHeight: 230.0,
                elevation: 10,
                //是否显示阴影,直接取值innerBoxIsScrolled,展开不显示阴影,合并后会显示
                forceElevated: innerBoxIsScrolled,
                ///自定义导航和中间内容的展示
                flexibleSpace: _displayNavAndEnvInfo(),
                /// TabBar 分栏标题
                bottom: _addTabBar(),
              ),
            ),
          ];
        },
        /// 分栏展示的页面信息
        body: _addTabBarView(),
      ),
    );
  }

  /// 自定义导航和中间内容展示
  Widget _displayNavAndEnvInfo() {
    return Container(
      color: Colors.white,
      width: _appModel.width,
      height: _topHeight,
      child: Column(
        children: <Widget>[
          _addNav(),
          _displayEnvDevice(),
        ],
      ),
    );
  }

  /// 自定义导航 可随意定制
  Widget _addNav() {
    return SizedBox(
      width: _appModel.width,
      height: _appModel.safeTop + kToolbarHeight,
      child: Container(
        margin: EdgeInsets.only(top: _appModel.safeTop),
        height: kToolbarHeight,
        width: _appModel.width,
        alignment: Alignment.centerLeft,
        child: Row(
          children: <Widget>[
            GestureDetector(
              onTap: () {
                widget.z.toggle!();
              },
              child: Row(
                children: [
                  Container(
                    margin: const EdgeInsets.only(left: 12, right: 6),
                    child: const Icon(
                      Icons.menu,
                      size: 20,
                      color: Colors.red,
                    ),
                  ),
                  Text("${_appModel.currentDatum?.hostname ?? ""}"),
                ],
              ),
            ),
            Expanded(child: Container())
          ],
        ),
      ),
    );
  }

  /// 导航和TabBar中间展示的内容,可随意自定义
  Widget _displayEnvDevice() {
    return Container(
      color: Colors.white,
    );
  }

  /// TabBar 展示样式自定义,可以滚动并且居左展示
  PreferredSize _addTabBar() {
    return PreferredSize(
      /// 设置高度
      preferredSize: const Size.fromHeight(35),
      child: Align(
        /// 设置展示方式
        alignment: Alignment.centerLeft,
        /// TabBar选中、未选中样式
        child: TabBar(
          /// 是否允许滚动 
          isScrollable: true,
          unselectedLabelColor: Colors.black54,
          unselectedLabelStyle: const TextStyle(fontSize: 15),
          labelColor: Colors.blue,
          labelStyle:
              const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
          indicatorSize: TabBarIndicatorSize.label,
          tabs: _tabs.map((String name) => Tab(text: name)).toList(),
        ),
      ),
    );
  }

  /// TabBar 分栏下的各个页面
  Widget _addTabBarView() {
    return TabBarView(
      children: _tabs.map((String name) {
        // 分栏下的页面(可随意定义、也可以设置成Text等控件),实现方式还有其他方式
        return RoomDevicePage();
      }).toList(),
    );
  }
}

以上便是菜单悬浮的效果实现,注释写的很详细,直接粘贴复制即可实现。

相关推荐
恋猫de小郭14 分钟前
Android 将强制应用使用主题图标,你怎么看?
android·前端·flutter
玲珑Felone1 小时前
从flutter源码看其渲染机制
android·flutter
ALLIN21 小时前
Flutter 三种方式实现页面切换后保持原页面状态
flutter
Dabei21 小时前
Flutter 国际化
flutter
Dabei21 小时前
Flutter MQTT 通信文档
flutter
Dabei1 天前
Flutter 中实现 TCP 通信
flutter
孤鸿玉1 天前
ios flutter_echarts 不在当前屏幕 白屏修复
flutter
前端 贾公子1 天前
《Vuejs设计与实现》第 16 章(解析器) 上
vue.js·flutter·ios
tangweiguo030519871 天前
Flutter 数据存储的四种核心方式 · 从 SharedPreferences 到 SQLite:Flutter 数据持久化终极整理
flutter
0wioiw01 天前
Flutter基础(②④事件回调与交互处理)
flutter