Skip to content

Commit

Permalink
More broadcasts improvements
Browse files Browse the repository at this point in the history
- better list layout: larger cards
- avoid unnecessary requests when switching tabs
- better nav bar on iOS
  • Loading branch information
veloce committed Dec 5, 2024
1 parent e493931 commit cf3ee30
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 113 deletions.
158 changes: 83 additions & 75 deletions lib/src/view/broadcast/broadcast_list_screen.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:async';

import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
Expand All @@ -21,6 +22,10 @@ import 'package:lichess_mobile/src/widgets/shimmer.dart';
final _dateFormatter = DateFormat.MMMd().add_Hm();
final _dateFormatterWithYear = DateFormat.yMMMd().add_Hm();

const kDefaultBroadcastImage = AssetImage('assets/images/broadcast_image.png');
const kBroadcastGridItemBorderRadius = BorderRadius.all(Radius.circular(16.0));
const kBroadcastGridItemContentPadding = EdgeInsets.symmetric(horizontal: 16.0);

/// A screen that displays a paginated list of broadcasts.
class BroadcastListScreen extends StatelessWidget {
const BroadcastListScreen({super.key});
Expand All @@ -29,7 +34,12 @@ class BroadcastListScreen extends StatelessWidget {
Widget build(BuildContext context) {
return PlatformScaffold(
appBar: PlatformAppBar(
title: Text(context.l10n.broadcastLiveBroadcasts),
title: AutoSizeText(
context.l10n.broadcastLiveBroadcasts,
minFontSize: 14.0,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
body: const _Body(),
);
Expand Down Expand Up @@ -100,23 +110,26 @@ class _BodyState extends ConsumerState<_Body> {
}

final isTablet = isTabletOrLarger(context);
final itemsByRow = isTablet ? 6 : 2;
final loadingItems = isTablet ? 36 : 12;
final itemsByRow = isTablet ? 2 : 1;
const loadingItems = 12;
final itemsCount = broadcasts.requireValue.past.length +
(broadcasts.isLoading ? loadingItems : 0);

final gridDelegate = SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: itemsByRow,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 1.3,
);

return SafeArea(
child: CustomScrollView(
controller: _scrollController,
slivers: [
SliverPadding(
padding: Styles.bodySectionPadding,
sliver: SliverGrid.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: itemsByRow,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
gridDelegate: gridDelegate,
itemBuilder: (context, index) => BroadcastGridItem(
broadcast: broadcasts.value!.active[index],
worker: _worker!,
Expand All @@ -136,11 +149,7 @@ class _BodyState extends ConsumerState<_Body> {
SliverPadding(
padding: Styles.bodySectionPadding,
sliver: SliverGrid.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: itemsByRow,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
gridDelegate: gridDelegate,
itemBuilder: (context, index) => BroadcastGridItem(
worker: _worker!,
broadcast: broadcasts.value!.upcoming[index],
Expand All @@ -160,11 +169,7 @@ class _BodyState extends ConsumerState<_Body> {
SliverPadding(
padding: Styles.bodySectionPadding,
sliver: SliverGrid.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: itemsByRow,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
gridDelegate: gridDelegate,
itemBuilder: (context, index) =>
(broadcasts.isLoading && index >= itemsCount - loadingItems)
? Shimmer(
Expand Down Expand Up @@ -237,8 +242,6 @@ final Map<ImageProvider, _CardColors> _colorsCache = {
),
};

const kDefaultBroadcastImage = AssetImage('assets/images/broadcast_image.png');

class _BroadcastGridItemState extends State<BroadcastGridItem> {
_CardColors? _cardColors;

Expand Down Expand Up @@ -294,7 +297,7 @@ class _BroadcastGridItemState extends State<BroadcastGridItem> {
textShade(context, 0.7);

return AdaptiveInkWell(
borderRadius: BorderRadius.circular(20),
borderRadius: kBroadcastGridItemBorderRadius,
onTap: () {
pushPlatformRoute(
context,
Expand All @@ -307,7 +310,7 @@ class _BroadcastGridItemState extends State<BroadcastGridItem> {
child: Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
borderRadius: kBroadcastGridItemBorderRadius,
color: backgroundColor,
boxShadow: Theme.of(context).platform == TargetPlatform.iOS
? null
Expand All @@ -320,7 +323,7 @@ class _BroadcastGridItemState extends State<BroadcastGridItem> {
width: 3,
)
: null,
borderRadius: BorderRadius.circular(20),
borderRadius: kBroadcastGridItemBorderRadius,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
Expand Down Expand Up @@ -350,61 +353,66 @@ class _BroadcastGridItemState extends State<BroadcastGridItem> {
),
),
),
if (widget.broadcast.round.startsAt != null ||
widget.broadcast.isLive)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
_formatDate(widget.broadcast.round.startsAt!),
style: TextStyle(
fontSize: 11,
color: subTitleColor,
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.broadcast.round.startsAt != null ||
widget.broadcast.isLive)
Padding(
padding: kBroadcastGridItemContentPadding,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
widget.broadcast.round.name,
style: TextStyle(
fontSize: 12,
color: subTitleColor,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
const SizedBox(width: 4.0),
if (widget.broadcast.isLive)
const Text(
'LIVE',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
color: Colors.red,
),
overflow: TextOverflow.ellipsis,
)
else
Text(
_formatDate(widget.broadcast.round.startsAt!),
style: TextStyle(
fontSize: 12,
color: subTitleColor,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
],
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
if (widget.broadcast.isLive) ...[
const SizedBox(width: 4.0),
const Text(
'LIVE',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.red,
),
overflow: TextOverflow.ellipsis,
const SizedBox(height: 4.0),
Padding(
padding: kBroadcastGridItemContentPadding,
child: Text(
widget.broadcast.title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: titleColor,
fontWeight: FontWeight.bold,
fontSize: 16,
),
],
],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
widget.broadcast.round.name,
style: TextStyle(
fontSize: 11,
color: subTitleColor,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
const SizedBox(height: 4.0),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
widget.broadcast.title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: titleColor,
fontSize: 13,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
],
Expand Down
7 changes: 0 additions & 7 deletions lib/src/view/broadcast/broadcast_overview_tab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,6 @@ class BroadcastOverviewTab extends ConsumerWidget {
child: ListView(
padding: Styles.bodyPadding,
children: [
if (Theme.of(context).platform == TargetPlatform.iOS) ...[
Text(
broadcast.title,
style: Styles.title,
),
const SizedBox(height: 16.0),
],
if (tournament.data.imageUrl != null) ...[
Image.network(tournament.data.imageUrl!),
const SizedBox(height: 16.0),
Expand Down
101 changes: 70 additions & 31 deletions lib/src/view/broadcast/broadcast_round_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import 'package:lichess_mobile/src/model/broadcast/broadcast.dart';
import 'package:lichess_mobile/src/model/broadcast/broadcast_providers.dart';
import 'package:lichess_mobile/src/model/broadcast/broadcast_round_controller.dart';
import 'package:lichess_mobile/src/model/common/id.dart';
import 'package:lichess_mobile/src/styles/styles.dart';
import 'package:lichess_mobile/src/utils/l10n_context.dart';
Expand Down Expand Up @@ -74,43 +75,81 @@ class _BroadcastRoundScreenState extends ConsumerState<BroadcastRoundScreen>

switch (tournament) {
case AsyncData(:final value):
// Eagerly initalize the round controller so it stays alive when switching tabs
ref.watch(
broadcastRoundControllerProvider(
_selectedRoundId ?? value.defaultRoundId,
),
);
if (Theme.of(context).platform == TargetPlatform.iOS) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: CupertinoSlidingSegmentedControl<_ViewMode>(
groupValue: _selectedSegment,
children: {
_ViewMode.overview: Text(context.l10n.broadcastOverview),
_ViewMode.boards: Text(context.l10n.broadcastBoards),
},
onValueChanged: (_ViewMode? view) {
if (view != null) {
setState(() {
_selectedSegment = view;
});
}
},
middle: AutoSizeText(
widget.broadcast.title,
minFontSize: 14.0,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
automaticBackgroundVisibility: false,
border: null,
),
child: Column(
children: [
Expanded(
child: _selectedSegment == _ViewMode.overview
? BroadcastOverviewTab(
broadcast: widget.broadcast,
tournamentId: _selectedTournamentId,
)
: BroadcastBoardsTab(
_selectedRoundId ?? value.defaultRoundId,
child: SafeArea(
bottom: false,
child: Column(
children: [
Container(
height: kMinInteractiveDimensionCupertino,
width: double.infinity,
decoration: BoxDecoration(
color: Styles.cupertinoAppBarColor.resolveFrom(context),
border: const Border(
bottom: BorderSide(
color: Color(0x4D000000),
width: 0.0,
),
),
_BottomBar(
tournament: value,
roundId: _selectedRoundId ?? value.defaultRoundId,
setTournamentId: setTournamentId,
setRoundId: setRoundId,
),
],
),
),
child: Padding(
padding: const EdgeInsets.only(
left: 16.0,
right: 16.0,
bottom: 8.0,
),
child: CupertinoSlidingSegmentedControl<_ViewMode>(
groupValue: _selectedSegment,
children: {
_ViewMode.overview:
Text(context.l10n.broadcastOverview),
_ViewMode.boards: Text(context.l10n.broadcastBoards),
},
onValueChanged: (_ViewMode? view) {
if (view != null) {
setState(() {
_selectedSegment = view;
});
}
},
),
),
),
Expanded(
child: _selectedSegment == _ViewMode.overview
? BroadcastOverviewTab(
broadcast: widget.broadcast,
tournamentId: _selectedTournamentId,
)
: BroadcastBoardsTab(
_selectedRoundId ?? value.defaultRoundId,
),
),
_BottomBar(
tournament: value,
roundId: _selectedRoundId ?? value.defaultRoundId,
setTournamentId: setTournamentId,
setRoundId: setRoundId,
),
],
),
),
);
} else {
Expand Down

0 comments on commit cf3ee30

Please sign in to comment.