[MUSIC PLAYING]
KHANH NGUYEN: Oh OK.
All right.
Let's go buy this.
Check out.
[POP]
Wait.
Wow, that was really fast shipping.
Cool.
My merch is here.
[MUSIC PLAYING]
Hey, y'all.
Welcome back to episode three of "Learning to Fly."
In this episode, we'll go over a bunch
of changes and improvements that have
been introduced to our MyDash app since the previous episode.
Then we'll go ahead and jump into the highlight
of this episode, the all new DashShop,
where we'll take a look at what a theoretical merch
store for Dash would look like and how I would implement it.
Of course, it wouldn't be learning
to fly without hitting some turbulence along the way.
So we'll also talk through some of the problems I ran into
and how I resolved it.
And finally, we'll wrap up with some questions for Fitz.
Let's get started.
[JINGLING SOUND]
In the previous episode, we went over our fancy new Dash theme.
We looked at the dashboard UI and discussed some of the road
bumps that I had run into, including but not limited
to the bottom navigation bar.
[JINGLING SOUND]
Since then, I took some of the notes
that we got from the last episode
and made some changes to MyDash.
[JINGLING SOUND]
First off, I was able to resolve my issue with accessing my Dash
theme colors.
As some of you pointed out, I had a row of consts
that I had overlooked.
Oops.
[JINGLING SOUND]
Furthermore, based on another suggestion,
I pulled my Dash theme into a separate file,
so that it's more organized and doesn't get all cluttered up
in the code.
Finally, from the discussion with Fitz,
my comment now is, well, more properly formatted
and wraps around underneath the username thanks
to rich text and text span.
[JINGLING SOUND]
While I was at it, I also changed the username
so that it distinguishes between post captions
and comments just a little bit more.
[JINGLING SOUND]
Finally, I pushed it as low as possible at the dashboard level
just like how it appears in the app, just above the card level.
[JINGLING SOUND]
Now, with all that covered, we can jump into the DashShop.
When I thought about building a Dash merch store
and what it would look like, this is what I came up with.
And this here is the coded-up Flutter version.
You have the main shop page where
there's a GridView of cards containing an image, name,
and price for each of the products.
There's a row at the top for the typical filter
button in the search box.
But shh, don't tell anyone, it doesn't really
do anything quite just yet.
From there, we can tap on one of the cards
and be taken over to the product page.
The neat part here is that I was able to use the hero
widget to animate from the shop page image
to the product page image.
It took about a whole minute for me
to watch the "Widget Of the Week" episode for it.
We'll link to it up there as well--
and another minute to add the code.
And voila, it just worked.
[JINGLING SOUND]
As for the product page images, they're
rotating through using a carousel slider.
The size selector is a toggle buttons widget
and a dropdown button or the quantity selector.
And of course, an elevated button for our must-have
add to bag functionality.
Now let's say I wanted to buy a sweatshirt in size medium.
I only want one for myself.
I hit Add To Bag, and it jumps me back to the shop page.
From there, we can tap the Bag button, which then will take us
to our, well, our bag.
And there you'll see our final page, our shopping
bag with all of our stuff.
The shopping bag is fairly simple.
It's made up of a list view of cards,
again, for the product previews.
You also have the price calculations at the bottom.
And finally, a Checkout button.
So when you hit checkout, it'll jump you back to the shop page
and clear your cart.
You're probably wondering, Khanh, why?
That's not how checkout is supposed to work.
Well, thank you for asking.
Let's jump to the decisions and considerations
portion of this episode.
[JINGLING SOUND]
I had left out the checkout process
because I would personally always want
to use a payment processor package
for that portion of the app, like Flutter Stripe.
On the same note, a Flutter cart package also exists,
but I decided to build my own cart logic,
because I thought it would be a good opportunity to write
and learn some non-UI code.
Bag state lives at the shop level of my app.
The inherent problem here is that the bag
doesn't persist if I navigate away from the shop page.
So in every other scenario, state management
would be important here.
I suspect using a package like Hive for a NoSQL database would
be a nice integration to have.
[JINGLING SOUND]
With all that said, I also made a lot of assumptions
that I would not have made with a real production-ready app,
like assuming that there will always
be one product image for every product in the shop.
This one would be relatively easy to get around by checking
if the product has an image.
Otherwise replace it with a default image.
The shop has easily been the most complex part of this app
that I've worked on so far.
So it's no surprise that I've run into the most hurdles
while building it.
[JINGLING SOUND]
Luckily, as I've been learning Flutter,
I've usually been able to look up
the thing that I want to do and then
just get an idea of the direction that I want to go in.
[JINGLING SOUND]
My problems most of the time end up getting solved the same way
as well, from figuring out why Flutter was yelling
at me about, the argument type color
can't be assigned to the parameter type material state
property, to the exception that kept
getting thrown for, incorrect use of parent data widget.
Two Google searches later, I found out
that I needed to wrap my color in a material state
property all, and that I had a rogue expanded
widget that wasn't inside of a row, column, or a flex.
So removing the expanded widget was the answer.
[JINGLING SOUND]
That's not to say that I didn't run into issues that
took me forever to figure out.
Two examples, number one, left-aligning a bunch of text
within a column.
I truly didn't think I would struggle as much as I did.
After a long while googling and fighting with my code,
I settled for wrapping the text in a size box
that takes up the entire width and then set text align to TextAlign.left
This method still feels a little hacky to me.
So I'm guessing there's probably a better way to do this.
If you have any recommendations, please drop a comment.
I learned that list views also have a default padding,
because there was a huge gap in my bag list of items
and the top header.
I just couldn't figure out why there was so much white space.
I also kept getting an overflow error
on the cards in my GridView.
But adding a size box was no help.
Container?
Nope.
The answer here as it turns out was the aspect ratio
on my GridView.
The aspect ratio defaults to one-to-one.
So the height of the card is constrained
by the width of the card, which my card was very much not
expecting.
And finally the one problem that I can't seem to figure out,
I can't add my entire assets directory to my pub spec
without getting an exception.
[JINGLING SOUND]
I still haven't figured this one out.
So let's phone a friend.
[PHONE RINGING]
[MUSIC PLAYING]
FITZ: Hello?
KHANH NGUYEN: Hi, Fitz.
FITZ: Oh.
KHANH NGUYEN: Hi, this is Khanh.
FITZ: Hello.
KHANH NGUYEN: Hi.
So I'm on "Who Wants to be a--
I mean, I'm filming an episode of "Learning to Fly."
but I have some questions that I would like answers for.
Can you help?
FITZ: Yeah, absolutely.
Let's do it.
What questions do you have for me?
KHANH NGUYEN: Cool.
Thanks, Fitz.
So my first question for you is, well, I've
encountered this problem where in order for me to get access
to all of my product images as an asset,
I have to add each of the image paths to my pub spec.
If I try to add my directory just called assets slash,
it throws an exception on the load async function.
Any ideas there?
FITZ: That's interesting.
Because you should be able to load a full directory.
KHANH NGUYEN: Mm-hmm.
FITZ: And I'm wondering if--
so I've seen sometimes the trailing slash
makes a difference whether you have it or not.
KHANH NGUYEN: Mm-hmm.
FITZ: And I'm also wondering if having a subdirectory
in the assets helps as well.
And I'm curious to hear more.
What did you experience, and what did you try?
KHANH NGUYEN: Yeah.
So when I tried-- so originally, I had only just one image,
it was a picture, it was like an image of Dash.
So I didn't really have to add a whole directory.
I was just saying, OK, let me just add this one image.
And that worked perfectly fine.
And then once I started building the DashShop,
I started adding more product images.
And so I decided, hey, instead of having just this one
image, just one image path, I'm going
to make a subdirectory within assets that's
called images with a shop directory in addition
to that so that I have all of the product images stored
in there.
And then so that's when I tried to load in that directory.
And it didn't work out too well.
Because then it just, the load async function,
it said something about the key was not loading.
So I'm not really sure there.
[LAUGHS]
FITZ: That's very interesting.
And so when you have this assets folder.
It's just an assets directory in the root of your source folder?
KHANH NGUYEN: Yeah.
Mm-hmm.
FITZ: Huh.
I don't know.
That's very interesting.
You'll have to follow up with me after you learn more
about that.
Because I don't know.
It should be working.
KHANH NGUYEN: OK.
All right.
Well, let me troubleshoot that, and then I'll
report back on the next episode then.
FITZ: Sounds great.
Look forward to the answer.
[LAUGHS]
KHANH NGUYEN: All right.
Cool.
So my next question for you is about my shop and product
pages.
So right now they're pretty tightly coupled.
The product page returns either a product or null.
So a product would indicate that the customer wanted
to add that product to cart.
So the product then gets added to our cart that
is stored on the shop page.
Null would indicate that they just
want to exit the product page without adding anything to cart
so that, well, it doesn't do anything.
Does that sound OK?
What is the correct way to return data,
say, from the product page to the main shop page?
FITZ: OK, that's really interesting.
Because I think you want to think about what
the purpose of each of those--
you're dealing with three different things here.
KHANH NGUYEN: Mm-hmm.
FITZ: You're dealing with the product,
the cart, and the main shopping page, right?
KHANH NGUYEN: Mm-hmm.
FITZ: And so then, your question really
is where is the functionality for adding
a product to the cart and who is responsible for that.
And so that's something to think about here.
Because should the product be responsible
for it adding itself to the cart?
KHANH NGUYEN: Mm-hmm.
FITZ: Should the cart be responsible for adding
a product to itself?
KHANH NGUYEN: Mm-hmm.
FITZ: Or should the shopping page
be responsible for adding a product to the cart.
And I don't know the right answer.
But I think once you've really determined that answer, that
will change what you think about in terms
of the returning from the product page
into the shopping page.
Because right now you're using that return value
to indicate whether it needs to be added to the cart.
And I think that's combining two different notions.
You have the navigation from the product page to the cart page,
or the shopping page.
And you also have this idea of adding something to the cart.
And I think when you combine those two,
I think that's where you end up with this question of is null
the right thing to return, or is the product
the right thing to return.
I'm not sure.
And maybe the not sureness is coming
from who's responsibility is it to add to the cart.
KHANH NGUYEN: Mm-hmm.
Actually, yeah, now that you mentioned
that, I'm starting to think it would probably
make sense to just completely separate those transitions
between pages.
So that the cart can handle just the,
oh, there's a product we're adding to cart.
So that way when we return, the shop page
doesn't need to bother even thinking
about what it needs to add to cart
or what not to add to cart.
FITZ: Yeah, exactly.
The cart could be essentially its own notion of state.
And so the product could say, make a change to this state
thing, which is add myself to this cart.
And then it doesn't have to worry about anything else,
and the shop page doesn't have to worry about anything either.
It just gets the current state of the state of the cart state.
[LAUGHS]
KHANH NGUYEN: Gotcha.
I think I got you.
Gotcha.
OK.
Let me I think.
That is definitely one thing I want to go back and try
to fix up then.
Because right now, it's so tightly
coupled that it didn't seem like it was a right way to do that.
So thanks, Fitz.
All right.
I think I got that down.
Cool.
FITZ: Cool.
KHANH NGUYEN: So my next question
for you is on the topic of builders.
So there's a lot of repetitive widgets in the DashShop,
primarily building the cards.
I probably could have used more builders instead
of for loops that I used.
I read-- heard that builders are supposed to be
more efficient than for loops.
Why is that?
What is the rule there?
Is there a place where a for loop
should be used instead of a builder?
Is there a rule of thumb that I should follow?
FITZ: Yeah.
I mean, I think the rule is--
and it's more of a principle than a rule.
I think rules are meant to be never broken,
and principles are sometimes broken.
But the principle would be, use builders where possible.
And there's two reasons why.
One is that in cases like list views or grid views,
you'll get a progressive build of what's on screen or maybe
just off screen slightly is going to be what's built.
When you just have a for loop building a static list,
it's going to build everything every time.
KHANH NGUYEN: Mm-hmm.
FITZ: Or you have to do some weird hand-waving in order
to get it to only build some things.
And that's where the builder comes in,
because it'll do some of that hand-waviness for you
of checking what's on the screen.
The other thing the builder will do
is give you a new build context.
And that's important for when you
need to use things like scaffolds,
or detect if there's an app bar, or try
to get the right navigator in order
to navigate between your two pages, or something like that.
That's important to have a new build
context, which you won't get if you just
use a static for loop list.
KHANH NGUYEN: Oh, OK.
I think-- right, the build context.
OK.
That makes sense.
So just as a principle, that's probably a good principle
to stick by.
Not rule, it's a principle.
Cool.
FITZ: Yeah.
KHANH NGUYEN: Let me go back, and I'll probably
have to check for any for loops I've
been using, just to try to turn that into builder code instead.
FITZ: Totally.
KHANH NGUYEN: Awesome.
So my last question for you is not really a question,
more so of just, I guess, a prompt.
Do you have any recommendations or feedback on the DashShop
for me, whether that be code review, the UI design,
or anything that you have?
FITZ: Yeah.
I think I was looking through it before this.
And I think that there's two pieces of feedback.
One is there are only like three items.
I want more Dash swag items.
That would be great.
And I would love to buy as many of them as possible.
[LAUGHS] That's one.
KHANH NGUYEN: Noted.
[LAUGHTER]
FITZ: I just need more swag.
KHANH NGUYEN: All right.
FITZ: The second one is, I noticed some of your widgets
were having a lot of code.
[LAUGHTER]
There was many, many lines and some nested widgets inside
of those build methods.
And sometimes, you have that one widget
which was encompassing a lot of different things.
And I think we talked a little bit about that
with the notion of who's responsible for adding
a product to the cart.
KHANH NGUYEN: Mm-hmm
FITZ: And so it looked like, from the code perspective,
there was a lot going on in each one of these things.
And it might be worth trying to think
about how to segment things out, how to break them up,
and how to just partition things out a little bit,
refactor them into some things.
KHANH NGUYEN: Gotcha.
Fitz, you are also being far too nice.
I think everything was just one huge build method.
FITZ: [LAUGHS]
KHANH NGUYEN: So I'm glad you called that out.
I will totally go back and fix that up.
In terms of cleaning that up, any
recommendations other than if--
it's a huge block of code.
Any suggestions where I should start to break that out?
FITZ: Yeah.
It's hard to say in a general sense.
But I would look for making sure that a widget is
responsible for one thing, and one thing
only as much as possible.
KHANH NGUYEN: OK.
Do one thing, and do it well.
FITZ: Yep.
KHANH NGUYEN: Cool.
OK.
That seems like another good principle
that I should live by when I built the Flutter code.
Cool.
I think that was all the questions I had for you.
Thank you so much, Fitz, for coming by and helping out
on such short notice.
FITZ: All right.
Absolutely.
And feel free to give me a ring on my fancy smartphone here.
All right.
KHANH NGUYEN: For sure.
You will be my first phone a friend.
[LAUGHS] Thanks, Fitz.
FITZ: All right.
Yep.
Good luck, Khanh.
KHANH NGUYEN: Bye.
[JINGLING SOUND]
All right.
So that just about does it for this episode of "Learn to Fly."
Thank you so much for joining.
And before we go, I do have a question for you.
What Dash merch would you like to theoretically see
in the DashShop?
You may see it in the next episode.
So that about does it again.
Thank you so much for joining, and I'll
see you in the next one.
Bye.
[MUSIC PLAYING]