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

下期在续...

相关推荐
傅里叶1 天前
Flutter项目使用 buf.build
flutter
恋猫de小郭1 天前
iOS 26 开始强制 UIScene ,你的 Flutter 插件准备好迁移支持了吗?
android·前端·flutter
yuanlaile1 天前
Flutter开发HarmonyOS鸿蒙App商业项目实战已出炉
flutter·华为·harmonyos
CodeCaptain1 天前
可直接落地的「Flutter 桥接鸿蒙 WebSocket」端到端实施方案
websocket·flutter·harmonyos
stringwu1 天前
Flutter 中的 MVVM 架构实现指南
前端·flutter
消失的旧时光-19432 天前
Flutter 异步体系终章:FutureBuilder 与 StreamBuilder 架构优化指南
flutter·架构
消失的旧时光-19432 天前
Flutter 异步 + 状态管理融合实践:Riverpod 与 Bloc 双方案解析
flutter
程序员老刘2 天前
Flutter版本选择指南:避坑3.27,3.35基本稳定 | 2025年10月
flutter·客户端
—Qeyser2 天前
Flutter网络请求Dio封装实战
网络·flutter·php·xcode·android-studio