Flutter---音效模式选择器

效果图

主要功能:

音效模式切换:用户可以在三种音效模式之间循环切换

  • 🎵 音乐模式 - 背景:音乐背景图,图标:苹果

  • 🎬 电影模式 - 背景:电影背景图,图标:香蕉

  • 🎮 游戏模式 - 背景:游戏背景图,图标:樱桃

交互功能:

左右切换:

  • 点击左侧图标(芒果)切换到上一个模式

  • 点击右侧图标(芒果)切换到下一个模式

  • 支持循环切换(音乐 ↔ 电影 ↔ 游戏)

页面布局:

  • 顶部:返回按钮(樱桃图标) + 模式标题

  • 中部:模式描述文字

  • 底部:模式选择控件 + 确认按钮(三色渐变)

步骤

①新建枚举

Dart 复制代码
enum SoundMode{music,movie,game}

②新建私有变量,设置为当前选中模式,默认为音乐模式

Dart 复制代码
//当前选中的模式
  SoundMode _currentMode = SoundMode.music;//进入的第一个页面

③通过switch/case获取当前的背景图、中间的图标、当前模式的标题、模式的描述、

Dart 复制代码
  //获取当前模式的背景图
  String get _backgroundImage{
    switch(_currentMode){
      case SoundMode.music:
        return "assets/images/music_mode_background.jpg";
      case SoundMode.movie:
        return "assets/images/movie_mode_background.jpg";
      case SoundMode.game:
        return "assets/images/game_mode_background.jpg";
      default:
        return "assets/images/music_mode_background.jpg";
    }
  }

  //获取当前模式的中间图标
  String get _centerIcon{
    switch(_currentMode){
      case SoundMode.music:
        return "assets/images/apple.png";
      case SoundMode.movie:
        return "assets/images/banana.png";
      case SoundMode.game:
        return "assets/images/cherry.png";
    }
  }

  //获取当前模式的标题
  String get _modeTitle{
    switch(_currentMode){
      case SoundMode.music:
        return "音乐模式";
      case SoundMode.movie:
        return "电影模式";
      case SoundMode.game:
        return "游戏模式";
    }
  }

  //获取当前模式的描述
  List<String> get _modeDescription{
    switch(_currentMode){
      case SoundMode.music:
        return ["这是音乐模式","音乐模式很好"];
      case SoundMode.movie:
        return ["这是电影模式","电影模式很好"];
      case SoundMode.game:
        return ["这是游戏模式","游戏模式很好"];
    }
  }

④写左右按钮切换模式的点击事件

Dart 复制代码
  //切换到上一个模式
  void _previousMode(){
    setState(() {
      final index = SoundMode.values.indexOf(_currentMode); //获取当前模式下标
      _currentMode = SoundMode.values[(index - 1) % SoundMode.values.length];
    });
  }

  //切换到下一个模式
  void _nextMode(){
    setState(() {
      final index = SoundMode.values.indexOf(_currentMode);//获取当前模式下标
      _currentMode = SoundMode.values[(index + 1) % SoundMode.values.length];
      //假设当前index = 0,则(0+1)%3 = 1,则SoundMode.values[1] = movie,然后重新构建UI
    });
  }

点击事件的实现

Dart 复制代码
//下一个模式
_nextMode() 执行过程:
1. index = SoundMode.values.indexOf(_currentMode) 
   → index = 0 (因为_currentMode是music)

2. (index + 1) % SoundMode.values.length
   → (0 + 1) % 3 = 1 % 3 = 1

3. SoundMode.values[1] 
   → SoundMode.movie

4. _currentMode = SoundMode.movie



//上一个模式

_previousMode() 执行过程(假设当前是movie):
1. index = SoundMode.values.indexOf(_currentMode) 
   → index = 1 (因为_currentMode是movie)

2. (index - 1) % SoundMode.values.length
   → (1 - 1) % 3 = 0 % 3 = 0

3. SoundMode.values[0] 
   → SoundMode.music

4. _currentMode = SoundMode.music

完整的触发流程

Dart 复制代码
用户点击 → 调用 _nextMode()/_previousMode() → setState() → 重建UI
     ↓
_currentMode 改变 → 所有getter重新计算
     ↓
_backgroundImage → 返回新背景图路径
_centerIcon → 返回新图标路径  
_modeTitle → 返回新标题
_modeDescription → 返回新描述
     ↓
UI使用新值重新渲染 → 用户看到界面更新

⑤建造UI,使用Stack,叠加布局

Dart 复制代码
body: Stack(
        children: [
          Container(
            height: double.infinity,
            width: double.infinity,
            decoration: BoxDecoration(
              image: DecorationImage(
                image: AssetImage(_backgroundImage), //动态背景图片
                fit:BoxFit.cover,
              ),
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Padding(padding: EdgeInsets.only(top:50,left:27),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  children: [
                    Image.asset("assets/images/cherry.png",width: 20,height: 20,),
                    const SizedBox(width: 102,),
                    Text(
                      _modeTitle,
                      style: const TextStyle(
                          color: Colors.white,
                          fontSize: 22,
                          fontWeight: FontWeight.bold
                      ),
                    ),
                  ],
                ),
                ),
                const SizedBox(height: 350,),

                //动态描述
                for (var description in _modeDescription)
                  Text(
                    description,
                    style: const TextStyle(
                      color: Colors.black,
                      fontSize: 14,
                    ),
                  ),
                const SizedBox(height: 67,),

                Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    GestureDetector(
                      onTap: _previousMode,
                      child: Image.asset("assets/images/mango.png",width: 20,height: 20,),
                    ),

                    const SizedBox(width: 21,),
                    Container(
                      width: 1,
                      height: 48,
                      decoration: const BoxDecoration(
                        color: Color(0xFFD8D8D8),
                      ),
                    ),
                    const SizedBox(width: 45,),
                    Image.asset(_centerIcon,width: 35,height: 35,),

                    const SizedBox(width: 45,),

                    Container(
                      width: 1,
                      height: 48,
                      decoration: const BoxDecoration(
                        color: Color(0xFFD8D8D8),
                      ),
                    ),
                    const SizedBox(width: 21,),
                    GestureDetector(
                      onTap: _nextMode,
                      child: Image.asset("assets/images/mango.png",width: 20,height: 20,),
                    ),

                  ],
                ),

                //确认按钮
                const SizedBox(height: 50,),
                Container(
                  width: 249,
                  height: 48,
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(27),
                    gradient: const LinearGradient(
                      begin: Alignment.centerLeft,    // 从上开始
                      end: Alignment.centerRight,   // 到底结束
                      colors: [
                        Colors.blue,      // 顶部颜色
                        Colors.blueGrey,     // 中间颜色
                        Colors.green,    // 底部颜色
                      ],
                    ),
                  ),
                  child: Center(
                    child: Text(
                      "确认",
                      style: const TextStyle(
                        color: Colors.white,
                        fontWeight: FontWeight.bold,
                        fontSize: 24,
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),

⑥UI中有一个三种颜色的渲染按钮(从左到右),改变两个参数就能变成从上到下的渲染

Dart 复制代码
//确认按钮
                const SizedBox(height: 50,),
                Container(
                  width: 249,
                  height: 48,
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(27),
                    gradient: const LinearGradient(
                      begin: Alignment.centerLeft,    // 左到右
                      end: Alignment.centerRight,   //从上到下渲染,可以换成topCenter到bottomCenter
                      colors: [
                        Colors.blue,      // 顶部颜色
                        Colors.blueGrey,     // 中间颜色
                        Colors.green,    // 底部颜色
                      ],
                    ),
                  ),
                  child: Center(
                    child: Text(
                      "确认",
                      style: const TextStyle(
                        color: Colors.white,
                        fontWeight: FontWeight.bold,
                        fontSize: 24,
                      ),
                    ),
                  ),
                ),

代码实例

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

enum SoundMode{music,movie,game}

class HomePage extends StatefulWidget{
  const HomePage({super.key});

  @override
  State<StatefulWidget> createState() => _HomePageState();

}

class _HomePageState extends State<HomePage> {

  //当前选中的模式
  SoundMode _currentMode = SoundMode.music;//进入的第一个页面

  //获取当前模式的背景图
  String get _backgroundImage{
    switch(_currentMode){
      case SoundMode.music:
        return "assets/images/music_mode_background.jpg";
      case SoundMode.movie:
        return "assets/images/movie_mode_background.jpg";
      case SoundMode.game:
        return "assets/images/game_mode_background.jpg";
      default:
        return "assets/images/music_mode_background.jpg";
    }
  }

  //获取当前模式的中间图标
  String get _centerIcon{
    switch(_currentMode){
      case SoundMode.music:
        return "assets/images/apple.png";
      case SoundMode.movie:
        return "assets/images/banana.png";
      case SoundMode.game:
        return "assets/images/cherry.png";
    }
  }

  //获取当前模式的标题
  String get _modeTitle{
    switch(_currentMode){
      case SoundMode.music:
        return "音乐模式";
      case SoundMode.movie:
        return "电影模式";
      case SoundMode.game:
        return "游戏模式";
    }
  }

  //获取当前模式的描述
  List<String> get _modeDescription{
    switch(_currentMode){
      case SoundMode.music:
        return ["这是音乐模式","音乐模式很好"];
      case SoundMode.movie:
        return ["这是电影模式","电影模式很好"];
      case SoundMode.game:
        return ["这是游戏模式","游戏模式很好"];
    }
  }

  //切换到上一个模式
  void _previousMode(){
    setState(() {
      final index = SoundMode.values.indexOf(_currentMode); //获取当前模式下标
      _currentMode = SoundMode.values[(index - 1) % SoundMode.values.length];
    });
  }

  //切换到下一个模式
  void _nextMode(){
    setState(() {
      final index = SoundMode.values.indexOf(_currentMode);//获取当前模式下标
      _currentMode = SoundMode.values[(index + 1) % SoundMode.values.length];
      //假设当前index = 0,则(0+1)%3 = 1,则SoundMode.values[1] = movie,然后重新构建UI
    });
  }

  //UI构建
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          Container(
            height: double.infinity,
            width: double.infinity,
            decoration: BoxDecoration(
              image: DecorationImage(
                image: AssetImage(_backgroundImage), //动态背景图片
                fit:BoxFit.cover,
              ),
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Padding(padding: EdgeInsets.only(top:50,left:27),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  children: [
                    Image.asset("assets/images/cherry.png",width: 20,height: 20,),
                    const SizedBox(width: 102,),
                    Text(
                      _modeTitle,
                      style: const TextStyle(
                          color: Colors.white,
                          fontSize: 22,
                          fontWeight: FontWeight.bold
                      ),
                    ),
                  ],
                ),
                ),
                const SizedBox(height: 350,),

                //动态描述
                for (var description in _modeDescription)
                  Text(
                    description,
                    style: const TextStyle(
                      color: Colors.black,
                      fontSize: 14,
                    ),
                  ),
                const SizedBox(height: 67,),

                Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    GestureDetector(
                      onTap: _previousMode,
                      child: Image.asset("assets/images/mango.png",width: 20,height: 20,),
                    ),

                    const SizedBox(width: 21,),
                    Container(
                      width: 1,
                      height: 48,
                      decoration: const BoxDecoration(
                        color: Color(0xFFD8D8D8),
                      ),
                    ),
                    const SizedBox(width: 45,),
                    Image.asset(_centerIcon,width: 35,height: 35,),

                    const SizedBox(width: 45,),

                    Container(
                      width: 1,
                      height: 48,
                      decoration: const BoxDecoration(
                        color: Color(0xFFD8D8D8),
                      ),
                    ),
                    const SizedBox(width: 21,),
                    GestureDetector(
                      onTap: _nextMode,
                      child: Image.asset("assets/images/mango.png",width: 20,height: 20,),
                    ),

                  ],
                ),

                //确认按钮
                const SizedBox(height: 50,),
                Container(
                  width: 249,
                  height: 48,
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(27),
                    gradient: const LinearGradient(
                      begin: Alignment.centerLeft,    // 左到右
                      end: Alignment.centerRight,   //从上到下渲染,可以换成topCenter到bottomCenter
                      colors: [
                        Colors.blue,      // 顶部颜色
                        Colors.blueGrey,     // 中间颜色
                        Colors.green,    // 底部颜色
                      ],
                    ),
                  ),
                  child: Center(
                    child: Text(
                      "确认",
                      style: const TextStyle(
                        color: Colors.white,
                        fontWeight: FontWeight.bold,
                        fontSize: 24,
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
相关推荐
majingming12321 分钟前
FUNCTION
java·前端·javascript
A_nanda1 小时前
Vue项目升级
前端·vue3·vue2
SuperEugene1 小时前
Axios 接口请求规范实战:请求参数 / 响应处理 / 异常兜底,避坑中后台 API 调用混乱|API 与异步请求规范篇
开发语言·前端·javascript·vue.js·前端框架·axios
abigale032 小时前
【浏览器 API / 网络请求 / 文件处理】前端文件上传全流程:从基础上传到断点续传
前端·typescript·文件上传·vue cli
Setsuna_F_Seiei2 小时前
AI 对话应用之页面滚动交互的实现
前端·javascript·ai编程
新缸中之脑2 小时前
追踪来自Agent的Web 流量
前端
wefly20173 小时前
从使用到原理,深度解析m3u8live.cn—— 基于 HLS.js 的 M3U8 在线播放器实现
java·开发语言·前端·javascript·ecmascript·php·m3u8
英俊潇洒美少年3 小时前
vue如何实现react useDeferredvalue和useTransition的效果
前端·vue.js·react.js
kyriewen114 小时前
给浏览器画个圈:CSS contain 如何让页面从“卡成PPT”变“丝滑如德芙”
开发语言·前端·javascript·css·chrome·typescript·ecmascript
英俊潇洒美少年4 小时前
react19和vue3的优缺点 对比
前端·javascript·vue.js·react.js