🎵 Аудиоплеер для FlutterFlow

Слой 1+2: Решение и Логика

Стандартный аудиоплеер во FlutterFlow иногда имеет ограничения в Web-версии или требует сложной настройки. Данный кастомный виджет на базе библиотеки just_audio работает стабильно на всех платформах и поддерживает фоновое воспроизведение.

Основные возможности:

  • Полная поддержка Web (в отличие от некоторых встроенных решений).
  • Кастомизация интерфейса (цвета, прогресс-бар).
  • Работа с заблокированным экраном (для мобильных устройств).

Параметры виджета:

  1. audioUrl (String): прямая ссылка на аудиофайл.
  2. name (String): название трека для отображения.
  3. showProgressBar (bool): переключатель видимости шкалы прогресса.

💻 Код виджета (Вариант 1)

Вы можете скопировать этот код и вставить его в раздел Custom Widgets во FlutterFlow. Не забудьте добавить зависимость just_audio в Pubspec Dependencies.

import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
 
class MusicWidget extends StatefulWidget {
  const MusicWidget({
    super.key,
    this.width,
    this.height,
    required this.audioUrl,
    required this.showProgressBar,
    required this.name,
  });
 
  final double? width;
  final double? height;
  final String audioUrl;
  final bool showProgressBar;
  final String name;
 
  @override
  State<MusicWidget> createState() => _MusicWidgetState();
}
 
class _MusicWidgetState extends State<MusicWidget> {
  late AudioPlayer _audioPlayer;
  bool _isPlaying = false;
 
  @override
  void initState() {
    super.initState();
    _audioPlayer = AudioPlayer();
    _loadAudio();
  }
 
  Future<void> _loadAudio() async {
    try {
      await _audioPlayer.setUrl(widget.audioUrl);
    } catch (e) {
      print("Error loading audio: $e");
    }
  }
 
  void _togglePlayPause() async {
    setState(() {
      _isPlaying = !_isPlaying;
    });
 
    if (_isPlaying) {
      await _audioPlayer.play();
    } else {
      await _audioPlayer.pause();
    }
  }
 
  @override
  void dispose() {
    _audioPlayer.dispose();
    super.dispose();
  }
 
  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Color(0xFF1C0335),
        borderRadius: BorderRadius.circular(10),
      ),
      padding: const EdgeInsets.all(12),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            widget.name,
            style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white),
          ),
          const SizedBox(height: 15),
          IconButton(
            icon: Icon(
              _isPlaying ? Icons.pause_circle_filled : Icons.play_circle_filled,
              color: Colors.white,
              size: 50,
            ),
            onPressed: _togglePlayPause,
          ),
          if (widget.showProgressBar)
            StreamBuilder<Duration>(
              stream: _audioPlayer.positionStream,
              builder: (context, snapshot) {
                final position = snapshot.data ?? Duration.zero;
                final duration = _audioPlayer.duration ?? Duration.zero;
                return Slider(
                  value: position.inSeconds.toDouble(),
                  max: duration.inSeconds.toDouble(),
                  onChanged: (value) => _audioPlayer.seek(Duration(seconds: value.toInt())),
                  activeColor: Colors.white,
                  inactiveColor: Colors.white24,
                );
              },
            ),
        ],
      ),
    );
  }
}

Слой 3: Источники (Proven Roots)