flutter tabbar点击拦截处理(基于persistent_bottom_nav_bar_v2 )

上次说到了模版项目 flutter 基于get的模版项目(可通过.bat自定义)tabbar的修改,今天扩展一下 persistent_bottom_nav_bar_v2 的一些特殊处理。

先说一下,这里没有采用router的处理方式,感觉有点麻烦。

  • tabbar item 的特殊交互事件,例如发布按钮、功能集合底部弹窗,这些情况下是不需要进行tabbar页面切换的,或者说按钮不需要去实现对应的tabbar页面

插件创建页面时提供了一个特殊的构建方法 PersistentTabConfig.noScreen,该方法不需要传入tabbar页面,但必须额外实现一个点击事件,该事件就是我们需要做特殊操作的出发点。

dart 复制代码
  PersistentTabConfig.noScreen({
    required this.item,
    required void Function(BuildContext) this.onPressed,
    this.navigatorConfig = const NavigatorConfig(),
    this.onSelectedTabPressWhenNoScreensPushed,
  }) : screen = Container();
  • 非强制登录模式下,用户点击tabbar切换到 个人中心 时需要跳转至登录页;

对于目前绝大多数项目来说,App整体架构无非分为两种模式:

  1. 强制登录:这种情况最简单,用户必须有token令牌才能进行内部操作;

  2. 静默登录/游客模式:允许用户浏览大多数非用户操作相关信息,例如资讯类、商城等,对这类App的个人中心常规操作也只有两种。第一种,默认展示个人中心缺省信息,即可以正常进入操作页;第二种,在点击交互时对事件进行拦截,进行特殊化操作,通常是跳转登录页

这里开贴主要也是说说第二种处理方式和思路。

通过 persistent_bottom_nav_bar_v2 插件Api的查找发现作者并没有开放 NavBarConfig.onItemSelected 对应的拦截事件,而后通过源码查找,发现在构建PersistentTabView的过程中有如下实现:

dart 复制代码
  .../persistent_bottom_nav_bar_v2-5.1.0->lib->components->persistent_tab_view.dart
  
  Widget navigationBarWidget() => PersistentTabViewScaffold(
        ...略...
        tabBar: widget.navBarBuilder(
          NavBarConfig(
            selectedIndex: _controller.index,
            items: widget.tabs.map((e) => e.item).toList(),
            navBarHeight: widget.navBarHeight,
            // 这里实现点击方法的触发
            onItemSelected: (index) {
              if (widget.tabs[index].onPressed != null) {
                widget.tabs[index].onPressed!(context);
              } else {
                if (widget.navigationShell != null) {
                  widget.navigationShell!.goBranch(
                    index,
                    initialLocation: widget.popAllScreensOnTapOfSelectedTab &&
                        index == widget.navigationShell!.currentIndex,
                  );
                } else if (widget.popAllScreensOnTapOfSelectedTab &&
                    _controller.index == index) {
                  popAllScreens();
                } else {
                  // 这里对tabbar页面进行切换
                  _controller.jumpToTab(index);
                }
              }
            },
          ),
        ),
        tabBuilder: (context, index) => _buildScreen(index),
        animatedTabBuilder: widget.animatedTabBuilder,
        navigationShell: widget.navigationShell,
      );

所以往上查找可得必传参数widget.navBarBuilder是我们实现点击事件拦截的关键代码。

插件自定义style 中发现我们可以对navBarBuilder传入自定义 DecoratedNavBar widget,在自定义的 tabbar item 创建时,对 navBarConfig.onItemSelected(前面说到这是拦截点击事件的关键)的调用,所以自定义 redirect_bottom_bar.dart 实现如下:

dart 复制代码
import 'package:flutter/material.dart';
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';

/// 重定向底部导航栏
class RedirectBottomNavBar extends StatelessWidget {
  const RedirectBottomNavBar({
    required this.navBarConfig,
    required this.onRedirected,
    super.key,
    this.navBarDecoration = const NavBarDecoration(),
  });

  final NavBarConfig navBarConfig;
  final NavBarDecoration navBarDecoration;

  /// 重定向底部导航栏操作
  /// 
  /// onRedirected返回值为true打开重定向, 否则取消重定向(正常触发业务逻辑)
  final Future<bool?> Function(int index) onRedirected;

  Widget _buildItem(ItemConfig item, bool isSelected) => Column( ...略... );

  @override
  Widget build(BuildContext context) => DecoratedNavBar(
        decoration: navBarDecoration,
        filter: navBarConfig.selectedItem.filter,
        opacity: navBarConfig.selectedItem.opacity,
        height: navBarConfig.navBarHeight,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: navBarConfig.items.map((item) {
            final int index = navBarConfig.items.indexOf(item);
            return Expanded(
              child: InkWell(
                onTap: () async {
                  // 这里对重定向的处理
                  if (await onRedirected.call(index) == true) {
                    return;
                  }
                  navBarConfig.onItemSelected(index);
                },
                child: _buildItem(
                  item,
                  navBarConfig.selectedIndex == index,
                ),
              ),
            );
          }).toList(),
        ),
      );
}

效果图前面贴过了,使用方式和插件官方一致:

dart 复制代码
  @override
  Widget build(BuildContext context) {
    return PersistentTabView(
      controller: controller.persistent,
      screenTransitionAnimation: const ScreenTransitionAnimation.none(),
      tabs: TabbarType.values
          .map(
            (e) => PersistentTabConfig(
              screen: e.body,
              item: ItemConfig(
                icon: _assets('${e.icon}_slt'),
                inactiveIcon: _assets('${e.icon}_nor'),
                title: e.title,
                activeForegroundColor: Colors.blue,
                inactiveForegroundColor: Colors.grey,
              ),
            ),
          )
          .toList(),
      navBarBuilder: (navBarConfig) {
        return RedirectBottomNavBar(
          navBarConfig: navBarConfig,
          // tabbar点击拦截处理
          onRedirected: (index) async {
            if (index == TabbarType.values.length - 1 &&
                !UserService.to.isLogined) {
              Get.dialog(AlertDialog(
                title: const Text('鉴权拦截处理'),
                content: const Text('点击按钮模拟登录操作,进入`个人中心`'),
                actions: [
                  TextButton(
                    onPressed: () {
                      UserService.to.login();
                      // 关闭dialog
                      Get.back();
                      // 切换到个人中心
                      controller.persistent.jumpToTab(index);
                    },
                    child: const Text('登录'),
                  )
                ],
              ));
              return true;
            }
            return null;
          },
          navBarDecoration: const NavBarDecoration(
            color: Colors.white,
            // borderRadius: BorderRadius.circular(2),
          ),
        );
      },
    );
  }

总结一下: 采用插件官方推荐的自定义style方式处理拦截事件,

好处:无侵入,保证插件原汁原味,不影响插件后续的升级;

坏处:自定义style仅仅是作为UI层面上自定义处理的一种方式,这里用来做事件拦截多少有点小题大作了,而且如果我们选取插件原有样式(style1~style16)的任意一种,我们要添加拦截的话都需要copy出来进行修改的,总的来说还是有点不尽人意,但是需求大于天,哈哈哈;

代码同步更新到Git:flutter模板项目命令

相关推荐
江上清风山间明月13 小时前
Flutter开发的应用页面非常多时如何高效管理路由
android·flutter·路由·页面管理·routes·ongenerateroute
Zsnoin能1 天前
flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题
flutter
早起的年轻人1 天前
Flutter CupertinoNavigationBar iOS 风格导航栏的组件
flutter·ios
HappyAcmen1 天前
关于Flutter前端面试题及其答案解析
前端·flutter
coooliang1 天前
Flutter 中的单例模式
javascript·flutter·单例模式
coooliang1 天前
Flutter项目中设置安卓启动页
android·flutter
JIngles1231 天前
flutter将utf-8编码的字节序列转换为中英文字符串
java·javascript·flutter
B.-1 天前
在 Flutter 中实现文件读写
开发语言·学习·flutter·android studio·xcode
freflying11192 天前
使用jenkins构建Android+Flutter项目依赖自动升级带来兼容性问题及Jenkins构建速度慢问题解决
android·flutter·jenkins
机器瓦力2 天前
Flutter应用开发:对象存储管理图片
flutter