lexiolab 0.1.0
lexiolab: ^0.1.0 copied to clipboard
LexioLab client SDK for Flutter — manifest polling, content-addressed bundle caching, runtime ICU formatting, and label lookup that never blocks a frame.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:lexiolab/lexiolab_flutter.dart';
import 'config.dart';
import 'sections/language_picker.dart';
import 'sections/plurals.dart';
import 'sections/rtl_screen.dart';
import 'sections/status_panel.dart';
import 'snapshot.dart';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return LexioLabScope(
config: LexioLabConfig(
org: llOrg,
project: llProject,
env: llEnvironment,
baseUrl: llBaseUrl,
// Required. The SDK never guesses a language.
language: 'en',
// Required. There is no "no content yet" state, because that state is
// a blank screen.
embedded: exampleSnapshot,
segments: <String>['checkout'],
// The demo would be dull on the default `nextLaunch`: a release that
// replaces text already on screen is withheld until the next launch,
// which is right for a real app and invisible in a demo.
applyMode: LLApplyMode.live,
mode: LLMode.development,
),
pollInterval: const Duration(seconds: 15),
child: const _Shell(),
);
}
}
class _Shell extends StatelessWidget {
const _Shell();
@override
Widget build(BuildContext context) {
// Reading the language aspect here is what makes a language switch rebuild
// MaterialApp — and a segment load or a release swap not touch it.
final LLBindings bindings = LL.bindingsOf(context);
return MaterialApp(
title: 'LexioLab',
debugShowCheckedModeBanner: false,
locale: bindings.locale,
supportedLocales: bindings.supportedLocales,
localizationsDelegates: <LocalizationsDelegate<Object?>>[
// The LexioLab delegate first; the globals supply the framework's own
// strings for the same locale.
...bindings.delegates,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF3B5BDB)),
useMaterial3: true,
),
home: const _Home(),
);
}
}
class _Home extends StatelessWidget {
const _Home();
@override
Widget build(BuildContext context) {
return LLSegmentScope(
segments: const <String>['checkout'],
// The app shell keeps its own direction — the SDK forces nothing.
child: Scaffold(
appBar: AppBar(title: const Text('LexioLab SDK')),
body: ListView(
padding: const EdgeInsets.all(16),
children: const <Widget>[
LanguagePicker(),
SizedBox(height: 24),
RtlScreen(),
SizedBox(height: 24),
Plurals(),
SizedBox(height: 24),
StatusPanel(),
],
),
),
);
}
}
/// Shared section chrome, so each section file is only about its own point.
class Section extends StatelessWidget {
const Section(
{super.key, required this.title, required this.children, this.note});
final String title;
final List<Widget> children;
final String? note;
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(title, style: Theme.of(context).textTheme.titleMedium),
if (note != null) ...<Widget>[
const SizedBox(height: 4),
Text(note!, style: Theme.of(context).textTheme.bodySmall),
],
const SizedBox(height: 12),
...children,
],
),
),
);
}
}