11.13日笔记

29 阅读2分钟

混入类保持页面状态:

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

  @override
  State<MyPage> createState() => _MyPageState();
}
// 这里混入
class _MyPageState extends State<MyPage> with AutomaticKeepAliveClientMixin {
  final ApiService _apiService = ApiService();
  List<Map<String, dynamic>> _favoritePlaylists = [];
  List<Map<String, dynamic>> _createdPlaylists = [];
  bool _isLoadingFavorite = true;
  bool _isLoadingCreated = true;
  Map<String, dynamic>? _favoriteMusic;

  @override
  bool get wantKeepAlive => true; // 这里保持页面状态
}

毛玻璃效果模糊层:

Stack(
        children: [
          Image.network(
            playlistDetail?['coverUrl'] ?? '',
            width: double.infinity,
            height: double.infinity,
            fit: BoxFit.cover,
            errorBuilder: (context, error, stackTrace) {
              return Container(color: Colors.grey[200]);
            },
          ),
          Positioned.fill(
// BackdropFilter 是一个背景滤镜组件,用于对背景内容应用图像滤镜效果,实现毛玻璃、模糊、颜色调整等视觉效果。
// BackdropFilter 可以对它后面的内容应用滤镜效果,创建出类似 iOS 毛玻璃、模糊背景的效果。
            child: BackdropFilter(
              filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
              child: Container(color: Colors.black.withValues(alpha: 0.1)),
            ),
          ),
        ],
      ),
// 下边背景的这种效果,三个层

image-20251113141844444.png

滑动后固定到头部组件:

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  final Widget child;

  _SliverAppBarDelegate({required this.child});

  @override
  double get minExtent => 40.0;

  @override
  double get maxExtent => 40.0;

  @override
  Widget build(
    BuildContext context,
    double shrinkOffset,
    bool overlapsContent,
  ) {
    return child;
  }

  @override
  bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
    return false;
  }
}
// 中
  Widget _buildPlayAllHeader() {
    return SliverPersistentHeader(
      pinned: true, //控制头部是否固定在顶部
      delegate: _SliverAppBarDelegate(
        child: GestureDetector(
          onTap: _playAllSongs,
          child: Container(
            color: Colors.white,
            height: 40,
            padding: const EdgeInsets.symmetric(horizontal: 16),
            child: Container(
              decoration: BoxDecoration(
                color: Colors.white.withOpacity(0.8),
                borderRadius: BorderRadius.circular(24),
              ),
              child: Row(
                children: [
                  Container(
                    width: 18,
                    height: 18,
                    decoration: BoxDecoration(
                      color: const Color.fromARGB(255, 233, 60, 47),
                      borderRadius: BorderRadius.circular(16),
                    ),
                    child: Icon(
                      Icons.play_arrow,
                      color: Colors.grey[200],
                      size: 14,
                    ),
                  ),
                  const SizedBox(width: 8),
                  Text(
                    '播放全部(${widget.songCount ?? '共${_playlistSongs.length}首'})',
                    style: const TextStyle(
                      fontSize: 14,
                      fontWeight: FontWeight.w500,
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
CustomScrollView(
            controller: _scrollController,
            slivers: [_buildAppBar(), _buildPlayAllHeader(), _buildSongList()],
          )

防抖机制 Callable class(可调用类):

//防抖机制,用于防止高频时间的触发
class Debouncer {
  final Duration delay;
  Timer? _timer;

  Debouncer({this.delay = const Duration(milliseconds: 500)});

  void call(VoidCallback action) {
    _timer?.cancel();
    _timer = Timer(delay, action);
  }

  void dispose() {
    _timer?.cancel();
  }
}
// 使用
final Debouncer _debouncer = Debouncer();
_debouncer.dispose();
_debouncer(() async {
      if (!_mounted) return;
      final results = await _apiService.getSearchSuggestions(query);
      if (!_mounted) return;

      setState(() {
        suggestions = results;
        showSuggestions = true;
      });
    });
// 这里就相当于_debouncer.call()

path_provider获取外部存储目录:

final directory = await getExternalStorageDirectory(); // 获取外部存储目录
final videoDir = Directory('${directory?.path}/videos');

      if (!await videoDir.exists()) { // 检查目录或文件是否存在的方法
        return [];
      }

      final files = await videoDir.list().toList(); // 异步列出目录中的所有文件和子目录
      return files
          .whereType<File>()  // 只保留 File 类型(排除目录)
          .where((file) => file.path.endsWith('.mp4')) // 只保留 .mp4 文件
          .map((file) => {
                'path': file.path, // 文件完整路径
                'title': file.path.split('/').last.replaceAll('.mp4', ''), 
              // 文件名(去掉扩展名)
              })
          .toList();

获取文件大小:

String _formatFileSize(int size) {
    if (size < 1024) return '$size B';
    if (size < 1024 * 1024) return '${(size / 1024).toStringAsFixed(2)} KB';
    if (size < 1024 * 1024 * 1024) {
      return '${(size / (1024 * 1024)).toStringAsFixed(2)} MB';
    }
    return '${(size / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB';
  }
_formatFileSize(File(video['path']).lengthSync()); // video['path']指文件路径
// lengthSync() 是 Dart 中 File 类的一个同步方法,用于获取文件的字节大小。

伪装成浏览器绕过反爬虫:

import 'dart:io';

class MyHttpOverrides extends HttpOverrides {
  @override
  HttpClient createHttpClient(SecurityContext? context) {
    return super.createHttpClient(context)
      ..userAgent =
          'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0';
  }
}

单例模式:

final apiService = ApiService();
class ApiService {
  static const String baseUrl = 'https://music-api.heheda.top';

  // 单例模式
  static final ApiService _instance = ApiService._internal();

  factory ApiService() => _instance;

  ApiService._internal();
}

忽略原生电池优化:

static const platform = MethodChannel('flutter_music/battery_optimization');

Future<void> _initWakelock() async {
    try {
      // 请求忽略电池优化
      final bool? isIgnoringBatteryOptimizations = await platform.invokeMethod(
        'isIgnoringBatteryOptimizations',
      );
      if (isIgnoringBatteryOptimizations == false) {
        await platform.invokeMethod('requestIgnoreBatteryOptimizations');
      }
    } catch (e) {
      debugPrint('请求电池优化失败: $e');
    }
  }