UI: Add ChannelTile with logos, search/filter, and design improvements

This commit is contained in:
2026-01-11 20:16:01 +09:00
parent c3a95684f3
commit e3bca82285
9 changed files with 265 additions and 36 deletions

View File

@@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import '../models/stream_entry.dart';
class ChannelTile extends StatelessWidget {
final StreamEntry entry;
final bool selected;
final VoidCallback? onTap;
final VoidCallback? onOpen;
const ChannelTile(
{super.key,
required this.entry,
this.selected = false,
this.onTap,
this.onOpen});
@override
Widget build(BuildContext context) {
final border = selected
? Border.all(color: Theme.of(context).colorScheme.primary, width: 2)
: null;
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), side: BorderSide.none),
color: selected ? Colors.grey[850] : Colors.transparent,
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
child: InkWell(
borderRadius: BorderRadius.circular(10),
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
border: border, borderRadius: BorderRadius.circular(10)),
child: Row(children: [
ClipRRect(
borderRadius: BorderRadius.circular(6),
child: SizedBox(
width: 56,
height: 40,
child: entry.logo != null
? CachedNetworkImage(
imageUrl: entry.logo!,
fit: BoxFit.cover,
placeholder: (c, s) =>
Container(color: Colors.grey[800]),
errorWidget: (c, s, e) => Container(
color: Colors.grey[800],
child: const Icon(Icons.tv, color: Colors.white30)),
)
: Container(
color: Colors.grey[800],
child: const Icon(Icons.tv, color: Colors.white30)),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(entry.title,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Colors.white)),
const SizedBox(height: 4),
Text(entry.url,
style: TextStyle(fontSize: 11, color: Colors.white70),
overflow: TextOverflow.ellipsis,
maxLines: 1),
])),
IconButton(
icon: const Icon(Icons.open_in_new, color: Colors.white70),
onPressed: onOpen,
tooltip: 'Open full screen')
]),
),
),
);
}
}