Atulização do quiz | Porcentagem na base de dados | Telas de Contrato com o usuario
This commit is contained in:
@@ -319,7 +319,10 @@ class _VideoScreenState extends State<VideoScreen> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Video grid
|
||||
// Lista vertical (um vídeo abaixo do outro, rolando para
|
||||
// baixo), mas cada card em si é horizontal — miniatura à
|
||||
// esquerda, título/descrição à direita — em vez da antiga
|
||||
// grelha de 2 colunas com cards verticais.
|
||||
Expanded(
|
||||
child: _filteredVideos.isEmpty
|
||||
? Center(
|
||||
@@ -332,15 +335,10 @@ class _VideoScreenState extends State<VideoScreen> {
|
||||
),
|
||||
),
|
||||
)
|
||||
: GridView.builder(
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
childAspectRatio: 0.85,
|
||||
),
|
||||
: ListView.separated(
|
||||
itemCount: _filteredVideos.length,
|
||||
separatorBuilder: (context, index) =>
|
||||
const SizedBox(height: 12),
|
||||
itemBuilder: (context, index) {
|
||||
return FadeSlideIn(
|
||||
delay: Duration(
|
||||
@@ -546,112 +544,127 @@ class _VideoButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TapBounce(
|
||||
scale: 0.95,
|
||||
scale: 0.97,
|
||||
child: Material(
|
||||
elevation: 10,
|
||||
shadowColor: Colors.black.withValues(alpha: 0.18),
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
color: Colors.white,
|
||||
child: InkWell(
|
||||
elevation: 8,
|
||||
shadowColor: Colors.black.withValues(alpha: 0.14),
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
onTap: () => _showVideoPlayer(context, video),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: 16 / 9,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
ColoredBox(
|
||||
color: const Color(0xFFFFE6F1),
|
||||
child: VideoThumbnail(video: video, borderRadius: 0),
|
||||
),
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Colors.transparent,
|
||||
Colors.black.withValues(alpha: 0.32),
|
||||
],
|
||||
stops: const [0.55, 1.0],
|
||||
),
|
||||
color: Colors.white,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
onTap: () => _showVideoPlayer(context, video),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 130,
|
||||
child: AspectRatio(
|
||||
aspectRatio: 16 / 9,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
ColoredBox(
|
||||
color: const Color(0xFFFFE6F1),
|
||||
child: VideoThumbnail(video: video, borderRadius: 0),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.92),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.play_arrow_rounded,
|
||||
color: VideoScreen._accentPink,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
left: 6,
|
||||
bottom: 6,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withValues(alpha: 0.5),
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
),
|
||||
child: Text(
|
||||
'EP. ${video.id}',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 9.5,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Colors.transparent,
|
||||
Colors.black.withValues(alpha: 0.32),
|
||||
],
|
||||
stops: const [0.55, 1.0],
|
||||
),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.92),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.play_arrow_rounded,
|
||||
color: VideoScreen._accentPink,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
left: 6,
|
||||
bottom: 6,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withValues(alpha: 0.5),
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
),
|
||||
child: Text(
|
||||
'EP. ${video.id}',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 9.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
video.title,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 15,
|
||||
color: VideoScreen._teal,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
video.description,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black.withValues(alpha: 0.6),
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
video.title,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 14,
|
||||
color: VideoScreen._teal,
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
Icons.chevron_right_rounded,
|
||||
color: Colors.black.withValues(alpha: 0.3),
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
video.description,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black.withValues(alpha: 0.6),
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -733,24 +746,73 @@ class _YoutubePlayerPageState extends State<_YoutubePlayerPage>
|
||||
if (!didPop) _controller.toggleFullScreenMode();
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
backgroundColor: value.isFullScreen
|
||||
? Colors.black
|
||||
: const Color(0xFFFAFAF7),
|
||||
appBar: value.isFullScreen
|
||||
? null
|
||||
: AppBar(
|
||||
backgroundColor: VideoScreen._teal,
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
title: Text(
|
||||
widget.video.title,
|
||||
style: const TextStyle(fontWeight: FontWeight.w900),
|
||||
: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(kToolbarHeight),
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: kAppBarGradient,
|
||||
),
|
||||
child: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
foregroundColor: Colors.white,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
title: Text(
|
||||
widget.video.title,
|
||||
style: const TextStyle(fontWeight: FontWeight.w900),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Em vez de centrar o vídeo num quadro 16:9 no meio de um ecrã
|
||||
// preto (o que sobrava como barras pretas em cima/baixo no
|
||||
// modo retrato), o vídeo fica encostado ao topo, a preencher a
|
||||
// largura toda, com o título e a descrição do episódio logo
|
||||
// abaixo — sem nenhum espaço preto sobrando. O modo tela cheia
|
||||
// (paisagem) continua a recortar o vídeo para preencher tudo.
|
||||
body: value.isFullScreen
|
||||
? _CoverYoutubePlayer(controller: _controller)
|
||||
: Center(
|
||||
child: AspectRatio(
|
||||
aspectRatio: 16 / 9,
|
||||
child: YoutubePlayer(controller: _controller),
|
||||
: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: 16 / 9,
|
||||
child: YoutubePlayer(controller: _controller),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
widget.video.title,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 19,
|
||||
color: VideoScreen._teal,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
widget.video.description,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black.withValues(alpha: 0.65),
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user