import 'package:flutter/material.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:pyramid/core/theme.dart'; class ScreenSharePicker extends StatefulWidget { const ScreenSharePicker({super.key}); static Future show(BuildContext context) async { return showDialog( context: context, builder: (context) => const ScreenSharePicker(), ); } @override State createState() => _ScreenSharePickerState(); } class _ScreenSharePickerState extends State with SingleTickerProviderStateMixin { late TabController _tabController; List _sources = []; bool _loading = true; @override void initState() { super.initState(); _tabController = TabController(length: 2, vsync: this); _loadSources(); } Future _loadSources() async { try { // FluffyChat Fix: Get both types at once to avoid native crashes on Windows final sources = await desktopCapturer.getSources(types: [ SourceType.Screen, SourceType.Window, ]); if (mounted) { setState(() { _sources = sources; _loading = false; }); } } catch (e) { debugPrint('[Picker] Error loading sources: $e'); if (mounted) setState(() => _loading = false); } } @override void dispose() { _tabController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final pt = PyramidTheme.of(context); final screens = _sources.where((s) => s.type == SourceType.Screen).toList(); final windows = _sources.where((s) => s.type == SourceType.Window).toList(); final screenSize = MediaQuery.sizeOf(context); return Center( child: Container( // Auf kleinen Bildschirmen (Mobile, Quer- wie Hochformat) einpassen. width: (screenSize.width - 32).clamp(280.0, 600.0), height: (screenSize.height - 48).clamp(320.0, 520.0), decoration: BoxDecoration( color: pt.bg1, borderRadius: BorderRadius.circular(pt.rXl), border: Border.all(color: pt.border), boxShadow: [BoxShadow(color: Colors.black54, blurRadius: 40)], ), child: Column( children: [ // Header Padding( padding: const EdgeInsets.fromLTRB(24, 20, 16, 8), child: Row( children: [ Text('Share your screen', style: TextStyle(color: pt.fg, fontSize: 20, fontWeight: FontWeight.w700)), const Spacer(), IconButton( icon: Icon(Icons.close_rounded, color: pt.fgDim), onPressed: () => Navigator.pop(context), ), ], ), ), // Tabs TabBar( controller: _tabController, indicatorColor: pt.accent, labelColor: pt.accent, unselectedLabelColor: pt.fgMuted, dividerColor: pt.border, tabs: const [ Tab(text: 'Screens'), Tab(text: 'Windows'), ], ), // Content Expanded( child: _loading ? Center(child: CircularProgressIndicator(color: pt.accent)) : TabBarView( controller: _tabController, children: [ _SourceGrid(sources: screens, pt: pt), _SourceGrid(sources: windows, pt: pt), ], ), ), // Footer Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: pt.bg2, borderRadius: BorderRadius.vertical(bottom: Radius.circular(pt.rXl)), border: Border(top: BorderSide(color: pt.border)), ), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('Cancel', style: TextStyle(color: pt.fgMuted)), ), ], ), ), ], ), ), ); } } class _SourceGrid extends StatelessWidget { final List sources; final PyramidTheme pt; const _SourceGrid({required this.sources, required this.pt}); @override Widget build(BuildContext context) { if (sources.isEmpty) { return Center(child: Text('No sources found', style: TextStyle(color: pt.fgDim))); } return GridView.builder( padding: const EdgeInsets.all(16), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 12, mainAxisSpacing: 16, childAspectRatio: 1.4, ), itemCount: sources.length, itemBuilder: (context, i) { final s = sources[i]; return _SourceTile(source: s, pt: pt); }, ); } } class _SourceTile extends StatefulWidget { final DesktopCapturerSource source; final PyramidTheme pt; const _SourceTile({required this.source, required this.pt}); @override State<_SourceTile> createState() => _SourceTileState(); } class _SourceTileState extends State<_SourceTile> { bool _hovered = false; @override Widget build(BuildContext context) { final pt = widget.pt; return MouseRegion( onEnter: (_) => setState(() => _hovered = true), onExit: (_) => setState(() => _hovered = false), cursor: SystemMouseCursors.click, child: GestureDetector( onTap: () => Navigator.pop(context, widget.source), child: Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: pt.bg3, borderRadius: BorderRadius.circular(pt.rBase), border: Border.all( color: _hovered ? pt.accent : pt.border, width: _hovered ? 2 : 1, ), ), clipBehavior: Clip.antiAlias, child: widget.source.thumbnail != null ? Image.memory( widget.source.thumbnail!, fit: BoxFit.cover, width: double.infinity, ) : Center(child: Icon(Icons.monitor_rounded, color: pt.fgDim, size: 32)), ), ), const SizedBox(height: 6), Text( widget.source.name, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( color: _hovered ? pt.fg : pt.fgMuted, fontSize: 12, fontWeight: _hovered ? FontWeight.w600 : FontWeight.w400, ), ), ], ), ), ); } }