Files
PiliPlus/lib/pages/dlna/view.dart
2026-04-14 13:46:47 +08:00

133 lines
3.5 KiB
Dart

import 'dart:async';
import 'package:PiliPlus/common/widgets/flutter/scroll_view/scroll_view.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart';
import 'package:dlna_dart/dlna.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class DLNAPage extends StatefulWidget {
const DLNAPage({super.key});
@override
State<DLNAPage> createState() => _DLNAPageState();
}
class _DLNAPageState extends State<DLNAPage> {
final _searcher = DLNAManager();
final Map<String, DLNADevice> _deviceList = {};
late final _url = Get.parameters['url']!;
late final _title = Get.parameters['title'];
Timer? _timer;
bool _isSearching = false;
DLNADevice? _lastDevice;
String? _lastDeviceKey;
@override
void initState() {
super.initState();
_onSearch(isInit: true);
}
Future<void> _onSearch({bool isInit = false}) async {
if (_isSearching) return;
_isSearching = true;
if (!isInit && mounted) {
_lastDevice = null;
_deviceList.clear();
setState(() {});
}
final deviceManager = await _searcher.start();
if (!mounted) {
return;
}
_timer = Timer(const Duration(seconds: 20), _searcher.stop);
await for (final deviceList in deviceManager.devices.stream) {
if (mounted) {
_deviceList.addAll(deviceList);
setState(() {});
}
}
if (mounted) {
setState(() {
_isSearching = false;
});
}
}
@override
void dispose() {
_timer?.cancel();
_timer = null;
_searcher.stop();
_lastDevice = null;
_lastDeviceKey = null;
super.dispose();
}
@override
Widget build(BuildContext context) {
final colorScheme = ColorScheme.of(context);
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: const Text('投屏'),
actions: [
IconButton(
tooltip: '搜索',
onPressed: _onSearch,
icon: const Icon(Icons.refresh),
),
const SizedBox(width: 6),
],
),
body: customScrollView(
slivers: [
if (_isSearching) linearLoading,
ViewSliverSafeArea(sliver: _buildBody(colorScheme)),
],
),
);
}
Widget _buildBody(ColorScheme colorScheme) {
if (!_isSearching && _deviceList.isEmpty) {
return HttpError(
errMsg: '没有设备',
onReload: _onSearch,
);
}
if (_deviceList.isNotEmpty) {
final keys = _deviceList.keys.toList();
return SliverList.builder(
itemCount: keys.length,
itemBuilder: (context, index) {
final key = keys[index];
final device = _deviceList[key]!;
final isCurr = key == _lastDeviceKey;
return ListTile(
title: Text(
device.info.friendlyName,
style: isCurr ? TextStyle(color: colorScheme.primary) : null,
),
subtitle: Text(key),
onTap: () async {
if (isCurr) return;
_lastDevice?.pause();
_lastDevice = device;
_lastDeviceKey = key;
setState(() {});
await device.setUrl(_url, title: _title ?? '');
await device.play();
},
);
},
);
}
return const SliverToBoxAdapter();
}
}