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

下期在续...

相关推荐
晚霞的不甘15 分钟前
实战:从零构建一个支持手机、手表与车机的 Flutter 全场景健康应用
flutter·智能手机
kirk_wang19 分钟前
当Flutter遇见纯血鸿蒙:SQLite兼容适配的实战与思考
flutter·移动开发·跨平台·arkts·鸿蒙
克喵的水银蛇2 小时前
Flutter 通用输入框封装实战:带校验 / 清除 / 密码切换的 InputWidget
前端·javascript·flutter
小蜜蜂嗡嗡15 小时前
flutter namespace问题
android·flutter
克喵的水银蛇15 小时前
Flutter 适配实战:屏幕适配 + 暗黑模式 + 多语言
前端·javascript·flutter
西西学代码16 小时前
flutter---自定义白噪音UI
flutter
肠胃炎16 小时前
Flutter ListView 组件及各种模式
flutter
sunly_16 小时前
Flutter:设备唯一id生成,存储,
flutter
走在路上的菜鸟19 小时前
Android学Dart学习笔记第十节 循环
android·笔记·学习·flutter
小蜜蜂嗡嗡20 小时前
【flutter项目从xcode运行时报错:Undefined symbol: _OBJC_CLASS_$_WeiboSDK】
flutter·cocoa·xcode