Thursday, July 13, 2023

Flutter Assets Generator

1. Create assets, folders, files:



2. Update dev_dependencies:

dev_dependencies:
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
build_runner: ^2.4.6
flutter_gen_runner: ^5.3.1

flutter_gen:
output: lib/generated/assets/ # Optional (default: lib/gen/)
line_length: 80 # Optional (default: 80)

integrations:
flutter_svg: true

assets:
enabled: true

outputs:
class_name: MyAssets
package_parameter_enabled: false
# Assets.imagesChip
# style: camel-case

# Assets.images_chip
# style: snake-case

# Assets.images.chip (default style)
style: dot-delimiter

exclude:
- assets/images/chip3/chip3.jpg
- pictures/chip5.jpg
- assets/flare/

fonts:
enabled: true
outputs:
class_name: MyFontFamily

colors:
enabled: true
outputs:
class_name: MyColorName
inputs:
- assets/color/colors.xml
- assets/colors/colors.xml

flutter:
uses-material-design: true
assets:
- assets/images/
- assets/images/chip3/chip3.jpg
- assets/images/chip4/
- assets/images/icons/fuchsia.svg
- assets/json/
- pictures/chip5.jpg
- assets/flare/

fonts:
- family: Raleway
fonts:
- asset: assets/fonts/Raleway-Regular.ttf
- asset: assets/fonts/Raleway-Italic.ttf
style: italic
- family: RobotoMono
fonts:
- asset: assets/fonts/RobotoMono-Regular.ttf
- asset: assets/fonts/RobotoMono-Bold.ttf
weight: 700

3. Run command to generate files:

# flutter pub run build_runner build --delete-conflicting-outputs REM generated assets, files, fonts, colors,...

or

# dart run build_runner build --delete-conflicting-outputs REM generated assets, files, fonts, colors,...

4. Generated files:


5. Use case:


Wednesday, July 12, 2023

Internationalizing Flutter apps

I. Basic setttings: 

1. In pubspec.yaml add below to dependencies:

# Localization
flutter_localizations:
sdk: flutter
intl: ^0.18.0
intl_utils: ^2.8.3

2. Create intl_en.arb, intl_vi.arb into lib/l10n:


intl_vi.arb: 

{
"@@locale": "vi",
"app_name": "vieBooks",
"greeting": "Xin chào!"
}

intl_en.arb:

{
"@@locale": "en",
"app_name": "vieBooks",
"greeting": "Hello!"
}

3. Run flutter pub run intl_utils:generate to generate languages files into lib/generate

4. Update main.dart


5. Checking the result:



II. Advanced setting with parameters:







Monday, July 10, 2023

Flutter Package Arrangement

 1. Package-by-layer

Using a package-by-layer structure means you group your files based on their technical aspects. So, for example, your database files get one package, your networking files get another, your widgets another and so on.

This is how WonderWords’ package distribution would look with package-by-layer:


Advantages

  • For some reason, package-by-layer speaks to our nature; it’s how our minds intuitively work. That makes the strongest argument in favor of this approach: there’s a low learning curve.
  • It encourages code reuse. Files belong to a layer and not to a feature of the app, so you don’t think twice if you need to reuse a component even if the team originally created it to support another feature.
  • Different projects end up having a similar (or even the same) structure. Curiously, that’s also the first point on the list of disadvantages.

Disadvantages

  • The package-by-layer structure doesn’t immediately convey the most interesting information about your app. When skimming through a codebase, it’s unlikely that you want to know if it “has screens” more than you want to know which features it has.
  • Everything is public. For example, every screen file can import all the state manager files, even though most screens use a single state manager. This makes it easier for inattentive developers to import files they’re not supposed to.
  • Developers have to jump around the file tree constantly. This is what happens when files that often change together are stored in different locations, such as screens and state managers. That goes against what Robert Cecil Martin, acclaimed author of “Clean Code”, taught us about the Single Responsibility Principle: “Gather together the things that change for the same reasons.” 
  • It doesn’t scale well. As the number of files in your project grows, the number of packages stays the same. Whether your project has five or fiftyscreens, you still only have one ui package.
  • It makes it hard to onboard new team members. You either know how all or none of the features work; there’s no middle ground. You feel like you have to understand everything in order to help with anything. 

2. Package-by-feature

An alternative to grouping files based on their technical aspects is to group them by their domain affinity. For example, instead of having quote_list_screen.dart under ui and quote_list_bloc.dart under state_managers, you could have both under a quote_list package.

For WonderWords, using a package-by-feature approach would look like this:


Advantages:

  • With a package-by-feature approach, finding files is a breeze. The structure of your codebase now mimics the design of your app.
  • It scales well. As the number of files grows, the number of packages grows accordingly.
  • The codebase becomes self-documenting. At a glance, you have an idea of how big the app is and what it does.
  • You have complete visibility control. For example, now quote_list_bloc.dart can only be visible inside the quote_list package. 
  • It offers smoother onboarding. You only have to understand the feature you’re working on.
  • You get clearer squad ownership. Each subteam knows exactly which packages it’s responsible for.
  • It’s easy to conduct experiments and migrations. Want to try a new state management approach? No problem. Restrain it to a single feature package and no one else has to worry about it. 

Disadvantages:

  • It promotes the creation of the so-called common package — a package that developers usually create to hold all the code used by more than one feature. This might seem good in theory. In practice, the common package becomes a giant dumpster of files that are completely unrelated to one another.
  • There’s a higher risk of code duplication. If you need something that’s already implemented in another feature, there’s a chance you either don’t know about it or don’t want to take on the burden of moving it to the common package, so you just create another version.
  • It demands a certain cognitive load when deciding where to place a file. “Should it be inside that package? Should I create another package for it? Should it be inside common?"

Now that you know how both package-by-layer and package-by-feature work, can you guess which one WonderWords uses?

Your answer is correct if you said neither... or both. 

3. Packaging by Convenience:

As you’ve seen, both approaches have pros and cons. Package-by-layer works best for files that aren’t tied to a single feature, like databases and networking stuff. In contrast, package-by-feature shines for files that are rarely reused, like screens and state managers.

So, why not mix both and create packages as you find convenient instead? 

Note“Convenient” may sound sloppy but, as you’ll see, that’s the opposite direction of where you’re heading.

Finally, this is how WonderWords’ package distribution really is:


Notice that some packages are feature-based, like quote_list, quote_details and sign_in. In contrast, all the other packages represented above are layer-based, like key_value_storage and component_library.

These are the four commandments governing WonderWords' package distribution:

1. Features get their own package

For this command, you follow the package-by-feature approach. But what’s a feature?

For some people, a feature is a screen; for others, it’s a flow of related screens. Furthermore, formal definitions will tell you that a screen can gather many features, like a home screen. At the same time, a feature can span out across different screens, like an e-commerce checkout process. Sounds complex, right?

Luckily, you don’t have to be that dogmatic. Here, you’ll consider a feature to be either: 

1. A screen.

2. A dialog that executes I/O calls, like networking and databases WonderWords' forgot_password package falls into this category.

Other than that, if it’s just a dummy UI component that you want to share  between two or more screens, like a search bar, you should place it inside the component_library package.

2. Features don’t know about each other

When screen A wants to open screen B, it doesn’t import screen B and navigate to it directly. Instead, screen A’s constructor receives a function that it can call when it wants to open screen B. In the end, the main connect the wires. application package will

To give you a concrete example from WonderWords, when the QuoteListScreen wants to open the QuoteDetailsScreen , it just calls the onQuoteTap callback it received in its constructor. 

3. Repositories also get their own package

Chapter 2, “Mastering the Repository Pattern”, will explain repositories in depth. For now, know that they’re classes responsible for retrieving and sending data by coordinating different sources, like the network and a database.

For example, in WonderWords, user_repository and database calls to send and retrieve the user’s information. 

4. No common package

Repeat with me: No common package.

When you need to share something between two or more packages, you’ll create a more specialized package to handle that. Five of your packages originated from this rule:

  • component_library: Holds UI components that either are, or have the potential to be, reused across different screens.
  • fav_qs_api: Since both user_repository and quote_repository talk to your remote API, it makes sense to create a separate package for it.
  •  key_value_storage: Same story as fav_qs_api, but this one wraps your local storage.
  • domain_models: You can expect that repositories will need to start sharing models or custom exceptions at some point. Therefore, it’s a good thing to have a separate package for your domain models right from the start. 

  •  form_fields: Contains field validation logic that different features share.
Notice how you could have easily dumped these into a single common package, but instead, you ended up putting them into six different specialized packages.

Note: Don’t worry if you find all this overwhelming; the following chapters go through each of these commandments in detail.

Hopefully, that was enough to hype you up for what’s yet to come. Now, you’ll go through the steps you need to run the app for yourself. 

Flutter Assets Generator

1. Create assets, folders, files: 2. Update dev_dependencies : dev_dependencies : flutter_test : sdk : flutter integration_test : ...