Skip to content

Commit

Permalink
[Presentation] Added a portrait / landscape layout to the game codes …
Browse files Browse the repository at this point in the history
…page
  • Loading branch information
Wolfteam committed Jul 13, 2021
1 parent 04a2233 commit bf112dd
Showing 1 changed file with 232 additions and 64 deletions.
296 changes: 232 additions & 64 deletions lib/presentation/game_codes/game_codes_page.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:genshindb/application/bloc.dart';
import 'package:genshindb/domain/models/models.dart';
import 'package:genshindb/generated/l10n.dart';
import 'package:genshindb/presentation/shared/bullet_list.dart';
import 'package:genshindb/presentation/shared/item_description_detail.dart';
Expand All @@ -23,14 +24,6 @@ class GameCodesPage extends StatefulWidget {
}

class _GameCodesPageState extends State<GameCodesPage> with SingleTickerProviderStateMixin, AppFabMixin {
late RefreshController _refreshController;

@override
void initState() {
super.initState();
_refreshController = RefreshController();
}

@override
Widget build(BuildContext context) {
final s = S.of(context);
Expand All @@ -42,6 +35,12 @@ class _GameCodesPageState extends State<GameCodesPage> with SingleTickerProvider
icon: const Icon(Icons.open_in_new),
onPressed: () => _launchUrl('https://genshin.mihoyo.com/en/gift'),
),
BlocBuilder<GameCodesBloc, GameCodesState>(
builder: (ctx, state) => IconButton(
icon: const Icon(Icons.refresh),
onPressed: state.isBusy ? null : () => context.read<GameCodesBloc>().add(const GameCodesEvent.refresh()),
),
),
IconButton(
icon: const Icon(Icons.info),
onPressed: () => _showInfoDialog(context),
Expand All @@ -52,72 +51,21 @@ class _GameCodesPageState extends State<GameCodesPage> with SingleTickerProvider
body: SafeArea(
child: BlocConsumer<GameCodesBloc, GameCodesState>(
listener: (ctx, state) {
if (!state.isBusy) {
_refreshController.refreshCompleted();
}

if (state.isInternetAvailable == false) {
ToastUtils.showWarningToast(ToastUtils.of(context), s.noInternetConnection);
}
},
builder: (ctx, state) => SmartRefresher(
header: const MaterialClassicHeader(),
controller: _refreshController,
onRefresh: () {
context.read<GameCodesBloc>().add(const GameCodesEvent.refresh());
},
child: state.expiredGameCodes.isEmpty && state.workingGameCodes.isEmpty
? state.isBusy
? const Loading(useScaffold: false)
: NothingFoundColumn(msg: '${s.noGameCodesHaveBeenLoaded}\n${s.pullToRefreshItems}', icon: Icons.refresh)
: CustomScrollView(
controller: scrollController,
slivers: [
if (state.workingGameCodes.isNotEmpty)
SliverToBoxAdapter(
child: Container(
margin: const EdgeInsets.only(top: 10),
child: ItemDescriptionDetail(
title: s.workingCodes,
textColor: Theme.of(context).accentColor,
body: Container(),
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, index) => GameCodeListItem(item: state.workingGameCodes[index]),
childCount: state.workingGameCodes.length,
),
),
if (state.expiredGameCodes.isNotEmpty)
SliverToBoxAdapter(
child: ItemDescriptionDetail(
title: s.expiredCodes,
textColor: Theme.of(context).accentColor,
body: Container(),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, index) => GameCodeListItem(item: state.expiredGameCodes[index]),
childCount: state.expiredGameCodes.length,
),
),
],
),
builder: (ctx, state) => _Layout(
working: state.workingGameCodes,
expired: state.expiredGameCodes,
isBusy: state.isBusy,
scrollController: scrollController,
),
),
),
);
}

@override
void dispose() {
_refreshController.dispose();
super.dispose();
}

Future<void> _launchUrl(String url) async {
if (await canLaunch(url)) {
await launch(url);
Expand Down Expand Up @@ -147,3 +95,223 @@ class _GameCodesPageState extends State<GameCodesPage> with SingleTickerProvider
);
}
}

class _Layout extends StatelessWidget {
final List<GameCodeModel> working;
final List<GameCodeModel> expired;
final bool isBusy;
final ScrollController scrollController;

const _Layout({
Key? key,
required this.working,
required this.expired,
required this.isBusy,
required this.scrollController,
}) : super(key: key);

@override
Widget build(BuildContext context) {
final s = S.of(context);
final isPortrait = MediaQuery.of(context).orientation == Orientation.portrait;
return working.isEmpty && expired.isEmpty
? isBusy
? const Loading(useScaffold: false)
: NothingFoundColumn(msg: '${s.noGameCodesHaveBeenLoaded}\n${s.pullToRefreshItems}', icon: Icons.refresh)
: isPortrait
? _PortraitLayout(working: working, expired: expired, scrollController: scrollController)
: _LandScapeLayout(working: working, expired: expired, scrollController: scrollController);
}
}

class _PortraitLayout extends StatefulWidget {
final List<GameCodeModel> working;
final List<GameCodeModel> expired;
final ScrollController scrollController;

const _PortraitLayout({
Key? key,
required this.working,
required this.expired,
required this.scrollController,
}) : super(key: key);

@override
__PortraitLayoutState createState() => __PortraitLayoutState();
}

class __PortraitLayoutState extends State<_PortraitLayout> {
late RefreshController _refreshController;

@override
void initState() {
super.initState();
_refreshController = RefreshController();
}

@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocListener<GameCodesBloc, GameCodesState>(
listener: (ctx, state) {
if (!state.isBusy) {
_refreshController.refreshCompleted();
}
},
child: SmartRefresher(
header: const MaterialClassicHeader(),
controller: _refreshController,
onRefresh: () => context.read<GameCodesBloc>().add(const GameCodesEvent.refresh()),
child: CustomScrollView(
controller: widget.scrollController,
slivers: [
if (widget.working.isNotEmpty)
SliverToBoxAdapter(
child: Container(
margin: const EdgeInsets.only(top: 10),
child: ItemDescriptionDetail(
title: s.workingCodes,
textColor: Theme.of(context).accentColor,
body: Container(),
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, index) => GameCodeListItem(item: widget.working[index]),
childCount: widget.working.length,
),
),
if (widget.expired.isNotEmpty)
SliverToBoxAdapter(
child: ItemDescriptionDetail(
title: s.expiredCodes,
textColor: Theme.of(context).accentColor,
body: Container(),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, index) => GameCodeListItem(item: widget.expired[index]),
childCount: widget.expired.length,
),
),
],
),
),
);
}

@override
void dispose() {
_refreshController.dispose();
super.dispose();
}
}

class _LandScapeLayout extends StatefulWidget {
final List<GameCodeModel> working;
final List<GameCodeModel> expired;
final ScrollController scrollController;

const _LandScapeLayout({
Key? key,
required this.working,
required this.expired,
required this.scrollController,
}) : super(key: key);

@override
__LandScapeLayoutState createState() => __LandScapeLayoutState();
}

class __LandScapeLayoutState extends State<_LandScapeLayout> {
late RefreshController _leftRefreshController;
late RefreshController _rightRefreshController;

@override
void initState() {
super.initState();
_leftRefreshController = RefreshController();
_rightRefreshController = RefreshController();
}

@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocListener<GameCodesBloc, GameCodesState>(
listener: (ctx, state) {
if (!state.isBusy) {
_leftRefreshController.refreshCompleted();
_rightRefreshController.refreshCompleted();
}
},
child: Row(
children: [
Expanded(
child: SmartRefresher(
header: const MaterialClassicHeader(),
controller: _leftRefreshController,
onRefresh: () => context.read<GameCodesBloc>().add(const GameCodesEvent.refresh()),
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Container(
margin: const EdgeInsets.only(top: 10),
child: ItemDescriptionDetail(
title: s.workingCodes,
textColor: Theme.of(context).accentColor,
body: Container(),
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, index) => GameCodeListItem(item: widget.working[index]),
childCount: widget.working.length,
),
),
],
),
),
),
Expanded(
child: SmartRefresher(
header: const MaterialClassicHeader(),
controller: _rightRefreshController,
onRefresh: () => context.read<GameCodesBloc>().add(const GameCodesEvent.refresh()),
child: CustomScrollView(
controller: widget.scrollController,
slivers: [
SliverToBoxAdapter(
child: Container(
margin: const EdgeInsets.only(top: 10),
child: ItemDescriptionDetail(
title: s.expiredCodes,
textColor: Theme.of(context).accentColor,
body: Container(),
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, index) => GameCodeListItem(item: widget.expired[index]),
childCount: widget.expired.length,
),
),
],
),
),
),
],
),
);
}

@override
void dispose() {
_leftRefreshController.dispose();
_rightRefreshController.dispose();
super.dispose();
}
}

0 comments on commit bf112dd

Please sign in to comment.