spotube/lib/pages/connect/connect.dart
Kingkor Roy Tirtho 68374efd3e
feat: LAN connect a.k.a control remote Spotube playback and local output device selection (#1355)
* feat: add connect server support

* feat: add ability discover and connect to same network Spotube(s) and sync queue

* feat(connect): add player controls, shuffle, loop, progress bar and queue support

* feat: make control page adaptive

* feat: add volume control support

* cd: upgrade macos runner version

* chore: upgrade inappwebview version to 6

* feat: customized devices button

* feat: add user icon next to devices button

* feat: add play in remote device support

* feat: show alert when new client connects

* fix: ignore the device itself from broadcast list

* fix: volume control not working

* feat: add ability to select current device's output speaker
2024-04-04 22:22:00 +06:00

94 lines
3.5 KiB
Dart

import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/connect/local_devices.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/connect/clients.dart';
import 'package:spotube/utils/service_utils.dart';
class ConnectPage extends HookConsumerWidget {
const ConnectPage({super.key});
@override
Widget build(BuildContext context, ref) {
final ThemeData(:colorScheme, :textTheme) = Theme.of(context);
final connectClients = ref.watch(connectClientsProvider);
final connectClientsNotifier = ref.read(connectClientsProvider.notifier);
final discoveredDevices = connectClients.asData?.value.services;
return Scaffold(
appBar: PageWindowTitleBar(
automaticallyImplyLeading: true,
title: Text(context.l10n.devices),
),
body: ListTileTheme(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
selectedTileColor: colorScheme.secondary.withOpacity(0.1),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: CustomScrollView(
slivers: [
SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
sliver: SliverToBoxAdapter(
child: Text(
context.l10n.remote,
style: textTheme.titleMedium,
),
),
),
const SliverGap(10),
SliverList.separated(
itemCount: discoveredDevices?.length ?? 0,
separatorBuilder: (context, index) => const Gap(10),
itemBuilder: (context, index) {
final device = discoveredDevices![index];
final selected =
connectClients.asData?.value.resolvedService?.name ==
device.name;
return Card(
child: ListTile(
leading: const Icon(SpotubeIcons.monitor),
title: Text(device.name),
subtitle: selected
? Text(
"${connectClients.asData?.value.resolvedService?.host}"
":${connectClients.asData?.value.resolvedService?.port}",
)
: null,
selected: selected,
onTap: () {
if (selected) {
ServiceUtils.push(
context,
"/connect/control",
);
} else {
connectClientsNotifier.resolveService(device);
}
},
trailing: selected
? IconButton(
icon: const Icon(SpotubeIcons.power),
onPressed: () =>
connectClientsNotifier.clearResolvedService(),
)
: null,
),
);
},
),
const ConnectPageLocalDevices(),
],
),
),
),
);
}
}