Mobilni razvoj
FlutterMaterial 3Dizajnerski sustavTematizacijaRazvoj mobilnih aplikacijaTamni način radaTipografija

Flutter dizajnerski sustav i tematizacija u 2026.: Material 3, dinamičke boje, tipografija i tamni način rada

AO
Adrijan Omićević
·14 min čitanja

# Što ćete izgraditi#

Skalabilan, na featuree otporan setup za tematizaciju Flutter dizajnerskog sustava uz Material 3, uključujući:

  • Arhitekturu temeljenu na tokenima za boje, tipografiju, razmake i radijuse
  • Material 3 ColorScheme i tematizaciju komponenti
  • Dinamičke boje na podržanim Android verzijama uz sigurne fallbacke
  • Predvidljivu strategiju za tamni način rada
  • Prakse predaje dizajna developerima koje smanjuju rework

Ako organizirate rastuću aplikaciju po featureima, uskladite ovaj vodič s feature-first strukturom poput Arhitektura Flutter aplikacije: Clean Architecture + feature-first. Tema treba biti zajednički temelj, a ne nešto što svaki feature iznova izmišlja.

# Zašto je ovo važno u stvarnim aplikacijama#

Tematizacija nije samo “da izgleda lijepo”. Utječe na brzinu razvoja i bugove:

  • Nedosljednost UI-ja raste linearno s veličinom tima ako nema zajedničkog sloja tokena. Dva developera mogu napraviti dva “skoro identična” gumba koji se razlikuju u paddingu, radijusu ili boji, a to nećete primijetiti do kasnog QA-a.
  • Flutter olakšava lokalno overrideanje stila, što je kratkoročno praktično, a dugoročno skupo.
  • Material 3 je više opinionated. Ako se odlučite za njega, dobivate dosljedne default vrijednosti, bolje obrasce pristupačnosti i model tematizacije komponenti koji smanjuje per-widget boilerplate.

🎯 Ključna poruka: Skalabilna tema je proizvod arhitekture: tokeni + teme komponenti + provedba pravila.

# Preduvjeti#

ZahtjevVerzijaNapomene
Flutter3.22+Podrška za Material 3 je zrela; ažurirajte na najnoviji stable ako je moguće
Dart3.4+Za moderne jezične značajke i bolju podršku analyzera
Material 3UključenPostavite useMaterial3: true u ThemeData
Android12+Potrebno za dinamičke boje; i dalje osigurajte fallback schemeove
Osnovni artefakti dizajnerskog sustavaPaleta boja, tipografska skala, skala razmaka iz dizajna

# Arhitektura teme: prvo tokeni, zatim ThemeData#

Dobra arhitektura Flutter teme razdvaja odgovornosti:

  1. 1
    Tokeni definiraju sirove vrijednosti i semantička značenja.
  2. 2
    Kompozicija teme gradi ThemeData iz tokena.
  3. 3
    Tematizacija komponenti standardizira widgete bez lokalnih overridea.
  4. 4
    Korištenje u featureima je read-only: featurei koriste temu, nikad je ne definiraju.

Preporučena struktura foldera#

Za srednju do veliku aplikaciju stavite dizajnerski sustav u vlastiti package unutar repozitorija (ili u zaseban package ako dijelite između aplikacija).

SlojPrimjer putanjeOdgovornost
Tokenipackages/ui_kit/lib/src/tokens/Boje, tipografija, razmaci, radijusi, elevation
Builderi temapackages/ui_kit/lib/src/theme/Izgradnja ThemeData za light i dark
Komponentepackages/ui_kit/lib/src/components/Gumbi, inputi, app barovi, kartice
Javni APIpackages/ui_kit/lib/ui_kit.dartExportajte samo stabilna sučelja

Ovo održava featuree čistima i sprječava kružne ovisnosti kako aplikacija raste.

💡 Savjet: Tokene učinite nepromjenjivima i izbjegavajte uvlačenje BuildContext u klase tokena. Tokeni trebaju biti čisti podaci, lako testabilni i sigurni za reuse u background isolateovima ako zatreba.

# Korak 1: Definirajte dizajnerske tokene (boje, razmaci, radijus)#

Tokeni su ugovor između dizajna i koda. Tretirajte ih kao stabilan API.

Tokeni boja: brand i semantika#

Treba vam dvije razine:

  • Tokeni palete: sirove boje poput brand plave ili neutralne sive ljestvice
  • Semantički tokeni: boje temeljene na značenju poput success, warning, onSurfaceMuted

U Material 3, semantički sloj se uglavnom izražava kroz ColorScheme. Ipak, korisno je zadržati eksplicitne semantičke tokene za ne-Material koncepte, poput grafikona ili statusnih oznaka (badges).

Dart
// packages/ui_kit/lib/src/tokens/app_palette.dart
import 'package:flutter/material.dart';
 
class AppPalette {
  AppPalette._();
 
  // Brand
  static const brandBlue = Color(0xFF2563EB);
  static const brandBlueDark = Color(0xFF1D4ED8);
 
  // Neutrals
  static const neutral0 = Color(0xFFFFFFFF);
  static const neutral900 = Color(0xFF0B1220);
 
  // Status
  static const success = Color(0xFF16A34A);
  static const warning = Color(0xFFF59E0B);
  static const danger = Color(0xFFDC2626);
}
Dart
// packages/ui_kit/lib/src/tokens/app_semantic_colors.dart
import 'package:flutter/material.dart';
import '../tokens/app_palette.dart';
 
class AppSemanticColors {
  final Color success;
  final Color warning;
  final Color danger;
 
  const AppSemanticColors({
    required this.success,
    required this.warning,
    required this.danger,
  });
 
  static const light = AppSemanticColors(
    success: AppPalette.success,
    warning: AppPalette.warning,
    danger: AppPalette.danger,
  );
 
  static const dark = AppSemanticColors(
    success: AppPalette.success,
    warning: AppPalette.warning,
    danger: AppPalette.danger,
  );
}

Tokeni razmaka: koristite skalu, ne nasumične brojeve#

Drift razmaka je jedan od najbržih načina da izgubite dosljednost. Koristite fiksnu skalu, obično baziranu na 4 ili 8. Material smjernice se često usklađuju s inkrementima od 4 dp.

Dart
// packages/ui_kit/lib/src/tokens/app_spacing.dart
class AppSpacing {
  AppSpacing._();
 
  static const double xs = 4;
  static const double sm = 8;
  static const double md = 12;
  static const double lg = 16;
  static const double xl = 24;
  static const double xxl = 32;
  static const double xxxl = 40;
}

Tokeni radijusa: neka set bude malen#

Previše radijusa znači vizualni šum. Odaberite 3 do 5.

Dart
// packages/ui_kit/lib/src/tokens/app_radii.dart
class AppRadii {
  AppRadii._();
 
  static const double sm = 8;
  static const double md = 12;
  static const double lg = 16;
  static const double pill = 999;
}

⚠️ Upozorenje: Nemojte izlagati sirovi BorderRadius.circular(13) u feature kodu. Ako radijus nije token, završit ćete s desecima jedinstvenih zaobljenja i nedosljednim “osjećajem” sučelja.

# Korak 2: Tipografski sustav (Material 3 + vaša skala)#

Tipografija treba biti i sustavna i semantička.

  • Sustavna znači: definirana skala, dosljedne veličine i line-height.
  • Semantička znači: namjena upotrebe poput title, body, label, a ne “18 bold”.

Definirajte tipografsku skalu#

Material 3 TextTheme uključuje stilove poput displayLarge, headlineMedium, titleLarge, bodyMedium, labelLarge. Mapirajte svoju dizajnersku skalu na njih.

Dart
// packages/ui_kit/lib/src/tokens/app_typography.dart
import 'package:flutter/material.dart';
 
class AppTypography {
  AppTypography._();
 
  static TextTheme textTheme({
    required String fontFamily,
    required Brightness brightness,
  }) {
    final base = brightness == Brightness.dark
        ? Typography.material2021().white
        : Typography.material2021().black;
 
    return base.copyWith(
      titleLarge: base.titleLarge?.copyWith(
        fontFamily: fontFamily,
        fontWeight: FontWeight.w700,
        fontSize: 22,
        height: 28 / 22,
      ),
      bodyMedium: base.bodyMedium?.copyWith(
        fontFamily: fontFamily,
        fontWeight: FontWeight.w400,
        fontSize: 16,
        height: 24 / 16,
      ),
      labelLarge: base.labelLarge?.copyWith(
        fontFamily: fontFamily,
        fontWeight: FontWeight.w600,
        fontSize: 14,
        height: 20 / 14,
        letterSpacing: 0.2,
      ),
    );
  }
}

Provedite semantičku upotrebu#

Izbjegavajte ad-hoc kreiranje stilova unutar featurea. Umjesto toga koristite stilove iz teme:

Dart
Text(
  'Payment method',
  style: Theme.of(context).textTheme.titleLarge,
)

Ako često trebate specifičnu varijantu (npr. “muted body”), napravite malu ekstenziju, a ne novi TextStyle posvuda.

Dart
// packages/ui_kit/lib/src/theme/text_theme_ext.dart
import 'package:flutter/material.dart';
 
extension AppTextThemeX on TextTheme {
  TextStyle? get bodyMuted => bodyMedium?.copyWith(
        color: color?.withValues(alpha: 0.72),
      );
}

Koristite ovako:

Dart
Text(
  'Updated 2 hours ago',
  style: Theme.of(context).textTheme.bodyMuted,
)

ℹ️ Napomena: Flutterov TextTheme po defaultu ne nosi boju. Mnogi timovi se oslanjaju na DefaultTextStyle i ColorScheme.onSurface. Ako koristite obrasce copyWith(color: ...), osigurajte da ostanu sigurni za kontrast u tamnom načinu rada.

# Korak 3: Material 3 ColorScheme, svijetli i tamni#

Material 3 tematizacija u Flutteru treba biti vođena ColorScheme-om plus temama komponenti. Krenite s brand seed bojom kako biste generirali konzistentne tonalne palete.

Izgradite ColorSchemeove#

Dart
// packages/ui_kit/lib/src/theme/app_color_schemes.dart
import 'package:flutter/material.dart';
import '../tokens/app_palette.dart';
 
class AppColorSchemes {
  AppColorSchemes._();
 
  static final ColorScheme light = ColorScheme.fromSeed(
    seedColor: AppPalette.brandBlue,
    brightness: Brightness.light,
  );
 
  static final ColorScheme dark = ColorScheme.fromSeed(
    seedColor: AppPalette.brandBlueDark,
    brightness: Brightness.dark,
  );
}

Ovo vam daje punu shemu: primary, secondary, surface, error i sve on-colors.

Brand overridei za kritične role#

Većini stvarnih brendova treba nekoliko eksplicitnih ograničenja. Overrideajte pažljivo kako ne biste narušili kontrast.

Dart
final light = AppColorSchemes.light.copyWith(
  primary: AppPalette.brandBlue,
);
 
final dark = AppColorSchemes.dark.copyWith(
  primary: AppPalette.brandBlueDark,
);

Praktično pravilo: overrideajte primary i možda tertiary, pa pustite Materialu da izračuna ostalo osim ako vaš dizajnerski sustav eksplicitno definira svaku rolu.

# Korak 4: Dinamičke boje na Androidu uz sigurne fallbacke#

Dinamičke boje prilagođavaju shemu vaše aplikacije korisnikovoj pozadini (wallpaper) na Androidu 12+. To može poboljšati dojam “native” aplikacije i smanjiti vizualno trenje.

Cilj nije “dinamično posvuda”. Cilj je “dinamično kad je dostupno, predvidljivo inače”.

Obrazac implementacije#

Ako koristite package dynamic_color, možete dohvatiti dinamičke sheme i imati fallback.

Dart
// app/lib/app.dart
import 'package:dynamic_color/dynamic_color.dart';
import 'package:flutter/material.dart';
import 'package:ui_kit/ui_kit.dart';
 
class AppRoot extends StatelessWidget {
  const AppRoot({super.key});
 
  @override
  Widget build(BuildContext context) {
    return DynamicColorBuilder(
      builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
        final theme = AppTheme.build(
          lightDynamic: lightDynamic,
          darkDynamic: darkDynamic,
        );
 
        return MaterialApp(
          theme: theme.light,
          darkTheme: theme.dark,
          themeMode: ThemeMode.system,
          home: const Placeholder(),
        );
      },
    );
  }
}

A u UI kitu:

Dart
// packages/ui_kit/lib/src/theme/app_theme.dart
import 'package:flutter/material.dart';
import '../tokens/app_semantic_colors.dart';
import 'app_color_schemes.dart';
import '../tokens/app_typography.dart';
 
class AppThemeBundle {
  final ThemeData light;
  final ThemeData dark;
 
  const AppThemeBundle({required this.light, required this.dark});
}
 
class AppTheme {
  AppTheme._();
 
  static AppThemeBundle build({
    ColorScheme? lightDynamic,
    ColorScheme? darkDynamic,
  }) {
    final lightScheme = lightDynamic ?? AppColorSchemes.light;
    final darkScheme = darkDynamic ?? AppColorSchemes.dark;
 
    return AppThemeBundle(
      light: _theme(brightness: Brightness.light, scheme: lightScheme),
      dark: _theme(brightness: Brightness.dark, scheme: darkScheme),
    );
  }
 
  static ThemeData _theme({
    required Brightness brightness,
    required ColorScheme scheme,
  }) {
    final textTheme = AppTypography.textTheme(
      fontFamily: 'Inter',
      brightness: brightness,
    );
 
    return ThemeData(
      useMaterial3: true,
      brightness: brightness,
      colorScheme: scheme,
      textTheme: textTheme,
      extensions: <ThemeExtension<dynamic>>[
        _SemanticColorsExt(
          colors: brightness == Brightness.dark
              ? AppSemanticColors.dark
              : AppSemanticColors.light,
        ),
      ],
    );
  }
}
 
class _SemanticColorsExt extends ThemeExtension<_SemanticColorsExt> {
  final AppSemanticColors colors;
  const _SemanticColorsExt({required this.colors});
 
  @override
  _SemanticColorsExt copyWith({AppSemanticColors? colors}) =>
      _SemanticColorsExt(colors: colors ?? this.colors);
 
  @override
  _SemanticColorsExt lerp(ThemeExtension<_SemanticColorsExt>? other, double t) =>
      this;
}

Ovaj obrazac drži dinamičke boje zatvorene unutar kompozicije teme. Featurei nastavljaju čitati ColorScheme i semantičke tokene kroz theme extensions.

💡 Savjet: Dodajte debug-only toggle “force dynamic” i “force fallback”. Tako ćete rano uloviti probleme s kontrastom bez potrebe da svaki developer ima fizički Android 12 uređaj.

# Korak 5: Tematizacija komponenti (gumbi, inputi, AppBar, kartice)#

Tematizacija komponenti je mjesto gdje dobivate dosljednost. Pravilo je jednostavno: ako se komponenta koristi 10+ puta, dajte joj temu komponente ili UI kit wrapper.

Tematizacija gumba#

Dart
// packages/ui_kit/lib/src/theme/component_themes.dart
import 'package:flutter/material.dart';
import '../tokens/app_radii.dart';
import '../tokens/app_spacing.dart';
 
class AppComponentThemes {
  AppComponentThemes._();
 
  static ElevatedButtonThemeData elevated(ColorScheme scheme, TextTheme text) {
    return ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        minimumSize: const Size(0, 48),
        padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(AppRadii.md),
        ),
        textStyle: text.labelLarge,
        backgroundColor: scheme.primary,
        foregroundColor: scheme.onPrimary,
      ),
    );
  }
 
  static InputDecorationTheme input(ColorScheme scheme) {
    return InputDecorationTheme(
      filled: true,
      fillColor: scheme.surfaceContainerHighest,
      border: OutlineInputBorder(
        borderRadius: BorderRadius.circular(AppRadii.md),
        borderSide: BorderSide(color: scheme.outlineVariant),
      ),
      enabledBorder: OutlineInputBorder(
        borderRadius: BorderRadius.circular(AppRadii.md),
        borderSide: BorderSide(color: scheme.outlineVariant),
      ),
      focusedBorder: OutlineInputBorder(
        borderRadius: BorderRadius.circular(AppRadii.md),
        borderSide: BorderSide(color: scheme.primary, width: 2),
      ),
      contentPadding: const EdgeInsets.symmetric(
        horizontal: AppSpacing.lg,
        vertical: AppSpacing.md,
      ),
    );
  }
}

Ukomponirajte u ThemeData:

Dart
return ThemeData(
  useMaterial3: true,
  colorScheme: scheme,
  textTheme: textTheme,
  elevatedButtonTheme: AppComponentThemes.elevated(scheme, textTheme),
  inputDecorationTheme: AppComponentThemes.input(scheme),
);

Kada koristiti wrappere umjesto tema#

Teme komponenti ne pokrivaju svaku potrebu dizajnerskog sustava. Koristite UI kit widgete kada:

  • Trebate API za varijante poput AppButton(variant: primary, size: md)
  • Trebate dosljednost layouta, ne samo boje i oblike
  • Želite spriječiti “kreativno” stiliziranje u feature kodu

Wrappere držite tankima. Omotajte Material komponente, ne re-implementirajte ih.

# Korak 6: Dosljedan tamni način rada bez iznenađenja#

Tamni način rada nije “invertiraj boje”. To je zasebna shema s drugačijim ponašanjem površina.

Praktična pravila koja sprječavaju bugove#

  1. 1
    Uvijek postavite i theme i darkTheme u MaterialApp.
  2. 2
    Izbjegavajte hard-coded boje u featureima, posebno Colors.black i Colors.white.
  3. 3
    Preferirajte colorScheme.surface, surfaceContainer, onSurface, outlineVariant.
  4. 4
    Validirajte uobičajene ekrane u oba moda golden testovima.

⚠️ Upozorenje: Česta zamka je korištenje Theme.of(context).primaryColor. U Material 3, preferirajte Theme.of(context).colorScheme.primary. primaryColor može dati neočekivane rezultate kada se oslanjate na generirane sheme.

Provjere kontrasta koje hvataju stvarne probleme#

Timovi često kasno otkriju probleme s kontrastom u tamnom načinu rada. Uhvatite ih rano:

  • Osigurajte da tekst na surface koristi onSurface
  • Osigurajte da disabled stanja ostanu čitljiva
  • Osigurajte da obrubi i separatori ne nestanu u tamnom načinu rada

Material 3 sheme su dizajnirane za kontrast, ali custom overridei ga mogu pokvariti.

# Korak 7: Dosljedni razmaci i layout kroz featuree#

Flutter tematizacija ne pokriva razmake kao što web pokriva CSS varijable. Trebate jednostavan obrazac:

  • Definirajte AppSpacing tokene
  • Koristite ih svugdje
  • Izbjegavajte EdgeInsets.all(10) u feature kodu

Za često korištene layoute, definirajte primitive poput AppSection ili AppPagePadding kao wrappere u UI kitu. Time uklanjate ponavljajuću logiku paddinga iz featurea i održavate vizualno poravnanje ekrana.

Dart
// packages/ui_kit/lib/src/components/app_page.dart
import 'package:flutter/material.dart';
import '../tokens/app_spacing.dart';
 
class AppPage extends StatelessWidget {
  final Widget child;
  const AppPage({super.key, required this.child});
 
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Padding(
        padding: const EdgeInsets.all(AppSpacing.lg),
        child: child,
      ),
    );
  }
}

# Kako održati dosljednost kroz feature timove#

Dosljednost je jednako proces koliko i kod. Ovo funkcionira u produkciji.

Jedna ulazna točka za temu, bez feature overridea#

Featurei ne bi trebali kreirati vlastiti ThemeData. Ako feature treba nešto novo:

  • Dodajte tokene ili temu komponente centralno
  • Pružite wrapper komponentu ako treba
  • Deprecateajte stare obrasce stiliziranja

Ovo se dobro slaže s feature-first setupima i čistim granicama kao u Clean Architecture + feature-first.

Dodajte guardrailove: code review i lint#

Minimalno, provodite:

  • Nema sirovog Color(...) u feature UI-ju
  • Nema direktnog TextStyle(...) osim ako je opravdano
  • Nema nasumičnih vrijednosti razmaka

Realno ne možete sve blokirati linterom, ali možete učiniti prekršaje rijetkima uz konvencije i checklistu za review.

# Predaja dizajna developerima: što tražiti#

Najbrži način da smanjite churn u implementaciji dizajna je dogovoriti očekivane izlaze.

Checklist za handoff#

ArtefaktMinimumZašto je važno
Uloge bojaMapiranje na Material 3 roleSmanjuje nejasnoće tipa “koja je ovo siva”
Tipografska skalaVeličine, debljine, line-heightSprječava one-off stilove teksta
Skala razmakaTokeni bazirani na 4 ili 8Drži layoute poravnatima kroz ekrane
Specifikacije komponentiStanja za hover, pressed, disabled, errorSprječava nedosljedne interakcije
Tamni način radaEksplicitna dark shema ili pravilaSprječava kasne popravke kontrasta

Mapirajte dizajnerske tokene na kod tokene#

Dogovorite nazive unaprijed. Ako dizajner koristi Space/16, preslikajte to kao AppSpacing.lg = 16. Ako koristi Radius/12, preslikajte kao AppRadii.md = 12.

Kad se timovi usklade oko naziva, code review postaje trivijalan, a novi developeri se brže onboardaju.

ℹ️ Napomena: Ako vaš dizajn tim koristi Figma varijable, razmislite o izvozu tokena u JSON i generiranju Dart konstanti. Čak je i ručni “single source of truth” spreadsheet bolji od ad-hoc kopiranja vrijednosti u kod.

# Performanse i animacije: tema ne smije raditi protiv vas#

Teški rebuildovi teme mogu se pokazati kao jank kad su upareni sa složenim ekranima i animacijama. Kreiranje teme držite centralizirano i immutable, te izbjegavajte rebuildanje ThemeData na svakom frameu.

Ako lovite glatkoću, kombinirajte čistu tematizaciju s praksama performansi iz Optimizacija performansi u Flutteru za 60fps.

Ako vaš dizajnerski sustav uključuje motion, i tokeni za animacije trebaju biti dosljedni. Ne dopustite da svaki feature bira vlastite trajanja i krivulje (curves). Za dublje obrasce animacija, pogledajte Vodič za Flutter animacije: implicit, explicit, Rive, Lottie.

# Uobičajene zamke (i kako ih izbjeći)#

  1. 1

    Hard-coded boje u feature widgetima
    Koristite ColorScheme i semantičke theme extensions. Ako potrebna boja ne postoji, dodajte token.

  2. 2

    Ad-hoc tipografija
    Mapirajte sve na TextTheme. Dodajte malu ekstenziju za ponavljane varijante.

  3. 3

    Drift razmaka
    Provodite skalu. Kreirajte male layout primitive poput AppPage za standardizaciju paddinga.

  4. 4

    Stiliziranje komponenti razvučeno po codebaseu
    Centralizirajte u temama komponenti ili UI kit wrapperima. Izbjegavajte lokalne styleFrom overridee posvuda.

  5. 5

    Dinamičke boje narušavaju identitet brenda
    Dinamičko držite opcionalnim. Po potrebi overrideajte kritične brand role i testirajte kontrast.

# Ključne poruke#

  • Definirajte sloj tokena za boje, tipografiju, razmake i radijuse i tretirajte ga kao stabilan API.
  • Gradite ThemeData iz tokena i centralizirajte tematizaciju komponenti kako biste eliminirali per-widget overridee.
  • Koristite dinamičke boje kao opcionalni izvor ColorScheme-a uz robustan brand fallback.
  • Provedite semantičku tipografiju kroz TextTheme i male ekstenzije, ne ad-hoc TextStyle.
  • Učinite tamni način rada pouzdanim izbjegavanjem hard-coded boja i validacijom oba moda golden testovima.
  • Uskladite handoff dizajn-dev oko naziva temeljenih na rolama i zajedničke mape tokena kako biste smanjili rework.

# Zaključak#

Skalabilan setup za tematizaciju Flutter dizajnerskog sustava manje je pitanje odabira “pravih boja”, a više stvaranje ponovljivog pipelinea: tokeni → kompozicija teme → teme komponenti, provedeno kroz sve featuree. Material 3 i dinamičke boje mogu učiniti da vaša aplikacija djeluje više native, ali samo ako zadržite jake brand fallbacke i tamni način rada siguran za kontrast.

Ako želite pomoć u implementaciji UI kita temeljenog na tokenima, migraciji na Material 3 ili standardizaciji tematizacije kroz feature-first codebase, Samioda može auditirati vaš trenutni UI sloj i isporučiti produkcijski spreman temelj dizajnerskog sustava. Javite se i mapirat ćemo vaše tokene, arhitekturu teme i strategiju komponenti na vaš roadmap.

FAQ

Share
A
Adrijan OmićevićOsnivač i senior developer

Osnivač i senior developer u Samiodi. 8+ godina iskustva u izradi React, Next.js, Flutter i n8n rješenja za klijente diljem Europe.

Više iz kategorije Mobilni razvoj

Sve

Trebate pomoć s projektom?

Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.