flutter之敲木鱼练习--基础案例

前言:

实践是检验真理的唯一标准,前期语法学的如何,一个小小demo试试手。 要想学的好,练习不可少;

一、本期敲木鱼demo主要功能介绍:

复制代码
主界面木鱼可以敲击,加功德
功德历史列表
木鱼样式功能切换
木鱼音效功能切换

实现效果图如下:

功德数增加动画效果:

功德历史列表:

木鱼样式选择面板:

音效选择列表:

以上为实现效果图

二、敲木鱼案例功能拆分

2.1 主页面的部分划分

页面布局角度: 主页面:上下布局--各封装一个模块,上布局 :Stack+Center+Positioned 下布局 :Center, 整体布局为:Column+Expanded

布局样式我们进行上下划分,功德数、音效按钮、图片按钮为上半部分,木鱼图片为下半部分

上半部分为图片、音效功德数这些模块设计到后续功能,我们做一个单独的模块封装。

新建一个页面,countpanel.dart【上半部分模块】,使用st快捷输入,这里使用的是无状态Widget【StatelessWidget】

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

class CountPanel extends StatelessWidget {
  final int count;//定义功德总数
  final VoidCallback onTapSwitchAudio;//定义切换音效方法
  final VoidCallback onTapSwitchImage;//定义切换木鱼图片方法


  const CountPanel({
    super.key,
    required this.count,
    required this.onTapSwitchAudio,
    required this.onTapSwitchImage,
  });

  @override
  Widget build(BuildContext context) {
  //自定义按钮内容样式
  final ButtonStyle style = ElevatedButton.styleFrom(
      minimumSize: const Size(50, 50),
      padding: EdgeInsets.zero,
      backgroundColor: Colors.green,
      elevation: 0,
    );
  //stack 组件 层叠布局和 Web 中的绝对定位、Android 中的 Frame 布局是相似的,子组件可以根据距父容器四个角的位置来确定自身的位置。
    return Stack(
      children: [
       //center 组件顾名思义,居中内容的组件
        Center(
          child: Text(
            '功德数: $count',
            style: const TextStyle(
              fontSize: 24,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
        //右侧音效、图片切换按钮--使用定位来实现 Positioned
         Positioned(
            right: 10,
            top: 10,
            //# Wrap 流失布局,当一行超出屏幕流失布局会自动拆分,进行换行
            child: Wrap(
              spacing: 8,
              direction: Axis.vertical,
              children: [
              //`ElevatedButton` 即"漂浮"按钮,自带点击事件:onPressed
                ElevatedButton(
                  style: style,
                  onPressed: onTapSwitchAudio,
                  child: const Icon(
                    Icons.music_note_outlined,
                    color: Colors.white,
                  ),
                ),
                ElevatedButton(
                  style: style,
                  onPressed: onTapSwitchImage,
                  child: const Icon(
                    Icons.image,
                    color: Colors.white,
                  ),
                )
              ],
            )),
      ],
    );
  }
}

下半部分图片看似只有一个,但是其实背后是包含着木鱼图片和文字动画效果,这里也封装为一个单独的模块 切换图片时,便于接收不同的imgUrl.

图片组件模块如下:

// 存放木鱼在首页点击图片

js 复制代码
class MuyuAssetsImage extends StatelessWidget {
  final String image;
  final VoidCallback onTap; //接收一个回调方法
  const MuyuAssetsImage({super.key, required this.image, required this.onTap});

  @override
  Widget build(BuildContext context) {
    return Center(
        // gestureDetector  组件监听手势回调
        child: GestureDetector(
      onTap: onTap,
      child: Image.asset(
        image,//接收父组件传递的url值
        height: 200,
      ),
    ));
  }
}

那么图片模块有了,我们来看一下文字动画效果如何实现

动画效果:是点击向上飘,放大又缩小 、文字由深色逐渐变淡

ScaleTransition 弹性动画,scale:放大数 SlideTransition ,根据位置变化的Widget
FadeTransition 淡出的动画效果, AnimationController 动画控制器

因此动画文字模块代码如下:

js 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_app_seven/muyu/fishenity.dart';

// 敲木鱼动画文字组件 --之前有bug 点击切换图片样式时会触发动画
class AnimateText extends StatefulWidget {
  final MeritRecord record;
  const AnimateText({super.key, required this.record});

  @override
  State<AnimateText> createState() => _AnimateTextState();
}

// 加入这个用于加入动画控制器
class _AnimateTextState extends State<AnimateText>
    with SingleTickerProviderStateMixin {
  late AnimationController controller;
  late Animation<double> opacity;
  late Animation<Offset> position;
  late Animation<double> scale;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
        vsync: this, duration: const Duration(milliseconds: 500));
    // 动画器属性配置,从开始
    opacity = Tween(begin: 1.0, end: 0.0).animate(controller);//透明度
    scale = Tween(begin: 1.0, end: 0.9).animate(controller);//放大到缩小值
    //位置变化
    position = Tween<Offset>(
      begin: const Offset(0, 5),
      end: Offset.zero,
    ).animate(controller);
    controller.forward();
  }

  @override
  // 在父widget中调用setState
  void didUpdateWidget(covariant AnimateText oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 避免功德未变化的情况下,启动动画场景
    if (oldWidget.record.id != widget.record.id) {
      controller.forward(from: 0);
    }
  }
  
  
  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    //过渡消失组件 还缺少缩放动画
    // 旋转
    return ScaleTransition(
      scale: scale,
      // 移动
      child: SlideTransition(
          position: position,
          child: FadeTransition(
            opacity: opacity,
            child: Text('功德数+${widget.record.value}'),
          )),
    );
  }
}

下期在续...

相关推荐
vvilkim14 小时前
Flutter 常用组件详解:Text、Button、Image、ListView 和 GridView
前端·flutter
getapi14 小时前
flutter把 pubspec.yaml 中的name改成了新的值
flutter·macos·cocoa
vvilkim14 小时前
Flutter 命名路由与参数传递完全指南
前端·flutter
程序员老刘14 小时前
iOS 26 beta1 真机无法执行hot reload
flutter·ios·客户端
无知的前端21 小时前
Flutter开发,GetX框架路由相关详细示例
android·flutter·ios
icc_tips1 天前
Flutter 的Async/Await 日常使用
flutter
造梦师1 天前
flutter基础面试知识汇总(二)
flutter
vvilkim1 天前
Flutter 核心概念:深入理解 StatelessWidget 与 StatefulWidget
开发语言·javascript·flutter
sunly_1 天前
Flutter:导航背景固定在顶部,下拉分页布局
开发语言·javascript·flutter