Slide 1

Slide 1 text

How to make your Flutter app accessible Vogel Csongor Lead Android and Flutter Developer GerfalconVogel gerfalcon

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Android project

Slide 4

Slide 4 text

iPhone project

Slide 5

Slide 5 text

Flutter project

Slide 6

Slide 6 text

Flutter project Hot Reload

Slide 7

Slide 7 text

Users by Country

Slide 8

Slide 8 text

Users by Country

Slide 9

Slide 9 text

Users by Country

Slide 10

Slide 10 text

Users by Country

Slide 11

Slide 11 text

Users by Country

Slide 12

Slide 12 text

?

Slide 13

Slide 13 text

Solutions l10n i18n a11y

Slide 14

Slide 14 text

Solutions i18n a11y l10n

Slide 15

Slide 15 text

Built-in solution i18n l10n

Slide 16

Slide 16 text

i18n l10n dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter TODO: 1 - Add flutter_localizations dependency. pubspec.yaml

Slide 17

Slide 17 text

app.dart i18n l10n TODO: 2 - Add delegates and supported locales to the App. MaterialApp( home: const HomePage(), localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: [ const Locale('en', ''), const Locale('hu', ''), ], );

Slide 18

Slide 18 text

app.dart i18n l10n TODO: 2 - Add delegates and supported locales to the App. MaterialApp( home: const HomePage(), localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: [ const Locale('en', ''), const Locale('hu', ''), ], ); 78 languages

Slide 19

Slide 19 text

i18n l10n .xml XLIFF files .xcloc .strings .json .ARB

Slide 20

Slide 20 text

i18n l10n .ARB { "@@locale": "en", "homeTitle": "Know your Santa", "@homeTitle": { "description": "Page title.", "context": "HomePage" }, "homeCurrentLanguage": "Current language: {language}", "@homeCurrentLanguage": { "description": "Show the current language on the Home page", "context": "HomePage", "placeholders": { "language": {} } } (...) } app_en.dart

Slide 21

Slide 21 text

i18n l10n .ARB app_hu.dart { "@@locale": "hu", "homeTitle": "Ismerd meg a Mikulásod", "homeAppbarTitle": "Kezdőlap", "homeCurrentLanguage": "Aktuális nyelv: {language}", "homeYourSanta": "A mikulásod", "santaName": "Mikulás", "santaHat": "Kalap", "santaTie": "Nyaktekerészeti mellfekvenc", (...) }

Slide 22

Slide 22 text

i18n l10n dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter intl: ^0.17.0 TODO: 3 - Add intl dependency and specify the generate flag pubspec.yaml

Slide 23

Slide 23 text

i18n l10n dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter intl: ^0.17.0 TODO: 3 - Add intl dependency and specify the generate flag pubspec.yaml flutter: uses-material-design: true generate: true

Slide 24

Slide 24 text

i18n l10n dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter intl: ^0.17.0 TODO: 4 - Create l10n.yaml configuration file in the root package. flutter: uses-material-design: true generate: true arb-dir: lib/l10n template-arb-file: app_en.arb output-localization-file: l10n.dart output-class: L10n l10n.yaml

Slide 25

Slide 25 text

i18n l10n dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter intl: ^0.17.0 TODO: 4 - Create l10n.yaml configuration file in the root package and start CODE GENERATION. flutter: uses-material-design: true generate: true arb-dir: lib/l10n template-arb-file: app_en.arb output-localization-file: l10n.dart output-class: L10n l10n.yaml l10n.dart l10n_en.dart l10n_hu.dart

Slide 26

Slide 26 text

app.dart i18n l10n TODO: 5 - Add our generated delegate to the App. MaterialApp( home: const HomePage(), localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: [ Locale('en'), Locale('hu'), ], ); import 'package:flutter_gen/gen_l10n/l10n.dart';

Slide 27

Slide 27 text

app.dart i18n l10n TODO: 5 - Add our generated delegate to the App. MaterialApp( home: const HomePage(), localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, L10n.delegate, ], supportedLocales: [ Locale('en'), Locale('hu'), ], ); import 'package:flutter_gen/gen_l10n/l10n.dart';

Slide 28

Slide 28 text

home_page.dart i18n l10n TODO: 6 - Use the l10n anywhere in the widget tree. import 'package:flutter_gen/gen_l10n/l10n.dart’; class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( /// (...) body: Container( child: Column( children: [ Text( ‘Know your Santa’, textAlign: TextAlign.center, ), /// (...) ], ), ), ); } }

Slide 29

Slide 29 text

import 'package:flutter_gen/gen_l10n/l10n.dart’; class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { final l10n = L10n.of(context)!; return Scaffold( /// (...) body: Container( child: Column( children: [ Text( l10n.homeTitle, textAlign: TextAlign.center, ), /// (...) ], ), ), ); } } i18n l10n home_page.dart TODO: 6 - Use the l10n anywhere in the widget tree.

Slide 30

Slide 30 text

import 'package:flutter_gen/gen_l10n/l10n.dart’; class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { final l10n = L10n.of(context)!; return Scaffold( /// (...) body: Container( child: Column( children: [ Text( l10n.homeTitle, textAlign: TextAlign.center, ), /// (...) ], ), ), ); } } i18n l10n home_page.dart TODO: 6 - Use the l10n anywhere in the widget tree.

Slide 31

Slide 31 text

import 'package:flutter_gen/gen_l10n/l10n.dart’; class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { final l10n = L10n.of(context)!; return Scaffold( /// (...) body: Container( child: Column( children: [ Text( l10n.homeTitle, textAlign: TextAlign.center, ), /// (...) ], ), ), ); } } i18n l10n Text( l10n.homeCurrentLanguage( l10n.localeName, ), ) home_page.dart

Slide 32

Slide 32 text

import 'package:flutter_gen/gen_l10n/l10n.dart’; class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { final l10n = L10n.of(context)!; return Scaffold( /// (...) body: Container( child: Column( children: [ Text( l10n.homeTitle, textAlign: TextAlign.center, ), /// (...) ], ), ), ); } } i18n l10n home_page.dart

Slide 33

Slide 33 text

RTL support i18n l10n

Slide 34

Slide 34 text

RTL support i18n l10n

Slide 35

Slide 35 text

RTL support i18n l10n

Slide 36

Slide 36 text

RTL support i18n l10n Positioned( right: 16, top: 32, child: ///(...) ), Padding( padding: EdgeInsets.only(left: 8, right: 16), child: Text(""), ),

Slide 37

Slide 37 text

RTL support i18n l10n Positioned( end: 16, top: 32, child: ///(...) ), Padding( padding: EdgeInsetsDirectional.only(start: 8, end: 16), child: Text(""), ),

Slide 38

Slide 38 text

RTL support i18n l10n Positioned end: 16, top: 32, child: ///(...) ), .directional( textDirection: Directionality.of(context), Padding( padding: EdgeInsetsDirectional.only(start: 8, end: 16), child: Text(""), ),

Slide 39

Slide 39 text

a11y

Slide 40

Slide 40 text

Over 1 billion people are estimated to experience disability. 15% of the world's population

Slide 41

Slide 41 text

Accessibility checklist Large fonts Render text widgets with user- specified font sizes Screen readers Communicate spoken feedback about UI contents Sufficient contrast Render widgets with colors that have sufficient contrast Tappable targets All tappable targets should be at least 48x48 pixels. Color vision deficiency testing. Controls should be usable and legible in colorblind and grayscale modes Context switching Nothing should change the user’s context automatically while typing in information. Generally, the widgets should avoid changing the user’s context without some sort of confirmation action. a11y

Slide 42

Slide 42 text

.ARB a11y Text-to-Speech Talkback VoiceOver NVDA

Slide 43

Slide 43 text

.ARB a11y Text-to-Speech Scaffold( appBar: AppBar( Semantics( label: "This is the appbar of the home page.", textField: true, child: Text(l10n.homeAppbarTitle), ), ),

Slide 44

Slide 44 text

.ARB a11y Text-to-Speech Scaffold( appBar: AppBar( Semantics( label: "This is the appbar of the home page.", textField: true, child: Text(l10n.homeAppbarTitle), ), ),

Slide 45

Slide 45 text

.ARB a11y Text-to-Speech Scaffold( appBar: AppBar( title: Semantics( label: "This is the appbar of the home page.", textField: true, child: Text(l10n.homeAppbarTitle), ), ),

Slide 46

Slide 46 text

.ARB a11y Text-to-Speech Semantics( label: "Home page", child: Scaffold( appBar: AppBar( title: Semantics( label: "This is the appbar of the home page.", textField: true, child: Text(l10n.homeAppbarTitle), ), ),

Slide 47

Slide 47 text

MediaQuery a11y Sufficient contrast MediaQuery

Slide 48

Slide 48 text

MediaQuery.of(context).highContrast a11y Sufficient contrast MediaQuery

Slide 49

Slide 49 text

MediaQuery.of(context).textScaleFactor a11y Scale factors MediaQuery

Slide 50

Slide 50 text

.ARB a11y Scale factors

Slide 51

Slide 51 text

The repo of the sample project: https://github.com/gerfalcon/flutter_l10n_a11y_sample

Slide 52

Slide 52 text

Resources Internationalizing Flutter apps https://flutter.dev/docs/development/accessibility-and-localization/internationalization Internationalizing and Localizing Your Flutter App https://www.raywenderlich.com/10794904-internationalizing-and-localizing-your-flutter- app Flutter ARB file (.arb) https://localizely.com/flutter-arb/ Accessibility https://docs.flutter.dev/development/accessibility-and-localization/accessibility A deep dive into Flutter’s accessibility widgets https://medium.com/flutter-community/a-deep-dive-into-flutters-accessibility-widgets- eb0ef9455bc Used icon by Icons8

Slide 53

Slide 53 text

GerfalconVogel gerfalcon