Jak szybciej budować aplikacje we Flutterze? Wykorzystaj masona i szablony

Jak szybciej budować aplikacje we Flutterze? Wykorzystaj masona i szablony

Masz już dość rozpoczynania projektów we Flutterze od zera? Chcesz, żeby istniał sposób, który pozwoli przyspieszyć wstępną konfigurację i ponownie wykorzystywać kod w różnych projektach?

Narzędzie o nazwie mason_cli może być dokładnie tym, czego potrzebujesz, by zoptymalizować proces rozwoju oprogramowania. Dowiedz się, jak pomoże Ci przy tworzeniu szablonów aplikacji i korzystaniu z nich.

Czym są szablony aplikacji?

Szablon to wstępnie zaprojektowany i napisany projekt, który może służyć jako podstawa do budowy aplikacji. Zwykle zawiera UI, bazowe funkcjonalności i inne elementy często spotykane w aplikacjach mobilnych.

Dzięki szablonom aplikacji oszczędzasz czas. W rezultacie możesz bardziej się skupić na dopasowywaniu produktu do indywidualnych potrzeb i wymagań.

Co to jest mason?

Pakiet mason to generator do budowy szablonów w Darcie, który pomaga zespołom szybko tworzyć pliki. To pozwala deweloperom rozwijać ich własne szablony aplikacji zwane cegłami, czyli bricks. Wykorzystują do tego neutralną językowo notację szablonu mustache.

Można jej używać do tworzenia szablonów dla każdego języka programowania. To sprawia, że mason jest szczególnie przydatny, gdy jeden framework jest stosowany w wielu projektach.

Ponadto jest rozwiązaniem typu open-source, rozwijanym i utrzymywanym przez społeczność. Nieustannie więc ewoluuje i działa coraz lepiej. Staje się przez to odpowiednim narzędziem dla każdego, kto chce zoptymalizować swój proces rozwoju produktu.

Zalety korzystania z masona

  • Możesz szybko przeprowadzić wstępną konfigurację projektów i stworzyć podstawę infrastruktury aplikacji za pomocą jednej prostej komendy.
  • Twój czas jest ważny, dlatego należy zmniejszać ilość kodu boilerplate pisanego ręcznie. Właśnie do tego służą pakiety serializacji i inne narzędzia do generowania kodu. Miej na uwadze, że muszą być wykorzystywane w określony sposób, żeby stworzyć predefiniowane metody, klasy itp.
  • Bricks są łatwo dostępne. BrickHub to popularny przykład platformy, na której można je znaleźć, ale jest też wiele innych szablonów, z jakich możesz korzystać.
  • Dzięki masonowi wygenerujesz swój własny kod, który odpowiada na Twoje indywidualne potrzeby.

Wyobraź sobie, że budujesz złożoną aplikację we Flutterze. W dużych projektach dobra baza odgrywa kluczową rolę. Nawet jeśli kopiujesz rzeczy z jednego pliku do drugiego, proces ten zajmie Ci dużo czasu. Pomyśl, jak powtarzalne i nudne byłoby to zadanie. Zamiast tego lepiej szybko skorzystać z szablonu. To pozwoli Ci zaoszczędzić cenny czas, który możesz spędzać na robieniu czegoś ciekawego np. animacji.

Wady korzystania z masona

Największym wyzwaniem jest utrzymanie i edycja bricków, która pozwala na dostosowanie ich do konkretnych potrzeb. Dlaczego to stanowi problem? Pisanie kodu z notacją mustache jest trudne z uwagi na błędy, z jakimi z dużym prawdopodobieństwem będziesz się musiał mierzyć. A Twoje IDE nie okaże się szczególnie pomocne. Jedynym rozwiązaniem jest instalacja widżetu, który obsługuje mustache.

Może to prowadzić do trudności w przypadku złożonych implementacji. Przykładowo jeśli zastosujesz warunki do generowania plików, struktura projektu będzie trudna od odczytania. Istnieje szansa, że doprowadzi to do pomyłek podczas dodawania kodów źródłowych przy poprawianiu katalogu.

Jak utworzyć szablon brick?

Masona można zainstalować za pośrednictwem pub.dev lub Homebrew. Po konfiguracji mason jest dostępny jako komenda uruchamiana w terminalu.

# ? Activate from https://pub.dev
dart pub global activate mason_cli
# ? Or install from https://brew.sh
brew tap felangel/mason
brew install mason

Szablony dla bricków składają się z pliku “brick.yaml” i katalogu “brick“, który zawiera kod szablonu. Kiedy go generujesz, mason zbiera wprowadzone zmienne potrzebne w przypadku danego bricka (albo z pomocą prompts, albo jako argumenty z wiersza poleceń). Później wprowadza je do szablonu przed wpisaniem właśnie wygenerowanego kodu na dysk.

Odkąd mason jest narzędziem wykorzystującym wiesz poleceń (CLI), możesz uruchomić go od razu na swoim terminalu.

Zacznij od przykładu z “hello world”. Jest on zawarty poza schematem, kiedy inicjujesz masona. Aby go wypróbować, uruchom “mason make hello” na terminalu. Najpierw zostaniesz poproszony o wprowadzenie danych, żeby podmienić zmienną bricka. Potem mason wygeneruje plik “HELLO.md” w aktualnym katalogu z tekstem “Hello, world!”. To prosty przykład, ale pokazuje, jak możesz wykorzystywać masona, by szybko generować kod z szablonu.

Przykłady zastosowania masona i szablonów aplikacji mobilnych

Aby stworzyć nowy szablon, musisz uruchomić komendę mason new [brick_name] –hooks. Generuje ona serię plików i folder “hooks”. To tutaj ustalasz, czy wolisz, żeby skrypty uruchamiały się przed stworzeniem bricka, czy później.

Folder z brickami zawiera wszystko, co jest związane z szablonem, w tym plik README, LICENCJĘ i CHANGELOG. Umożliwiają one przygotowanie dokładnego opisu i dzielenie się swoimi brickami z innymi (np. przez Brickhub).

Przykładem zastosowania masona jest stworzenie szablonu do budowy modelu flutterowej aplikacji.

Napisanie modelu może być nużące, ponieważ wymaga wykorzystania dużej ilości kodu boilerplate (zwłaszcza, gdy korzystasz z  json_serializable). Następnie trzeba go jeszcze podzielić na warstwę domenową i danych. Dzięki masonowi możesz stworzyć szablon, który generuje niezbędny kod, wliczając w to “import statements” i adnotację @JsonSerializable().

Jak używać masona w praktyce?

Po pierwsze musisz skonfigurować swój szablon. Aby to zrobić, przejdź do pliku brick.yaml.

name: mason_example_model
description: mason article model template
# The following defines the version and build number for your brick.
# A version number is three numbers separated by dots, like 1.2.34
# followed by an optional build number (separated by a +).
version: 0.2.0
# The following defines the environment for the current brick.
# It includes the version of mason that the brick requires.
environment:
 mason: ">=0.1.0-dev.41 <0.1.0"
# Variables specify dynamic values that your brick depends on.
# Zero or more variables can be specified for a given brick.
# Each variable has:
#  * a type (string, number, boolean, enum, or array)
#  * an optional short description
#  * an optional default value
#  * an optional list of default values (array only)
#  * an optional prompt phrase used when asking for the variable
#  * a list of values (enums only)
vars:
 modelName:
   type: string
   description: Model name
default: model
   prompt: Insert the model name

Teraz możesz zacząć pracować nad strukturą swoich plików:

? brick
┣ ? lib
┃ ┣ ? data
┃ ┃ ┗ ? {{modelName.snakeCase()}}_dto.dart
┃ ┣ ? domain
┃ ┃ ┗ ? {{modelName.snakeCase()}}.dart

Po odpowiednim ustrukturyzowaniu katalogu brick oraz po uruchomieniu szablonu z katalogu głównego projektu, stwórz foldery danych i domenowe. Potem dodaj do nich wszystkie modele.

Najpierw przyjrzyj się jednak kwestii .snakeCase(). Jeśli nazewnictwo w Darcie jest ci znane, wiesz pewnie, że pliki powinny mieć nazwy zgodne z zapisem snake case. Aby to osiągnąć (lub zastosować jakikolwiek inny styl zapisu), wykorzystaj wbudowane lambdy, które wysyłają się z masonem. Wszystkie dostępne notacje są w dokumentacji. Kiedy już sobie z tym poradzisz, przejdź do {{modelName.snakeCase()}}_dto.dart.

import 'package:json_annotation/json_annotation.dart';
part '{{modelName.snakeCase()}}_dto.g.dart';
@JsonSerializable()
class {{modelName.pascalCase()}}Dto {
 String id;
 
 {{modelName.pascalCase()}}Dto(
   this.id,
 );
factory {{modelName.pascalCase()}}Dto.fromJson(Map<String, dynamic> json) => _${{modelName.pascalCase()}}DtoFromJson(json);
 
 Map<String, dynamic> toJson() => _${{modelName.pascalCase()}}DtoToJson(this);
}

Podobnie jak w przypadku plików, klasy korzystają z notacji pascalCase(). Dlatego musisz je odpowiednio zmienić.

Jak widzisz, Twój szablon aktualnie zawiera tylko jeden String. Ale możesz dodać cały szereg właściwości jako zmienną dla Twojego bricka! W ten sposób tworzysz też metody pozwalające na szybką serializację.

Po ukończeniu tego zadania, przejdź do implementacji modelu domenowego.

import 'package:equatable/equatable.dart';
 
class {{modelName.pascalCase()}} extends Equatable {
 final String id;
 
 const {{modelName.pascalCase()}}({
   required this.id,
 });
@override
 List<Object> get props => [
       id,
     ];
}

Żeby używać bricka, musisz uruchomić komendę “mason generate [brick_name] [output_path]”. Trzeba tylko wpisać nazwę bricka i pożądaną ścieżkę (lub stworzyć to w aktualnym katalogu). W ten sposób wygenerujesz niezbędne pliki razem z wpisaną wartością uzyskaną z podanych prompts, zgodnie z szablonem aplikacji.

Mason przyspiesza proces tworzenia powtarzalnego kodu. Pomaga też zespołom w utrzymaniu spójności w kodzie. Definiując szablon i wykorzystując go we wszystkich projektach, zyskujesz pewność, że cały kod ma taką samą strukturę i bazuje na tych samych konwencjach.

Ale teraz kod zawiera błędy. Wynika to z faktu, że @JsonSerializable() musi być wygenerowany przez komendę “flutter pub run build_runner build –delete-conflicting-outputs”. I tutaj na scenę wkracza funkcjonalność hooks. To nic innego jak skrypty uruchamiane zanim model zostanie wygenerowany (pre_gen.dart) i po tym zdarzeniu (post_gen.dart). Ten przypadek wymaga od Ciebie uruchomienia komendy generującej kod do serializacji, gdy model jest utworzony.

W takim razie teraz otwórz plik post_gen.dart. Najpierw zobaczysz metodę, która zawiera HookContext. To daje Ci dostęp do zmiennych szablonu oraz takich funkcjonalności, jak mason_logger. Aby uruchomić skrypt w terminalu, po prostu wywołaj Process z pakietu dart:io.

import 'dart:io';
import 'package:mason/mason.dart';
 
void run(HookContext context) async {
 final codeGen = context.logger.progress('Build runner gen in progress');
 await Process.run(
   'flutter',
   ['pub', 'run', 'build_runner', 'build', '--delete-conflicting-outputs'],
   runInShell: true,
 );
 codeGen.complete();

Kiedy teraz wygenerujesz swój szablon, dzięki wywołaniu tego skryptu powinieneś zobaczyć kod pozbawiony błędów.

Jeśli zastanawiasz się, czy rezultat wart jest wysiłku, posłużę się bardziej skomplikowanym przykładem z modelami, które wprowadzą warunki do Twoich szablonów.

Jak realizować złożone projekty dzięki szablonom?

Każdy proces rozwoju oprogramowania – czy to frontend, czy backend – prowadzi do warunków. Aplikacje mobilne mogą zawierać setki ekranów. Aby utrzymać kod w dobrym stanie, w celu dostarczenia przejrzystego UI należy podzielić pliki w określony sposób.

Struktura kodu w prostej aplikacji może zawierać kilka folderów. Ale w przypadku złożonych produktów, pliki i katalogi mogą się mnożyć. Jeśli nie działasz zgodnie z ustalonym wcześniej porządkiem, pewne elementy mogą się zagubić.

Spójrz na przykład z warstwą domenową i danych. Dodaj kilka folderów, żeby przeskalować złożoność i opcje do budowy modeli służących osobno do wysyłania zapytań i odbioru odpowiedzi. W warstwie domenowej zdecyduj, czy chcesz tworzyć odrębny folder (np. ekran zawierający kilka modeli).

Zacznij od stworzenia struktury pliku np. dla klasy użytkowników. Musisz wykorzystać warunki, by szablon sam ustalił, czy istnieje potrzeba wygenerowania plików, czy nie. Mason radzi sobie z warunkami za pomocą zmiennych boolean, więc kolejny krok to dodanie tych warunków do brick.yaml.

createDirectory:
   type: boolean
   description: Indicator if model has it's own directory
   default: false
   prompt: Do you want to create directory for model?
 createToJson:
   type: boolean
   description: Indicator if model has toJson() method
default: false
   prompt: Do you want to create toJson() for model?
 createFromJson:
   type: boolean
   description: Indicator if model has fromJson() method
   default: false
   prompt: Do you want to create fromJson() for model?

Następnie przejdź do projektu i zacznij pracę nad składnią warunkową {{#statement}}fileName.dart{{/statement}}. Jedyna wada tego rozwiązania polega na tym, że notacja mustache wykorzystuje ‘/’ jako zamknięcie dla warunku. To jednocześnie informuje Twój system plików, że powinien przejść do folderów. W IDE będzie się to wyświetlać w taki sposób:

? brick
 ┗ ? {{#statement}}fileName.dart{{
 ┃ ┗ ? statement}}

Jeśli warunek jest prawdziwy, generator stworzy właściwy plik w postaci:

? Output
 ┗ ? fileName.dart

Z tego powodu radzę dodać diagram z drzewem plików w README do każdego szablonu, jaki tworzysz. To rozwiązanie pomoże też innym programistom, którzy korzystają z Twojego szablonu. Wtedy otrzymasz rezultat podobny do tego:

? brick
 ┗ ? {{#statement}}fileName.type{{/statement}}

Rozwiązanie dla złożonych modeli

? __brick__
 ┣ ? lib
 ┃ ┣ ? communication
 ┃ ┃ ┣ ? network
 ┃ ┃ ┃ ┣ ? dto
 ┃ ┃ ┃ ┃ ┣ ? {{modelName.snakeCase()}}
 ┃ ┃ ┃ ┃ ┃ ┣ ? {{#createToJson}}requests{{/createToJson}}
 ┃ ┃ ┃ ┃ ┃ ┃ ┗ ? {{modelName.snakeCase()}}_request_dto.dart 
 ┃ ┃ ┃ ┃ ┃ ┣ ? {{#createFromJson}}responses{{/createFromJson}}
 ┃ ┃ ┃ ┃ ┃ ┃ ┗ ? {{modelName.snakeCase()}}_response_dto.dart
 ┃ ┣ ? domain
 ┃ ┃ ┣ ? models
 ┃ ┃ ┃ ┣ ? {{#createDirectory}}{{modelName.snakeCase()}}{{/createDirectory}}
 ┃ ┃ ┃ ┃ ┗ ? {{modelName.snakeCase()}}.dart
 ┃ ┃ ┃ ┗ ? {{^createDirectory}}{{modelName.snakeCase()}}.dart{{/createDirectory}}

Musisz wiedzieć, że znaki # i ^ wskazują, czy warunek jest prawdziwy, czy fałszywy. Katalogi z plikami zapytań i odpowiedzi zostaną stworzone tylko wtedy, gdy powiązane booleans są prawdziwe. Z drugiej strony modele domenowe będą stworzone w każdym przypadku – albo w osobnym katalogu, albo w modelach.

Kiedy jeszcze warto używać masona?

Z masona możesz też korzystać, by generować inne typy kodu czy komponenty w aplikacji flutterowej. Z jego pomocą stworzysz np. szablon dla niestandardowego widżetu, który zawiera niezbędne warunki importu oraz kod dla konstruktorów, właściwości i metod. W ten sposób oszczędzasz czas i zmniejszasz ilość powtarzalnego kodu, jaki trzeba napisać dla każdego widżetu.

Innym sposobem na wykorzystanie masona jest stworzenie szablonu strony albo ekranu w aplikacji. Może zawierać niezbędne warunki importu albo kod layoutu strony, widżetów czy funkcjonalności. Jest to szczególnie zalecane w przypadku stron, które mają podobną strukturę lub podobne elementy, takie jak strona logowania albo ekran ustawień.

Możesz też użyć masona do generowania kodu popularnych funkcji aplikacji, takich jak networking, lokalizowanie czy zarządzanie stanem.

To sposób na zaoszczędzenie sporej ilości czasu, ponieważ nie ma potrzeby pisania tego samego kodu wielokrotnie. Może to być użyteczne zwłaszcza w przypadku integracji Firebase’a lub innych złożonych layoutów, które zawierają odtwarzacze wideo lub audio (i są np. stosowane w przypadku podcastów).

Wnioski

Mason daje szeroki zakres możliwości w zakresie generowania kodu aplikacji flutterowej. Tworząc spersonalizowane szablony i stosując je w różnych projektach, zespoły oszczędzają czas i zyskują pewność, że kod jest spójny. W ten sposób ulepszają proces rozwoju produktu i zwiększają jakość ostatecznego rozwiązania.

Narzędzie mason_cli pozwala też specjalistom tworzyć i stosować ich własne szablony zwane brickami z wykorzystaniem notacji szablonu mustache.

To rozwiązanie zdecydowanie warte jest Twojej uwagi jeśli chcesz lepiej spożytkować czas przeznaczony na projekt. Masz jeszcze jakieś pytania związane z masonem i szablonami? Zadaj je w komentarzu.

Marcel - Flutter Developer

Marcel Kozień

Flutter Developer, którego pasjonuje tworzenie wydajnego oprogramowania. Wierzy, że w przypadku rozwoju aplikacji mniej znaczy więcej. Kiedy nie pisze kodu, ogląda dobre filmy, buduje konstrukcje z Lego do swojej coraz bogatszej kolekcji i odkrywa nowe miejsca dzięki podróżom.

Dowiedz się więcej

Wycena projektu

Opowiedz nam o swoim projekcie i napisz, jak możemy Ci pomóc.

Dlaczego warto rozwijać z nami projekty?

Logo Mobile Trends Awards

Mobile Trends Awards 2021

Wygrana w kategorii
ŻYCIE CODZIENNE

Nagroda Legalnych Bukmacherów

Nagroda Legalnych Bukmacherów 2019

Najlepsza aplikacja mobilna

Mobile Trends Awards logo

Mobile Trends Awards 2023

Wygrana w kategorii
MCOMMERCE ROZWÓJ

23

opinie klientów

Clutch logo