混入类保持页面状态:
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(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
child: Container(color: Colors.black.withValues(alpha: 0.1)),
),
),
],
),

滑动后固定到头部组件:
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;
});
});
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>()
.where((file) => file.path.endsWith('.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());
伪装成浏览器绕过反爬虫:
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');
}
}