Flutter 开发的极简风格音乐播放器

2,006 阅读3分钟

767shots_so.jpeg

  • 去年失业闲的无聊开发了一个简单的音乐播放器,过去一年,个人水平也有长进,近期花时间使用最新版Flutter SDK 重构。

  • 其实是有设计图的进行参考的,具体的作者已经找不到了,不过贴一下展示一下。

QQ_1762002673499.png

  • Flutter 在不同设备上的UI兼容性非常好。

微信图片_2025-11-01_211354_496.jpg

项目中一些技术分享

半透明的高斯模糊AppBar 和 BottomNavigationBar

# Flutter 透明模糊化AppBar 和 BottomNavigationBar 优化方案

参考代码

appBar: AppBar(
    title: const Text("DM's Blur Gallery"),
    flexibleSpace: BlurWidget(child: SizedBox.expand()),
),
extendBody: true,
bottomNavigationBar: BlurWidget(
child: _buildMenus(themeData),
),

滚动列表中的高斯模糊锯齿和效果丢失问题

每个音乐卡片都存在高斯模糊区域。如果裁剪设置不合理,就会存在问题

QQ_1762003682944.png

ClipRRect(
      borderRadius: BorderRadius.circular(10),
      clipBehavior: Clip.antiAliasWithSaveLayer,

Flutter 的ClipRRect组件有四种模式,默认是hardEdge,性能较好,但是在physics: ClampingScrollPhysics() 时,滚动列表会丢失模糊效果。 Flutter 在安卓上,listview 类型 默认都是 ClampingScrollPhysics。 将Clip模式设置为Clip.antiAliasWithSaveLayer ,即可取得最佳效果。

下面是裁剪模式的粗略翻译。

none 不裁减
hardEdge 粗略裁剪
antiAlias 高精度裁剪
antiAliasWithSaveLayer 超高精度裁剪,并隔离区域

主题色切换

在main.dart的App配置中,设置

return GetMaterialApp(
      title: 'DMusic',
      theme: _getLightTheme(),
      darkTheme: _getDarkTheme(),
      themeMode: ThemeMode.dark,
      debugShowCheckedModeBanner: false,
      home: SplashPage(),
    );
colorScheme: const ColorScheme.dark(
        brightness: Brightness.dark,
        // 主色调
        primary: Colors.black,
        //主色调 - 反转
        inversePrimary: Colors.black,
        // 文字颜色
        onSurface: Colors.white,
        // 文字颜色 - 反转
        onInverseSurface: Colors.black,
        // 表面颜色
        surface: Colors.black,
        // 表面颜色 - 反转
        inverseSurface: Colors.white,
      ),

弧形进度条(支持点击和拖拽)

1.gif

# Flutter 椭圆弧型(弓型)进度条

主要是通过修改Flutter 默认Slider的渲染逻辑,加入了路径渲染 和 路径裁剪。

  • 自行实现难度较高,所以修改官方组件相对简单。

底部卡片动画

2.gif

animate_do: ^4.2.0 # 动画组件

底部卡片的动画使用的是

SlideInUp(
   from: curveHeight,
   animate: controller.slideController?.isCompleted ?? false,
   controller: (slideController) {
       controller.slideController = slideController;
   },
   child: _buildCard(theme),
),

底部弧形卡片

/// 底部画刷
class BottomCurvePainter extends CustomPainter {
  BottomCurvePainter(this.backgroundColor);

  /// 背景色
  final Color backgroundColor;

  @override
  void paint(Canvas canvas, Size size) {
    debugPrint("重绘:底部背景-画刷");

    // 贝塞尔曲率
    double bezier = 25;

    Path background = Path()
      ..moveTo(0, bezier)
      ..quadraticBezierTo(size.width / 2, -bezier, size.width, bezier)
      ..lineTo(size.width, size.height)
      ..lineTo(0, size.height)
      ..lineTo(0, bezier)
      ..close();

    //canvas.drawShadow(background, Colors.black, 10, true);
    canvas.clipPath(background);
    canvas.drawColor(backgroundColor, BlendMode.srcIn);
  }

  @override
  bool shouldRepaint(covariant BottomCurvePainter oldDelegate) {
    return backgroundColor != oldDelegate.backgroundColor;
  }
}

音频播放

just_audio: ^0.10.5 # 音频播放

这个组件其实有一些问题,比如

  • 不兼容Windows
  • 没有切换音乐的回调

有可能是文档没查好,如果有不同理解,请在评论区提出。

播放按钮动画

3.gif

AnimatedIcon(
   size: iconSize,
   color: theme.colorScheme.primary,
   icon: AnimatedIcons.play_pause,
   progress: controller ?? AlwaysStoppedAnimation(0),
),

SafeArea安全区域

按钮下方弧形屏幕区域,既是非安全区域

QQ_1762005337996.png

/// 底部安全区域
double bottomSafe = MediaQuery.of(context).padding.bottom;

/// 使用SafeAre的minimum就可以设置安全区域。
/// minimum 是最低安全距离的意思,当设备没有安全距离或安全距离小于最小值都会使用设定的最小值。
SliverSafeArea(
  minimum: EdgeInsets.only(
  // left: 15,
  // right: 15,
  bottom: bottomHeight + 20,
),

11月23日 增加Navidrome支持(仅支持专辑列表) 增加歌词显示

a2349176-8d14-473f-9985-4a3ec939df12.png

最后源码也放出来,当然这个项目也会持续更新,如果有喜欢设计的朋友可以联系我,一起参与到后续的开发设计中。 源码地址