DocsFlutter6 min read

Flutter

siyak_flutter keeps your source string in the widget tree and lets the server learn its translation. No key tables, no codegen, no Intl ceremony — and the first frame never waits on the network.

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  siyak_flutter:
    path: ../vendor/siyak_flutter    # the folder from the downloaded archive

# pub.dev hosting is announced later; until then the archive is the install path.
lib/main.dart
import 'package:siyak_flutter/siyak_flutter.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  final siyak = Siyak.init(
    apiKey: const String.fromEnvironment('SIYAK_API_KEY'),
    endpoint: const String.fromEnvironment('SIYAK_ENDPOINT'),
    target: 'ar-EG',
    source: 'en',
    context: 'home screen',
    onLog: debugPrint,
  );

  unawaited(siyak.preload());   // one request, every approved string
  runApp(MyApp(siyak: siyak));
}

The rebuild pattern

A cached getter is useless if nothing repaints when the translation lands, so the client exposes two ValueNotifiers: translationRevision fires whenever a new string enters the cache, and lastError fires with the service's error code — or with null when it recovers.

class _HomeState extends State<Home> {
  final siyak = Siyak.shared;

  @override
  void initState() {
    super.initState();
    siyak.translationRevision.addListener(_repaint);
  }

  void _repaint() => setState(() {});

  @override
  void dispose() {
    siyak.translationRevision.removeListener(_repaint);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => Text(siyak.t('Settings'));
}

Right-to-left

if (siyak.isRightToLeft) {            // ar, fa, he, ur, ckb, … (region-insensitive)
  // Flip the whole app, not one Text.
  return Directionality(textDirection: TextDirection.rtl, child: ...);
}
  • isRtlTag("ar-EG") is true; the check is by primary subtag, so a region never breaks it.
  • warmUp(listOfStrings) before a screen you know is heavy — it batches and resolves, so nothing paints twice in the source language.
  • The cache persists through shared_preferences and is a 30-day LRU of 1000 entries by default; in a pure-Dart test with no platform registrar it degrades to memory instead of throwing.
  • Errors are typed: QuotaExceededException, RateLimitedException, InvalidApiKeyException, OriginNotAllowedException, BackendUnavailableException, SiyakNetworkException. t() still never throws — they surface through lastError and onLog.

Running against a local server

flutter run --dart-define=SIYAK_ENDPOINT=http://10.0.2.2:8787 \
            --dart-define=SIYAK_API_KEY=sk_test_…
// and pass allowInsecureEndpoint: true to Siyak.init — it refuses plain http
// by default, because a release build pointing at http:// is almost always a bug.