flutter 使用Bloc+PageView+BottomNavigationBar实现传统首页布局

这篇具有很好参考价值的文章主要介绍了flutter 使用Bloc+PageView+BottomNavigationBar实现传统首页布局。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

本篇文章主要记录首页框架搭配bloc的使用示例,本篇文章将会使用上一篇文章中的代码,有兴趣的朋友可以去参考一下实现,除了使用pageview还有另外一种实现,但是最后发现那种方式有两个问题,一个是进入首页后会加载所有PageWidget,第二个是每次切换PageWidget时都会走一遍build方法,这显然不符合实际使用场景,所以这里参考部分文章对PageView的使用,也引入了PageView的页面缓存与懒加载完美的符合实际使用,下面看示例。


一、目录结构

├── bloc
│   ├── cubit.dart
│   └── state.dart
└── view.dart

二、具体步骤

1. 使用state定义BottomNavigationBar切换状态

代码如下:

part of 'cubit.dart';

enum HomeTab {
  home('首页'),
  classification('分类'),
  dynamics('动态'),
  me('我的');

  final String title;
  const HomeTab(this.title);
}
final class HomeState extends Equatable {
  final HomeTab tab;

  const HomeState({this.tab = HomeTab.home});

  
  List<Object?> get props => [tab];

  HomeState copyWith({
    HomeTab? tab,
  }) {
    return HomeState(
      tab: tab ?? this.tab,
    );
  }
}

2.使用Cubit定义简单的事件处理

代码如下:

import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

part 'state.dart';

class HomeCubit extends Cubit<HomeState> {
  HomeCubit() : super(const HomeState());

  void setTab(HomeTab tab) => emit(state.copyWith(tab: tab));
}

3.封装PageView带缓存子类,listview也可以使用(需要慎重)

代码如下:

part of lib_rock_utils;

/// {@template keep_alive_wrapper}
/// 帮助 PageView或ListView等滑动控件实现缓存
/// 使用方式,只需要将需要缓存的子项用该类包裹即可
///
/// {@endtemplate}
final class KeepAliveWrapper extends StatefulWidget {
  /// {@macro keep_alive_wrapper}
  const KeepAliveWrapper({
    super.key,
    this.keepAlive = true,
    required this.child,
  });

  /// 是否缓存, 默认缓存
  final bool keepAlive;
  /// 布局
  final Widget child;

  
  State<KeepAliveWrapper> createState() => _KeepAliveWrapperState();
}

class _KeepAliveWrapperState extends State<KeepAliveWrapper>
    with AutomaticKeepAliveClientMixin {
  
  Widget build(BuildContext context) {
    super.build(context); // 必须调用
    return widget.child;
  }

  
  void didUpdateWidget(covariant KeepAliveWrapper oldWidget) {
    if (oldWidget.keepAlive != widget.keepAlive) {
      // keepAlive 状态需要更新,实现在 AutomaticKeepAliveClientMixin 中
      updateKeepAlive();
    }
    super.didUpdateWidget(oldWidget);
  }

  
  bool get wantKeepAlive => widget.keepAlive; // 是否需要缓存
}

4.实际的布局, 直接将布局放到main中即可看到效果

代码如下:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lib_rock_utils/lib_rock_utils.dart';

import '../test_http2/view.dart';
import 'bloc/cubit.dart';

class HomePage extends StatelessWidget {
  /// 首页
  const HomePage({super.key});

  
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (BuildContext context) => HomeCubit(),
      child: const _HomePageView(),
    );
  }
}

class _HomePageView extends StatefulWidget {
  const _HomePageView();

  
  State<_HomePageView> createState() => _HomePageViewState();
}

class _HomePageViewState extends State<_HomePageView> {
  final PageController _pageController = PageController();
  // 记录两次点击返回键的时间,如果在1秒内点击两次就退出
  DateTime? lastPopTime;
  // 当前需要切换的页面,需要与BottomNavigationBar个数对应
  final List<Widget> pageList = [
    KeepAliveWrapper(child: HomeTwoPage()),
    KeepAliveWrapper(
        child: TestHomeItemPage(txt: HomeTab.classification.title)),
    KeepAliveWrapper(child: TestHomeItemPage(txt: HomeTab.dynamics.title)),
    KeepAliveWrapper(child: TestHomeItemPage(txt: HomeTab.me.title)),
  ];

  
  Widget build(BuildContext context) {
    final selectedTab = context.select((HomeCubit cubit) => cubit.state.tab);

    return Scaffold(
      body: WillPopScope(
        onWillPop: () async {
          // 如果当前tab不在首页, 则按返回触发切换到首页
          if (selectedTab != HomeTab.home) {
            _pageController.jumpToPage(0);
            context.read<HomeCubit>().setTab(HomeTab.home);
          } else {
            // 双击退出判断
            lastPopTime ??= DateTime.now();
            if (DateTime.now().difference(lastPopTime!) <= const Duration(seconds: 1)) {
              lastPopTime = DateTime.now();
              LogUtil.error('再按一次退出APP');
            } else {
              lastPopTime = DateTime.now();
              // 退出app
              await SystemChannels.platform.invokeMethod('SystemNavigator.pop');
            }
          }
          return false;
        },
        child: PageView(
          // 禁止左右滑动
          physics: const NeverScrollableScrollPhysics(),
          controller: _pageController,
          children: pageList,
        ),
      ),
      // 底部导航
      bottomNavigationBar: _buildBottomNavigationBar(context, selectedTab),
    );
  }

  // 底部导航栏布局
  BottomNavigationBar _buildBottomNavigationBar(
      BuildContext context, HomeTab selectedTab) {
    return BottomNavigationBar(
      currentIndex: selectedTab.index,
      type: BottomNavigationBarType.fixed,
      fixedColor: Theme.of(context).colorScheme.inversePrimary,
      onTap: (index) {
        // 切换页面
        _pageController.jumpToPage(index);
        final tab = HomeTab.values[index];
        // 发送切换指令
        context.read<HomeCubit>().setTab(tab);
      },
      items: [
        BottomNavigationBarItem(
          label: HomeTab.home.title,
          icon: const Icon(Icons.home),
        ),
        BottomNavigationBarItem(
          label: HomeTab.classification.title,
          icon: const Icon(Icons.my_library_books),
        ),
        BottomNavigationBarItem(
          label: HomeTab.dynamics.title,
          icon: const Icon(Icons.cloud),
        ),
        BottomNavigationBarItem(
          label: HomeTab.me.title,
          icon: const Icon(Icons.person),
        ),
      ],
    );
  }
}

// 其他布局,用于演示使用
class TestHomeItemPage extends StatelessWidget {
  final String txt;

  const TestHomeItemPage({super.key, required this.txt});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(txt),
      ),
      body: Center(
        child: Text(txt),
      ),
    );
  }
}

总结

在开始写这个页面之前,也踩了一些坑,比如大多数文章都是使用简单的方式实现了效果,可以用吗?可以用,但是我认为该控件的使用场景并不在首页,也许做轮播合适,毕竟以目前的app来看,首页是比较复杂的功能,也不太可能出现一次加载多个布局,每次显示都重绘一次,这样对于复杂的功能来讲性能堪忧,所以当时写完测试之后发现了不对,然后想到也许flutter有类似于ViewPage这样的组件,按照这个思路确实很容易就实现这个页面的简单效果,当然由于项目使用了bloc,所以对bloc的依赖比较高,如果项目中使用的其它状态管理框架,照着修改即可,如果有更好的思路欢迎一起讨论与学习。文章来源地址https://www.toymoban.com/news/detail-550409.html

到了这里,关于flutter 使用Bloc+PageView+BottomNavigationBar实现传统首页布局的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Flutter Bloc和StreamController结合实现登录功能

    本篇博文通过Flutter Bloc自己提供的登录demo来分析Bloc的用法(源码点此),虽然只是个小小的登录demo,但是麻雀虽小,五脏俱全。通过该demo,你可以了解到: 1、Dart yield yield*的用法 2、Flutter StreamController的作用 3、StreamController和Bloc之间的通信(博主认

    2024年02月12日
    浏览(31)
  • Flutter 使用pageview无缝隙自动轮播教程

    导入要使用的轮播图片 声明变量 然后在initState里面初始化一下 在dispose里面去掉 最后在你需要的地方加入下面代码就行了

    2024年02月07日
    浏览(34)
  • Flutter 之Bloc入门指南实现倒计时功能

    使用Bloc开发Flutter的项目,其项目结构都很简单明确,需要创建状态,创建事件,创建bloc,创建对应的View。flutter_timer项目来分析下Bloc的使用方法。 通过这篇博客,你

    2024年02月14日
    浏览(41)
  • Flutter底部导航BottomNavigationBar

    Flutter底部导航BottomNavigationBar 主要代码: 注意: 当item数量大于3个时一定要加type: BottomNavigationBarType.fixed,属性,源码应该默认为了shifting属性,当大于3个时菜单显示不正常,但是可以正常点击。 不设置type属性或者设置了type: BottomNavigationBarType.shifting,未选中的显示为和背景颜

    2024年02月15日
    浏览(23)
  • flutter:BottomNavigationBar和TabBar

    BottomNavigationBar r和 TabBar 都是用于创建导航栏的组件,但它们有一些区别。 位置不同: BottomNavigationBar 通常位于屏幕底部,用于主要导航;而 TabBar 通常位于屏幕顶部或底部,用于切换不同的视图或页面。 样式不同: BottomNavigationBar 是一个水平的导航栏,通常包含固定数量的

    2024年02月14日
    浏览(30)
  • Flutter Bloc组件buildWhen的妙用

    在Flutter中当状态发生改变的时候,Widget会重新build刷新页面。但是当状态发生改变的时候后,我们指向让有关联的Widget重绘,与之无关的Widget保持不变,比如对于登录页面,有用户名和密码两个组件:如下图。 构建代码如下: 当我们输入用户名的时候,仅仅希望 _UserNameInp

    2024年02月12日
    浏览(33)
  • Flutter之hydrated_bloc源码分析

    Flutter_Bloc是状态管理组件,hydrated_bloc是 Flutter_Bloc的扩展,它可以在APP重启的情况下,自动记录上次APP的状态。android中可以使用SharePreference来实现状态记录,在Flutter之hydrate_bloc组件入门指南一文中已经讲解了其基本用法,本篇博文就不对其原理进行简单分析,以计数器demo为

    2024年02月09日
    浏览(29)
  • 使用nio代替传统流实现文件上传和下载功能

    1.文件下载 2.文件上传

    2024年02月13日
    浏览(30)
  • 向量数据库入坑:传统文本检索方式的降维打击,使用 Faiss 实现向量语义检索

    在上一篇文章《聊聊来自元宇宙大厂 Meta 的相似度检索技术 Faiss》中,我们有聊到如何快速入门向量检索技术,借助 Meta AI(Facebook Research)出品的 faiss 实现“最基础的文本内容相似度检索工具”,初步接触到了“语义检索”这种对于传统文本检索方式具备“降维打击”的新

    2024年02月16日
    浏览(29)
  • 安卓:BottomNavigationBar——底部导航栏控件

    目录 一、BottomNavigationBar介绍 二、BottomNavigationBar的常用方法及其常用类 (一)、常用方法 1. 添加菜单项 2. 移除菜单项 3. 设置选中监听器 4. 设置当前选中项  5. 设置徽章  6. 样式和颜色定制 7. 动画效果  8. 隐藏底部导航栏。  9、设置模式 10.初始化 bottomNavigation  (二)、

    2024年02月08日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包