Skip to content

Commit

Permalink
Merge pull request #1 from emanuel-braz/feature/add-mobile-layout
Browse files Browse the repository at this point in the history
feat: add mobile layout
  • Loading branch information
emanuel-braz authored Apr 12, 2024
2 parents 9afba0e + 04755ef commit 753fec5
Show file tree
Hide file tree
Showing 7 changed files with 302 additions and 129 deletions.
5 changes: 5 additions & 0 deletions lib/core/utils/layout_util.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import 'package:flutter/material.dart';

class LayoutUtil {
static bool isMobileLayout(BuildContext context) => MediaQuery.of(context).size.width < 600;
}
155 changes: 28 additions & 127 deletions lib/presentation/home/home_page.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_sp_social/data/social_qr_code.dart';
import 'package:flutter_sp_social/presentation/home/event_store.dart';
import 'package:pretty_qr_code/pretty_qr_code.dart';
import 'package:flutter_sp_social/core/utils/layout_util.dart';

const _defaultQrIcon = AssetImage('images/dash.png');
import 'event_store.dart';
import 'home_view/home_page_factory.dart';

class HomePage extends StatefulWidget {
const HomePage({super.key});
Expand All @@ -15,14 +14,10 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> {
final _store = EventStore();

late final double _cardMaxWidth;

@override
void initState() {
super.initState();

final params = Uri.base.queryParameters;
_cardMaxWidth = double.tryParse(params['width'] ?? '') ?? 300.0;
WidgetsBinding.instance.addPostFrameCallback((_) {
_store.loadEventData();
});
Expand All @@ -38,132 +33,38 @@ class _HomePageState extends State<HomePage> {
}

return Scaffold(
appBar: AppBar(
title: Text(
_store.value!.eventName,
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
color: Theme.of(context).colorScheme.background,
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
semanticsLabel: _store.value!.eventName,
),
actions: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, 'sorteio');
},
child: const Text(
'Sorteio',
),
),
),
],
centerTitle: true,
backgroundColor: _store.value!.color != null
? Color(int.parse(_store.value!.color!, radix: 16))
: Theme.of(context).colorScheme.primary,
),
backgroundColor: Theme.of(context).colorScheme.background,
body: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(16),
child: SingleChildScrollView(
padding: const EdgeInsets.only(bottom: 16),
child: Wrap(
spacing: 16,
runSpacing: 16,
children: _store.value!.socialQrCodes
.map((e) => _QRCode(socialQrCode: e, width: _cardMaxWidth))
.toList()),
),
),
);
});
}
}

class _QRCode extends StatelessWidget {
final SocialQrCode socialQrCode;
final double width;

const _QRCode({required this.socialQrCode, required this.width});

@override
Widget build(BuildContext context) {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
showDialog(
context: context,
barrierColor: Colors.black.withOpacity(0.98),
builder: (_) => AlertDialog(
backgroundColor: Theme.of(context).colorScheme.surface,
content: SizedBox(
width: MediaQuery.sizeOf(context).height * 0.7,
child: AbsorbPointer(absorbing: true, child: this),
),
));
},
child: Container(
constraints: BoxConstraints(maxWidth: width),
padding: const EdgeInsets.only(top: 16, left: 16, right: 16),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(16),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(8),
),
child: PrettyQrView(
qrImage: QrImage(
QrCode.fromData(
data: socialQrCode.qrCode,
errorCorrectLevel: QrErrorCorrectLevel.H,
),
),
decoration: PrettyQrDecoration(
shape: PrettyQrSmoothSymbol(
color: socialQrCode.color != null
? Color(int.parse(socialQrCode.color!, radix: 16))
: Theme.of(context).colorScheme.primary,
roundFactor: 0,
),
image: PrettyQrDecorationImage(
image: socialQrCode.icon != null
? NetworkImage(socialQrCode.icon!) as ImageProvider
: _defaultQrIcon,
position: PrettyQrDecorationImagePosition.embedded,
),
)),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
socialQrCode.title,
appBar: AppBar(
title: Text(
_store.value!.eventName,
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
color: Theme.of(context).colorScheme.background,
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
semanticsLabel: socialQrCode.title,
semanticsLabel: _store.value!.eventName,
),
actions: [
if (!LayoutUtil.isMobileLayout(context))
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, 'sorteio');
},
child: const Text(
'Sorteio',
),
),
),
],
centerTitle: true,
backgroundColor: _store.value!.color != null
? Color(int.parse(_store.value!.color!, radix: 16))
: Theme.of(context).colorScheme.primary,
),
],
),
),
),
);
backgroundColor: Theme.of(context).colorScheme.background,
body: HomePageViewFactory.build(context, _store));
});
}
}
122 changes: 122 additions & 0 deletions lib/presentation/home/home_view/desktop_view.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import 'package:flutter/material.dart';
import 'package:flutter_sp_social/data/social_qr_code.dart';
import 'package:flutter_sp_social/presentation/home/event_store.dart';
import 'package:pretty_qr_code/pretty_qr_code.dart';

class DesktopView extends StatefulWidget {
final EventStore store;

const DesktopView({super.key, required this.store});

@override
State<DesktopView> createState() => _DesktopViewState();
}

class _DesktopViewState extends State<DesktopView> {
late final double _cardMaxWidth;

@override
void initState() {
super.initState();
final params = Uri.base.queryParameters;
_cardMaxWidth = double.tryParse(params['width'] ?? '') ?? 300.0;
}

@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(16),
child: SingleChildScrollView(
padding: const EdgeInsets.only(bottom: 16),
child: Wrap(
spacing: 16,
runSpacing: 16,
children:
widget.store.value!.socialQrCodes.map((e) => _QRCode(socialQrCode: e, width: _cardMaxWidth)).toList()),
),
);
}
}

class _QRCode extends StatelessWidget {
final SocialQrCode socialQrCode;
final double width;

const _QRCode({required this.socialQrCode, required this.width});

@override
Widget build(BuildContext context) {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
showDialog(
context: context,
barrierColor: Colors.black.withOpacity(0.98),
builder: (_) => AlertDialog(
backgroundColor: Theme.of(context).colorScheme.surface,
content: SizedBox(
width: MediaQuery.sizeOf(context).height * 0.7,
child: AbsorbPointer(absorbing: true, child: this),
),
));
},
child: Container(
constraints: BoxConstraints(maxWidth: width),
padding: const EdgeInsets.only(top: 16, left: 16, right: 16),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(16),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(8),
),
child: PrettyQrView(
qrImage: QrImage(
QrCode.fromData(
data: socialQrCode.qrCode,
errorCorrectLevel: QrErrorCorrectLevel.H,
),
),
decoration: PrettyQrDecoration(
shape: PrettyQrSmoothSymbol(
color: socialQrCode.color != null
? Color(int.parse(socialQrCode.color!, radix: 16))
: Theme.of(context).colorScheme.primary,
roundFactor: 0,
),
image: PrettyQrDecorationImage(
image: socialQrCode.icon != null
? NetworkImage(socialQrCode.icon!) as ImageProvider
: const AssetImage('images/dash.png'),
position: PrettyQrDecorationImagePosition.embedded,
),
)),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
socialQrCode.title,
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
color: Theme.of(context).colorScheme.background,
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
semanticsLabel: socialQrCode.title,
),
),
],
),
),
),
);
}
}
15 changes: 15 additions & 0 deletions lib/presentation/home/home_view/home_page_factory.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
import 'package:flutter_sp_social/core/utils/layout_util.dart';
import 'package:flutter_sp_social/presentation/home/event_store.dart';
import 'package:flutter_sp_social/presentation/home/home_view/desktop_view.dart';
import 'package:flutter_sp_social/presentation/home/home_view/mobile_view.dart';

class HomePageViewFactory {
static Widget build(BuildContext context, EventStore store) {
if (LayoutUtil.isMobileLayout(context)) {
return MobileView(store: store);
} else {
return DesktopView(store: store);
}
}
}
65 changes: 65 additions & 0 deletions lib/presentation/home/home_view/mobile_view.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher_string.dart';

import '../../../data/social_qr_code.dart';
import '../event_store.dart';

class MobileView extends StatelessWidget {
final EventStore store;

const MobileView({super.key, required this.store});

@override
Widget build(BuildContext context) {
return ListView.separated(
padding: const EdgeInsets.all(16),
itemCount: store.value!.socialQrCodes.length,
separatorBuilder: (context, index) => const SizedBox(height: 16),
itemBuilder: (context, index) => ListTileWidget(socialQrCode: store.value!.socialQrCodes[index]),
);
}
}

class ListTileWidget extends StatelessWidget {
final SocialQrCode socialQrCode;
const ListTileWidget({super.key, required this.socialQrCode});

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final textTheme = theme.textTheme;

Widget icon = socialQrCode.icon != null
? Image.network(socialQrCode.icon!, fit: BoxFit.fitWidth, width: 48, height: 48)
: Image.asset('images/dash.png', width: 48, height: 48, fit: BoxFit.fitWidth);

return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () => launchUrlString(socialQrCode.qrCode),
child: Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(color: theme.colorScheme.primary),
),
padding: const EdgeInsets.all(16),
child: Row(
children: [
icon,
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(socialQrCode.title, style: textTheme.headlineSmall),
Text(socialQrCode.qrCode, style: textTheme.bodySmall),
],
),
),
],
)),
),
);
}
}
Loading

0 comments on commit 753fec5

Please sign in to comment.