Flutter---音效模式选择器
效果图
主要功能:
音效模式切换:用户可以在三种音效模式之间循环切换
🎵 音乐模式 - 背景:音乐背景图,图标:苹果
🎬 电影模式 - 背景:电影背景图,图标:香蕉
🎮 游戏模式 - 背景:游戏背景图,图标:樱桃
交互功能:
左右切换:
点击左侧图标(芒果)切换到上一个模式
点击右侧图标(芒果)切换到下一个模式
支持循环切换(音乐 ↔ 电影 ↔ 游戏)
页面布局:
顶部:返回按钮(樱桃图标) + 模式标题
中部:模式描述文字
底部:模式选择控件 + 确认按钮(三色渐变)
步骤
①新建枚举
enum SoundMode{music,movie,game}
②新建私有变量,设置为当前选中模式,默认为音乐模式
//当前选中的模式SoundMode _currentMode = SoundMode.music;//进入的第一个页面
③通过switch/case获取当前的背景图、中间的图标、当前模式的标题、模式的描述、
//获取当前模式的背景图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});}
点击事件的实现
//下一个模式
_nextMode() 执行过程:
1. index = SoundMode.values.indexOf(_currentMode) → index = 0 (因为_currentMode是music)2. (index + 1) % SoundMode.values.length→ (0 + 1) % 3 = 1 % 3 = 13. SoundMode.values[1] → SoundMode.movie4. _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 = 03. SoundMode.values[0] → SoundMode.music4. _currentMode = SoundMode.music
完整的触发流程
用户点击 → 调用 _nextMode()/_previousMode() → setState() → 重建UI↓
_currentMode 改变 → 所有getter重新计算↓
_backgroundImage → 返回新背景图路径
_centerIcon → 返回新图标路径
_modeTitle → 返回新标题
_modeDescription → 返回新描述↓
UI使用新值重新渲染 → 用户看到界面更新
⑤建造UI,使用Stack,叠加布局
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中有一个三种颜色的渲染按钮(从左到右),改变两个参数就能变成从上到下的渲染
//确认按钮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到bottomCentercolors: [Colors.blue, // 顶部颜色Colors.blueGrey, // 中间颜色Colors.green, // 底部颜色],),),child: Center(child: Text("确认",style: const TextStyle(color: Colors.white,fontWeight: FontWeight.bold,fontSize: 24,),),),),
代码实例
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';enum SoundMode{music,movie,game}class HomePage extends StatefulWidget{const HomePage({super.key});@overrideState<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构建@overrideWidget 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到bottomCentercolors: [Colors.blue, // 顶部颜色Colors.blueGrey, // 中间颜色Colors.green, // 底部颜色],),),child: Center(child: Text("确认",style: const TextStyle(color: Colors.white,fontWeight: FontWeight.bold,fontSize: 24,),),),),],),),],),);}
}