当前位置: 首页 > article >正文

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

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

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

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

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

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

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


http://www.kler.cn/news/133681.html

相关文章:

  • C#中的string和string builder有什么区别
  • docker 安装mongodb 实现 数据,日志,配置文件外挂
  • 关于我开始热爱生活,也会把该做的做好这件事
  • 【算法每日一练]-分块(保姆级教程 篇1)POJ3648
  • 百胜杯答题系统
  • 公网访问全能知识库工具AFFINE,Notion的免费开源替代
  • 【hive遇到的坑】—使用 is null / is not null 对string类型字段进行null值过滤无效
  • C++ 虚函数和多态性
  • React整理总结(三)
  • 公司内部网络架设悟空CRM客户管理系统 cpolar无需公网IP实现内网,映射端口外网访问
  • 【测开求职】面试题:HR面相关的开放性问题
  • 基于Prometheus快速搭建网络质量监控平台
  • 2023_“数维杯”问题B:棉秸秆热解的催化反应-详细解析含代码
  • 计算机毕业设计选题推荐-点餐微信小程序/安卓APP-项目实战
  • Java Web——JavaScript基础
  • 高防IP是什么?如何隐藏源站IP?如何进行防护?
  • 代码随想录二刷 | 数组 | 总结篇
  • 03 前后端数据交互【小白入门SpringBoot + Vue3】
  • wpf devexpress在未束缚模式中生成Tree
  • IDEA写mybatis程序,java.io.IOException:Could not find resource mybatis-config.xml
  • 单元测试实战(六)其它
  • 【HarmonyOS开发】设备调试避坑指南
  • 三十一、W5100S/W5500+RP2040树莓派Pico<TCP_Server多路socket>
  • 别再吐槽大学教材了,来看看这些网友强推的数学神作!
  • 【我和Python算法的初相遇】——体验递归的可视化篇
  • SQL note2:DIsks and Files
  • linux如何一键自动安装系统(PXE)
  • 03_面向对象高级_多态
  • C++11的互斥包装器
  • 怎样助力IT运维团队更专业、更协作、更高效