import 'package:flutter/material.dart';
import 'package:atomic_x_core/atomicxcore.dart';
class LiveListPage extends StatefulWidget {
const LiveListPage({super.key});
@override
State<LiveListPage> createState() => _LiveListPageState();
}
class _LiveListPageState extends State<LiveListPage> {
final _liveListStore = LiveListStore.shared;
final ScrollController _scrollController = ScrollController();
bool _isLoading = false;
@override
void initState() {
super.initState();
_liveListStore.liveState.liveList.addListener(_onLiveListChanged);
_scrollController.addListener(_onScroll);
_fetchLiveList();
}
void _onLiveListChanged() {
setState(() {});
}
void _onScroll() {
if (_scrollController.position.pixels >=
_scrollController.position.maxScrollExtent - 200) {
_loadMore();
}
}
Future<void> _fetchLiveList() async {
if (_isLoading) return;
setState(() => _isLoading = true);
final result = await _liveListStore.fetchLiveList(cursor: '', count: 20);
if (!result.isSuccess) {
debugPrint('List live stream failed: ${result.message}');
}
setState(() => _isLoading = false);
}
Future<void> _loadMore() async {
if (_isLoading) return;
final cursor = _liveListStore.liveState.liveListCursor.value;
if (cursor.isEmpty) return;
setState(() => _isLoading = true);
await _liveListStore.fetchLiveList(cursor: cursor, count: 20);
setState(() => _isLoading = false);
}
Future<void> _refresh() async {
_liveListStore.reset();
await _fetchLiveList();
}
void _onLiveCardTap(LiveInfo liveInfo) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => YourAudiencePage(liveID: liveInfo.liveID),
),
);
}
@override
void dispose() {
_liveListStore.liveState.liveList.removeListener(_onLiveListChanged);
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final liveList = _liveListStore.liveState.liveList.value;
return Scaffold(
appBar: AppBar(title: const Text('Live Stream List')),
body: RefreshIndicator(
onRefresh: _refresh,
child: liveList.isEmpty
? Center(
child: _isLoading
? const CircularProgressIndicator()
const Text('No live stream')
)
: GridView.builder(
controller: _scrollController,
padding: const EdgeInsets.all(8),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.75,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: liveList.length + (_isLoading ? 1 : 0),
itemBuilder: (context, index) {
if (index >= liveList.length) {
return const Center(child: CircularProgressIndicator());
}
return _buildLiveCard(liveList[index]);
},
),
),
);
}
Widget _buildLiveCard(LiveInfo liveInfo) {
return GestureDetector(
onTap: () => _onLiveCardTap(liveInfo),
child: Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Cover image
Expanded(
child: Stack(
fit: StackFit.expand,
children: [
Image.network(
liveInfo.coverURL,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(color: Colors.grey[300]);
},
),
Positioned(
top: 8,
left: 8,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2,
),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(4),
),
child: const Text(
Live streaming
style: TextStyle(color: Colors.white, fontSize: 10),
),
),
),
viewers
Positioned(
bottom: 8,
right: 8,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2,
),
decoration: BoxDecoration(
color: Colors.black54,
borderRadius: BorderRadius.circular(4),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.visibility,
color: Colors.white,
size: 12,
),
const SizedBox(width: 4),
Text(
'${liveInfo.totalViewerCount}',
style: const TextStyle(
color: Colors.white,
fontSize: 10,
),
),
],
),
),
),
],
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
liveInfo.liveName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 4),
Row(
children: [
CircleAvatar(
radius: 10,
backgroundImage: NetworkImage(
liveInfo.liveOwner.avatarURL,
),
),
const SizedBox(width: 4),
Expanded(
child: Text(
liveInfo.liveOwner.userName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
),
],
),
],
),
),
],
),
),
);
}
}
class YourAudiencePage extends StatelessWidget {
final String liveID;
const YourAudiencePage({super.key, required this.liveID});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Live Streaming Room')),
body: Center(child: Text('Live Streaming Room: $liveID')),
);
}
}