mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-04-20 11:08:03 +08:00
133 lines
3.5 KiB
Dart
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();
|
|
}
|
|
}
|