前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >flutter系列之:做一个会飞的菜单

flutter系列之:做一个会飞的菜单

作者头像
程序那些事
发布于 2023-09-06 02:18:18
发布于 2023-09-06 02:18:18
22500
代码可运行
举报
文章被收录于专栏:程序那些事程序那些事
运行总次数:0
代码可运行

简介

flutter中自带了drawer组件,可以实现通用的菜单功能,那么有没有一种可能,我们可以通过自定义动画来实现一个别样的菜单呢?

答案是肯定的,一起来看看吧。

定义一个菜单项目

因为这里的主要目的是实现菜单的动画,所以这里的菜单比较简单,我们的menu是一个StatefulWidget,里面就是一个Column组件,column中有四行诗:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  static const _menuTitles = [
    '迟日江山丽',
    '春风花草香',
    '泥融飞燕子',
    '沙暖睡鸳鸯',
  ];

    Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      child:_buildContent()
    );
  }


  Widget _buildContent() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const SizedBox(height: 16),
        ..._buildListItems()
      ],
    );
  }

  List<Widget> _buildListItems() {
    final listItems = <Widget>[];
    for (var i = 0; i < _menuTitles.length; ++i) {
      listItems.add(
         Padding(
            padding: const EdgeInsets.symmetric(horizontal: 36.0, vertical: 16),
            child: Text(
              _menuTitles[i],
              textAlign: TextAlign.center,
              style: const TextStyle(
                fontSize: 24,
                fontWeight: FontWeight.w500,
              ),
            ),
      )
      );
    }
    return listItems;
  }

让menu动起来

怎么让menu动起来呢?我们需要给最外层的AnimateMenuApp添加一个AnimationController,所以需要在_AnimateMenuAppState添加SingleTickerProviderStateMixin的mixin,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class _AnimateMenuAppState extends State<AnimateMenuApp>
    with SingleTickerProviderStateMixin {
  late AnimationController _drawerSlideController;

然后在initState中对_drawerSlideController进行初始化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  void initState() {
    super.initState();

    _drawerSlideController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 150),
    );
  }

在让menu动起来之前,我们需要设计一下动画的样式。假如我们的动画是让menu从右向左飞出。那么我们可以使用FractionalTranslation来进行offset进行位置变换。

并且当菜单没有开启的时候,我们需要显示一个空的组件,这里用SizedBox来替代。

当菜单开启的时候,就执行这个FractionalTranslation的动画,所以我们的build方法需要这样写:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  Widget _buildDrawer() {
    return AnimatedBuilder(
      animation: _drawerSlideController,
      builder: (context, child) {
        return FractionalTranslation(
          translation: Offset(1.0 - _drawerSlideController.value, 0.0),
          child: _isDrawerClosed() ? const SizedBox() : const Menu(),
        );
      },
    );
  }

FractionalTranslation中的Offset是根据_drawerSlideController的value来进行变化的。

那么_drawerSlideController的value怎么变化呢?

我们定义一个_toggleDrawer方法,在点击菜单按钮的时候来触发这个方法,从而实现_drawerSlideController的value变化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  void _toggleDrawer() {
    if (_isDrawerOpen() || _isDrawerOpening()) {
      _drawerSlideController.reverse();
    } else {
      _drawerSlideController.forward();
    }
  }

同时,我们定义下面几个判断菜单状态的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  bool _isDrawerOpen() {
    return _drawerSlideController.value == 1.0;
  }

  bool _isDrawerOpening() {
    return _drawerSlideController.status == AnimationStatus.forward;
  }

  bool _isDrawerClosed() {
    return _drawerSlideController.value == 0.0;
  }

因为菜单图标需要根据菜单状态来发生改变,菜单的状态又是依赖于_drawerSlideController,所以,我们把IconButton放到一个AnimatedBuilder里面,从而实现动态变化的效果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  PreferredSizeWidget _buildAppBar() {
    return AppBar(
      title: const Text(
        '动画菜单',
        style: TextStyle(
          color: Colors.black,
        ),
      ),
      backgroundColor: Colors.transparent,
      elevation: 0.0,
      automaticallyImplyLeading: false,
      actions: [
        AnimatedBuilder(
          animation: _drawerSlideController,
          builder: (context, child) {
            return IconButton(
              onPressed: _toggleDrawer,
              icon: _isDrawerOpen() || _isDrawerOpening()
                  ? const Icon(
                Icons.clear,
                color: Colors.black,
              )
                  : const Icon(
                Icons.menu,
                color: Colors.black,
              ),
            );
          },
        ),
      ],
    );
  }

最后实现的效果如下:

添加菜单内部的动画

上面的例子中整个菜单是作为一个整体来动画的,有没有可能菜单里面的每一个item也有自己的动画呢?

答案当然是肯定的。

我们只需要在上面的基础上将menu组件添加动画支持即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class _MenuState extends State<Menu> with SingleTickerProviderStateMixin

动画中的位移我们选择使用Transform.translate,同时还添加了淡入淡出的效果,也就是把上面例子中的Padding用AnimatedBuilder包裹起来,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  List<Widget> _buildListItems() {
    final listItems = <Widget>[];
    for (var i = 0; i < _menuTitles.length; ++i) {
      listItems.add(
        AnimatedBuilder(
          animation: _itemController,
          builder: (context, child) {
            final animationPercent = Curves.easeOut.transform(
              _itemSlideIntervals[i].transform(_itemController.value),
            );
            final opacity = animationPercent;
            final slideDistance = (1.0 - animationPercent) * 150;

            return Opacity(
              opacity: opacity,
              child: Transform.translate(
                offset: Offset(slideDistance, 0),
                child: child,
              ),
            );
          },
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 36.0, vertical: 16),
            child: Text(
              _menuTitles[i],
              textAlign: TextAlign.center,
              style: const TextStyle(
                fontSize: 24,
                fontWeight: FontWeight.w500,
              ),
            ),
          ),
        ),
      );
    }
    return listItems;
  }

AnimatedBuilder中的builder返回的是一个Opacity对象,里面包含了opacity和child两个属性。其中最终要的一个变化值是animationPercent,这个值是根据_itemController的value和初始设置的各个item的变化时间来决定的。

每个item的值是不一样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  void _createAnimationIntervals() {
    for (var i = 0; i < _menuTitles.length; ++i) {
      final startTime = _initialDelayTime + (_staggerTime * i);
      final endTime = startTime + _itemSlideTime;
      _itemSlideIntervals.add(
        Interval(
          startTime.inMilliseconds / _animationDuration.inMilliseconds,
          endTime.inMilliseconds / _animationDuration.inMilliseconds,
        ),
      );
    }
  }

最后运行结果如下:

总结

在flutter中一切皆可动画,我们只需要掌握动画创作的诀窍即可。

本文的例子:https://github.com/ddean2009/learn-flutter.git

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-06-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序那些事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
EasyExcel与POI对比及实现百万数据导入导出的基础示例
EasyExcel 是阿里巴巴开源的一款专注于解决大数据量Excel导入导出场景的Java类库。相较于传统的Apache POI等库,EasyExcel在设计上注重性能优化和降低内存开销,特别是在处理包含大量数据的Excel文件时表现突出。
用户7353950
2024/05/21
2.8K0
EasyExcel与POI对比及实现百万数据导入导出的基础示例
利用springboot 将数据库中的数据导出为excle。还实现将excle里面的数据上传到数据库里面
写一个接口,浏览器一输入这个接口,那么就可以导出数据库里面的数据到excle表里面了。要实现这个功能。我们使用springboot
一写代码就开心
2020/11/20
4.2K1
利用springboot 将数据库中的数据导出为excle。还实现将excle里面的数据上传到数据库里面
震撼!通过双重异步,Excel 10万行数据导入从191秒优化到2秒!
在现代的企业级应用开发中,海量数据的处理效率和并发性能优化是一个非常重要的课题。无论是大规模数据导入、文件解析,还是在分布式系统中处理高并发任务,如何提升系统的处理速度、合理利用计算资源、减少线程上下文切换的开销,这些都是开发者必须面对的问题。在这一背景下,线程池技术以及异步编程逐渐成为提升系统性能的利器。
程序员皮皮林
2024/11/10
4450
震撼!通过双重异步,Excel 10万行数据导入从191秒优化到2秒!
如何使用 SpringBoot 集成 EasyExcel 3.x 来实现优雅的 Excel 导入导出功能?
在日常开发中,Excel 是一个常用的数据交换格式。在Web应用程序中,实现Excel的导入和导出功能是非常常见的需求。SpringBoot 是一个流行的Java开发框架,而 EasyExcel 是一个强大且易于使用的Java Excel操作库。本文将向您介绍如何使用 SpringBoot 集成 EasyExcel 3.x 来实现优雅的 Excel 导入导出功能。
网络技术联盟站
2023/07/03
3.3K0
如何使用 SpringBoot 集成 EasyExcel 3.x 来实现优雅的 Excel 导入导出功能?
Spring Boot使用easyexcel实现导入导出Excel
目标:实现Spring Boot使用easyexcel实现导入导出Excel 工具:IDEA--2020.1 学习目标:Spring Boot使用easyexcel实现导入导出Excel 本次学习的工程下载链接放到文本最后面(含数据库) 首先导入依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.1.6</version> <
背雷管的小青年
2020/06/09
23.7K14
Spring Boot + EasyExcel 导入导出,好用到爆,可以扔掉 POI 了!
EasyExcel是阿里巴巴开源poi插件之一,主要解决了poi框架使用复杂,sax解析模式不容易操作,数据量大起来容易OOM,解决了POI并发造成的报错。
程序猿DD
2023/04/04
1.2K0
Spring Boot + EasyExcel 导入导出,好用到爆,可以扔掉 POI 了!
EasyExcel处理Mysql百万数据的导入导出案例,秒级效率,拿来即用!
今天终于更新新专栏 《EfficientFarm》 的第二篇博文啦,本文主要来记录一下对于EasyExcel的高效应用,包括对MySQL数据库百万级数据量的导入与导出操作,以及性能的优化(争取做到秒级性能!)。
JavaBuild
2024/05/27
8550
EasyExcel处理Mysql百万数据的导入导出案例,秒级效率,拿来即用!
Spring Boot + EasyExcel 导入导出,好用到爆,可以扔掉 POI 了!
EasyExcel是阿里巴巴开源poi插件之一,主要解决了poi框架使用复杂,sax解析模式不容易操作,数据量大起来容易OOM,解决了POI并发造成的报错。
芋道源码
2021/10/14
2.4K0
厉害了!12秒将百万数据通过EasyExcel导入MySQL数据库中
我们在上一篇文章中提到了通过EasyExcel处理Mysql百万数据的导入功能(一键看原文),当时我们经过测试数据的反复测验,100万条放在excel中的数据,仅有4个字段的情况下,导入数据库平均耗时500秒,这对于我们来说肯定难以接受,今天我们就来做一次性能优化。
JavaBuild
2024/05/27
1.2K0
厉害了!12秒将百万数据通过EasyExcel导入MySQL数据库中
excel导入导出百万级数据优化
在我前年找实习的时候,遇到了面试官问我:mysql从excel导出百万级数据,该怎么做?我听到的第一反应是:我*,我哪去接触百万级的数据,你们导出的数据是什么?我还是一个才找实习工作的大学生啊。后来也有各种各样的八股文,介绍这种导入导出的优化,然而我拒绝囫囵吞枣式学习,背八股文的方式学习。shigen也在这里实测了,在此先感谢蜗牛,为我提供了高质量的代码参考和分析案例。
shigen
2023/08/22
9440
使用EasyExcel导入导出Excel
快速开始:https://www.yuque.com/easyexcel/doc/easyexcel
Miloce
2022/09/28
2.8K0
大型项目技术栈第六讲 EasyExcel的使用
实体类对需要导出或者导入的字段增加@ExcelProperty注解,index值为对应excel中的列,value为表头,format为日期格式化
易兮科技
2020/09/26
1.1K0
读取Excel还用POI?试试这款开源工具
Java 后端程序员应该会遇到读取 Excel 信息到 DB 等相关需求,脑海中可能突然间想起 Apache POI 这个技术解决方案,但是当 Excel 的数据量非常大的时候,你也许发现,POI 是将整个 Excel 的内容全部读出来放入到内存中,所以内存消耗非常严重,如果同时进行包含大数据量的 Excel 读操作,很容易造成内存溢出问题
用户4172423
2019/10/23
1.3K0
读取Excel还用POI?试试这款开源工具
告别Apache POI,这才是Java读写Excel的利器
Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。
马拉松程序员
2022/04/26
1.8K0
告别Apache POI,这才是Java读写Excel的利器
封装了一个Excel导入加校验的工具,同事们用了都说好
最近在做Excel导入功能,产品要求对导入数据先进行校验然后再入库。于是简单封装了一个工具,结果兄弟们用了都说好,今天就把思路分享出来。
码农小胖哥
2021/04/26
4.5K0
封装了一个Excel导入加校验的工具,同事们用了都说好
SpringBoot整合EasyExcel实现复杂Excel表格的导入&导出功能
🎉SpringBoot整合EasyExcel实现复杂Excel表格的导入&导出功能
IT_陈寒
2023/12/14
6630
SpringBoot整合EasyExcel实现复杂Excel表格的导入&导出功能
实现百万级数据从Excel导入到数据库的方式
这个案例实际上涉及到多个方面,需要我们系统地分析。让我们首先看看,从Excel中读取百万级数据并将其插入数据库时可能遇到的问题:
@派大星
2024/04/15
7070
实现百万级数据从Excel导入到数据库的方式
【二十五】springboot使用EasyExcel和线程池实现多线程导入Excel数据
在公司开发时,遇到一个很常见的导入功能的需求,需要导入Excel文件,由此想到了阿里巴巴的EasyExcel这个方便的工具,当客户给我说需要支持大数据量导入时,我想到了使用线程池来多线程处理导入数据库这个操作。由此本章记录一下这次操作。
小z666
2024/06/21
1.3K0
【二十五】springboot使用EasyExcel和线程池实现多线程导入Excel数据
EasyExcel如何实现复杂数据的导入
在我们常使用的系统中,难免会遇到数据导入的情况。其实导入做起来并不是很难,直接用到easyexcel读取数据写入到数据库即可。看似好简单的样子,是的,现在这些开源的框架已经帮我们把所有能遇到的问题都给考虑到了。那我们需要考虑到什么呢?shigen觉得最重要的是实际的业务场景。
用户10868235
2023/12/08
6330
EasyExcel如何实现复杂数据的导入
Springboot整合EasyExcel,实现Excel文件上传
EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。 它能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。EasyExcel是在尽可能节约内存的情况下支持读写百M的Excel。
utopia
2023/03/21
2K0
推荐阅读
相关推荐
EasyExcel与POI对比及实现百万数据导入导出的基础示例
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档