From idea to reality in 2 days with Flutter Web: How I posted a “meme-product” over Product Hunt

Mariano Zorrilla
Flutter Community
Published in
9 min readApr 14, 2020

LINK Product Hunt:

LINK TMSBAE:

Do you work for a large corporation? I do… Did you ever thought (more than once) “Oh, boy… 9am and my day is already packed by meeting and zero coding!” I do, almost every day 🙃👨‍💻

I’m pretty sure you think the same way every time you spend 2 hours checking your favorite social media in the middle of the most boring meeting even done by management/product/you name it.

BUT ENOUGH INFOMERCIAL BLACK AND WHITE FLASHBACKS!

1Where do I start (The Problem):

Ok, I know everyone it’s at home (please BE AT HOME safe and sound for you, your family and everyone around the world ❤️🏠) and we all have a lot of time to do… things, right? (TV shows, movies, social medial… yeah, I know).

For those who are fortuned enough to work from home, the only way to communicate its over chat, emails and MEETINGS 🥶 lots of those, like… LOTS. And I’m not going to mention those video-chat meetings, but they’re becoming sooooo popular (and controversial) nowadays.

2The memes (The research?):

We all love memes, right? There goes some valuable time we’ll never get back… BUT YOU GOTTA LOVE MEMES 🥰

So, I thought “How can I combine a meme with an actual product” and you know where this is going.

3Manos a la obra (It’s working time):

Alright, I have my 8 hours of “office work” (let me put those pants back on) and then, nothing else, I’m stuck like everyone else helping stop this controversial global problematic… by I digress.

So, I don’t have much Web knowledge as I’m mostly a Mobile Developer who nowadays ended up doing more management duties (I know the pain from both sides) but I DO know Flutter (notice me Senpai 😍) and Flutter Web it’s currently on BETA but, you cares… I love danger! Well, not really, but I was more than sure that I could achieve this.

4Naming your product:

First thing first, think about a good name for your product, I’m normally suck at that but, this one is a meme, pretty much was served to me!

I got it!

That Meeting Should Be An Email

or TMSBAE which sounds like “Teams Bae” and yes, we’re there for you, for those teams, who hate pointless meetings as much as me.

5Money, money, money:

- Pro Tip: Please, check if your product name wasn’t stolen from you. I was lucky enough but, RUN to your shopping domain of choice and reserve it ASAP! I paid $60/year for a total of 2 domains (.io, .com).

Email, I paid $6/month for it, meh… the regular G services, nothing fancy.

Backend? Ain’t nobody got time for that! Firebase (PaaS), easy… no need to over think, you get database, hosting, storage, etc “for free” (until you become popular)

6Work, Work, Work, Work?

Using my free time to start (yet again) a new project is always FUN, right? But this one actually looked like having real potential and the scope wasn’t THAT high.

Weapon of choice?!

Why? Because it’s SO EASY to start a new project, design and actually GET THINGS DONE!

-Side note, really quick: Mobile Developers, Web is a bit different, even if we use it every single day, but we need to think about routes (paths) more than ever! Even more than Deep Linking.

I came with a quick idea after that Meetup we did with Simon in NYC and the folks of Flutter NYC

My solution came with a small StatefulWidget Navigator, nothing fancy and not the cleanest work in the world:

class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'TMSBAE',
onGenerateRoute: (settings) => NavigatorRoute.route(settings.name),
);
}
}

Every single path will came from onGenerateRoute and then parse that bad boy into our Navigator.

Once I get that path, I used hard core MACHINE LEARNING… or If/Else, for short.

@override
void initState() {
super.initState();
Future.delayed(Duration(milliseconds: 0), () {
if (widget.path == '/') {
Navigator.of(context).pushAndRemoveUntil(HomeScreen.route(), (_) => false);
return;
} else if (widget.path == '/join') {
Navigator.of(context).pushAndRemoveUntil(JoinScreen.route(), (_) => false);
return;
} else if (widget.path.contains('/join/')) {
Navigator.of(context).pushAndRemoveUntil(JoinScreen.routeCode(widget.path.split('/')[2]), (_) => false);
return;
} else if (widget.path == '/bae') {
Navigator.of(context).pushAndRemoveUntil(BaeScreen.route(), (_) => false);
} else if (widget.path.contains('/result/')) {
Navigator.of(context).pushAndRemoveUntil(ResultScreen.routeCode(widget.path.split('/')[2]), (_) => false);
} else if (widget.path == '/about') {

} else {
Navigator.of(context).pushAndRemoveUntil(HomeScreen.route(), (_) => false);
return;
}
});
}

What this does is to avoid getting a huge stack of Pages like the following:

{URL}/join/djUdmd19m4

Will open:

  • “/” Home Screen
  • “/join” Join Screen
  • “/join/djUdmd19m4” The actual Screen we want to open

and that’s the most common issue Flutter developers have using the regular onGenerateRoute method.

7Design:

I’m lucky enough to also be a UI/UX designer/developer… so, technically I’m a Full Stack Developer?! (1 second… let me update my LinkedIn really quick!)

I checked a few design websites to be inspired and know a bit more where to go with my design, color palette, etc… I also looked at some Open Source Fonts to use and blah, blah.

Every project I do, I want to add something weird, different, special and some of those examples are the latest I published online:

But those are one/two screens size, nothing fancy, no Database/Business logic, just a plain cool example using animations and what not.

8Coding:

Again, even if I have free time, remember, I have a full time job, the only free time is the one outside my daily duties… but enough to get this new project done quickly.

While doing a Website, one HUGE word always comes around “RESPONSIVE” yes, PLEASE! Remember this! We have smartphones, laptops, desktop, tablets, etc… your design should be functional/useful in EVERY SINGLE SCREEN!

SingleChildScrollView

is one of those Widgets you’re going to use A LOT, not the most useful if you’re planning to have dynamic lists because this one loads everything in memory.

MediaQuery

To get sizes and orientations, super useful, everyone should know it by now, but… yeah.

LayoutBuilder

It’s a great one! While thinking about different form factors and screens, this one takes a good lead.

Screens

I have a few: Landing Page (Home Screen), Creating Screen, Join Screen, Result Screen, Vote Screen, About Screen.

Home Screen

My entire business logic to move between screens is by using:

Navigator.of(context).pushAndRemoveUntil

I don’t want to keep a stack for this product as I can move around between screens with my AppBar and some other elements/buttons in my screens, please, it becomes more PWA friendly.

PWA

PWA on MacOS

Yes, Flutter supports PWA and No, these two are not different things, they’re just a complement between one and other.

If you’re using Flutter v.1.5+ make sure to run “flutter create .” to have your missing files. IF by any reason you don’t have it, let me give you the following snippets:

In side your index.html <body>:

<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
});
}
</script>

Over your /web folder you should have a manifest.json file, if not, here is it:

{
"name": "App Name",
"short_name": "App Short Name",
"start_url": ".",
"display": "standalone",
"background_color": "#A83EF6",
"theme_color": "#A83EF6",
"description": "App Description",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}

Pay close attention to some elements over there:

"display": "standalone"

allows you to have a full screen/native look-and-feel. You can read more about it here:

You icons should be inside /web/icons (or any other folder, but that way looks nice) and will be the one being used in your home screen / app drawer.

For my crazy looking header, I used:

ClipPath

with the following snippet:

class HeaderWaveClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
var path = Path();
path.lineTo(0, 0);
path.lineTo(0, size.height - 100);
path.cubicTo(
0,
size.height - 90,
size.width * 0.004,
size.height - 70,
size.width * 0.06,
size.height - 75,
);
path.cubicTo(
size.width * 0.07,
size.height - 75,
size.width * 0.14,
size.height - 95,
size.width * 0.12,
size.height - 65,
);
path.cubicTo(
size.width * 0.12,
size.height - 65,
size.width * 0.08,
size.height - 10,
size.width / 3 * 0.98,
size.height - 40,
);
path.cubicTo(
(size.width / 3) * 1.08,
size.height - 40,
(size.width / 3) - 60,
size.height - 5,
(size.width / 3) + 100,
size.height - 5,
);
path.cubicTo(
(size.width / 3) * 1.8,
size.height - 5,
(size.width / 2) * 1.6,
size.height,
(size.width / 2) * 1.52,
size.height - 30,
);
path.cubicTo(
(size.width / 2) * 1.52,
size.height - 35,
(size.width / 2) * 1.4,
size.height - 60,
size.width - (size.width * 0.05),
size.height - 60,
);
path.cubicTo(
size.width - (size.width * 0.05),
size.height - 60,
size.width,
size.height - 60,
size.width,
size.height - 90,
);
path.lineTo(size.width, 0);
return path;
}

@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

THAT ONE took me HOURS to resolve… math is fun and all, but… yeah, hours to make it responsive and look nice on every single screen.

I’d said that everything took me around 12 hours of coding while many of those hours where design + testing in the frame of 2 days of work.

Hosting over Firebase took me seconds (literally) and using my own domain were so simple thanks to G-Domains.

9 Becoming viral? (Hopefully)

I knew one specific website had what I needed to post my new creation and that one was Product Hunt.

Every new/cool/weird product/project ends over there and I wanted to know how that feels! Wanna to create my first viral product after so many years of coding without many cool/funny ideas.

The process is pretty much straight forward:

  • Create an account
  • Wait 1 week (or follow their Newsletter to have instant access)
  • Publish your product
  • Follow every single detail (title, description, icon, screenshots/videos, etc)
  • Publish! (You can schedule this one)

Final Thoughts

That was really fun to do and hopefully people use it, a lot! I know is just a meme, but this one has potential and maybe teams will include my auto generated links inside their calendars so everyone will have the chance to decide if That Meeting… Should Be An Email.

Thank you so much!

--

--

Mariano Zorrilla
Flutter Community

GDE Flutter — Tech Lead — Engineer Manager | Flutter | Android @ Venmo